Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
52cfce63
Commit
52cfce63
authored
Dec 07, 2020
by
GitLab Release Tools Bot
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'dev/master'
parents
8c4d1fb8
c29c3797
Changes
50
Hide whitespace changes
Inline
Side-by-side
Showing
50 changed files
with
1192 additions
and
60 deletions
+1192
-60
CHANGELOG-EE.md
CHANGELOG-EE.md
+21
-0
CHANGELOG.md
CHANGELOG.md
+48
-0
app/assets/javascripts/behaviors/markdown/render_mermaid.js
app/assets/javascripts/behaviors/markdown/render_mermaid.js
+26
-6
app/controllers/explore/projects_controller.rb
app/controllers/explore/projects_controller.rb
+3
-1
app/controllers/projects/feature_flags_controller.rb
app/controllers/projects/feature_flags_controller.rb
+3
-3
app/controllers/search_controller.rb
app/controllers/search_controller.rb
+0
-1
app/controllers/users_controller.rb
app/controllers/users_controller.rb
+1
-1
app/finders/projects_finder.rb
app/finders/projects_finder.rb
+4
-0
app/finders/starred_projects_finder.rb
app/finders/starred_projects_finder.rb
+11
-0
app/graphql/types/user_type.rb
app/graphql/types/user_type.rb
+4
-5
app/models/operations/feature_flags/user_list.rb
app/models/operations/feature_flags/user_list.rb
+5
-0
app/presenters/user_presenter.rb
app/presenters/user_presenter.rb
+14
-0
app/services/feature_flags/update_service.rb
app/services/feature_flags/update_service.rb
+11
-0
app/services/todos/destroy/entity_leave_service.rb
app/services/todos/destroy/entity_leave_service.rb
+4
-2
app/validators/zoom_url_validator.rb
app/validators/zoom_url_validator.rb
+6
-1
app/views/devise/confirmations/new.html.haml
app/views/devise/confirmations/new.html.haml
+1
-1
app/views/explore/projects/_projects.html.haml
app/views/explore/projects/_projects.html.haml
+5
-1
config/application.rb
config/application.rb
+1
-0
config/feature_categories.yml
config/feature_categories.yml
+1
-0
db/post_migrate/20201109114603_schedule_remove_inaccessible_epic_todos.rb
...20201109114603_schedule_remove_inaccessible_epic_todos.rb
+29
-0
db/schema_migrations/20201109114603
db/schema_migrations/20201109114603
+1
-0
doc/api/graphql/reference/gitlab_schema.graphql
doc/api/graphql/reference/gitlab_schema.graphql
+2
-2
doc/api/graphql/reference/gitlab_schema.json
doc/api/graphql/reference/gitlab_schema.json
+3
-3
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+1
-1
doc/user/todos.md
doc/user/todos.md
+1
-1
ee/app/services/ee/todos/destroy/entity_leave_service.rb
ee/app/services/ee/todos/destroy/entity_leave_service.rb
+33
-0
ee/app/services/epics/update_service.rb
ee/app/services/epics/update_service.rb
+5
-0
ee/app/services/todos/destroy/confidential_epic_service.rb
ee/app/services/todos/destroy/confidential_epic_service.rb
+34
-0
ee/app/workers/all_queues.yml
ee/app/workers/all_queues.yml
+8
-0
ee/app/workers/todos_destroyer/confidential_epic_worker.rb
ee/app/workers/todos_destroyer/confidential_epic_worker.rb
+16
-0
ee/lib/ee/gitlab/background_migration/remove_inaccessible_epic_todos.rb
...ab/background_migration/remove_inaccessible_epic_todos.rb
+132
-0
ee/spec/lib/ee/gitlab/background_migration/remove_inaccessible_epic_todos_spec.rb
...ckground_migration/remove_inaccessible_epic_todos_spec.rb
+112
-0
ee/spec/migrations/schedule_remove_inaccessible_epic_todos_spec.rb
...igrations/schedule_remove_inaccessible_epic_todos_spec.rb
+30
-0
ee/spec/services/ee/todos/destroy/entity_leave_service_spec.rb
...ec/services/ee/todos/destroy/entity_leave_service_spec.rb
+45
-0
ee/spec/services/epics/update_service_spec.rb
ee/spec/services/epics/update_service_spec.rb
+6
-0
ee/spec/services/todos/destroy/confidential_epic_service_spec.rb
.../services/todos/destroy/confidential_epic_service_spec.rb
+50
-0
ee/spec/workers/todos_destroyer/confidential_epic_worker_spec.rb
.../workers/todos_destroyer/confidential_epic_worker_spec.rb
+14
-0
lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
...ab/background_migration/remove_inaccessible_epic_todos.rb
+13
-0
spec/controllers/confirmations_controller_spec.rb
spec/controllers/confirmations_controller_spec.rb
+80
-0
spec/controllers/projects/feature_flags_controller_spec.rb
spec/controllers/projects/feature_flags_controller_spec.rb
+34
-0
spec/controllers/search_controller_spec.rb
spec/controllers/search_controller_spec.rb
+1
-1
spec/controllers/users_controller_spec.rb
spec/controllers/users_controller_spec.rb
+78
-11
spec/features/explore/user_explores_projects_spec.rb
spec/features/explore/user_explores_projects_spec.rb
+11
-0
spec/features/markdown/mermaid_spec.rb
spec/features/markdown/mermaid_spec.rb
+65
-0
spec/finders/projects_finder_spec.rb
spec/finders/projects_finder_spec.rb
+23
-0
spec/finders/starred_projects_finder_spec.rb
spec/finders/starred_projects_finder_spec.rb
+48
-11
spec/requests/api/graphql/user/starred_projects_query_spec.rb
.../requests/api/graphql/user/starred_projects_query_spec.rb
+27
-0
spec/requests/api/graphql/user_query_spec.rb
spec/requests/api/graphql/user_query_spec.rb
+46
-2
spec/requests/api/projects_spec.rb
spec/requests/api/projects_spec.rb
+39
-6
spec/validators/zoom_url_validator_spec.rb
spec/validators/zoom_url_validator_spec.rb
+36
-0
No files found.
CHANGELOG-EE.md
View file @
52cfce63
Please view this file on the master branch, on stable branches it's out of date.
Please view this file on the master branch, on stable branches it's out of date.
## 13.6.2 (2020-12-07)
### Security (1 change)
-
Cleanup todos for confidential epics that are no longer accessible by the user.
## 13.6.1 (2020-11-23)
## 13.6.1 (2020-11-23)
-
No changes.
-
No changes.
...
@@ -184,6 +191,13 @@ Please view this file on the master branch, on stable branches it's out of date.
...
@@ -184,6 +191,13 @@ Please view this file on the master branch, on stable branches it's out of date.
-
Remove duplicated BS display properties from member overriding UI. !47126 (Takuya Noguchi)
-
Remove duplicated BS display properties from member overriding UI. !47126 (Takuya Noguchi)
## 13.5.5 (2020-12-07)
### Security (1 change)
-
Cleanup todos for confidential epics that are no longer accessible by the user.
## 13.5.4 (2020-11-13)
## 13.5.4 (2020-11-13)
### Fixed (1 change)
### Fixed (1 change)
...
@@ -429,6 +443,13 @@ Please view this file on the master branch, on stable branches it's out of date.
...
@@ -429,6 +443,13 @@ Please view this file on the master branch, on stable branches it's out of date.
-
Remove bootstrap class in licensed user count. !45443
-
Remove bootstrap class in licensed user count. !45443
## 13.4.7 (2020-12-07)
### Security (1 change)
-
Cleanup todos for confidential epics that are no longer accessible by the user.
## 13.4.6 (2020-11-03)
## 13.4.6 (2020-11-03)
### Fixed (1 change)
### Fixed (1 change)
...
...
CHANGELOG.md
View file @
52cfce63
...
@@ -2,6 +2,22 @@
...
@@ -2,6 +2,22 @@
documentation
](
doc/development/changelog.md
)
for instructions on adding your own
documentation
](
doc/development/changelog.md
)
for instructions on adding your own
entry.
entry.
## 13.6.2 (2020-12-07)
### Security (10 changes)
-
Validate zoom links to start with https only. !1055
-
Require at least 3 characters when searching for project in the Explore page.
-
Do not show emails of users in confirmation page.
-
Forbid setting a gitlabUserList strategy to a list from another project.
-
Fix mermaid resource consumption in GFM fields.
-
Ensure group and project memberships are not leaked via API for users with private profiles.
-
GraphQL User: do not expose email if set to private.
-
Filter search parameter to prevent data leaks.
-
Do not expose starred projects of users with private profile via API.
-
Do not show starred & contributed projects of users with private profile.
## 13.6.1 (2020-11-23)
## 13.6.1 (2020-11-23)
### Fixed (5 changes)
### Fixed (5 changes)
...
@@ -529,6 +545,22 @@ entry.
...
@@ -529,6 +545,22 @@ entry.
-
Change wording on the project remove fork page. !47878
-
Change wording on the project remove fork page. !47878
## 13.5.5 (2020-12-07)
### Security (10 changes)
-
Validate zoom links to start with https only. !1055
-
Require at least 3 characters when searching for project in the Explore page.
-
Do not show emails of users in confirmation page.
-
Forbid setting a gitlabUserList strategy to a list from another project.
-
Fix mermaid resource consumption in GFM fields.
-
Ensure group and project memberships are not leaked via API for users with private profiles.
-
GraphQL User: do not expose email if set to private.
-
Filter search parameter to prevent data leaks.
-
Do not expose starred projects of users with private profile via API.
-
Do not show starred & contributed projects of users with private profile.
## 13.5.4 (2020-11-13)
## 13.5.4 (2020-11-13)
### Fixed (4 changes)
### Fixed (4 changes)
...
@@ -1148,6 +1180,22 @@ entry.
...
@@ -1148,6 +1180,22 @@ entry.
-
Bump cluster applications CI template. !45472
-
Bump cluster applications CI template. !45472
## 13.4.7 (2020-12-07)
### Security (10 changes)
-
Validate zoom links to start with https only. !1055
-
Require at least 3 characters when searching for project in the Explore page.
-
Do not show emails of users in confirmation page.
-
Forbid setting a gitlabUserList strategy to a list from another project.
-
Fix mermaid resource consumption in GFM fields.
-
Ensure group and project memberships are not leaked via API for users with private profiles.
-
GraphQL User: do not expose email if set to private.
-
Filter search parameter to prevent data leaks.
-
Do not expose starred projects of users with private profile via API.
-
Do not show starred & contributed projects of users with private profile.
## 13.4.6 (2020-11-03)
## 13.4.6 (2020-11-03)
### Fixed (1 change)
### Fixed (1 change)
...
...
app/assets/javascripts/behaviors/markdown/render_mermaid.js
View file @
52cfce63
...
@@ -18,7 +18,13 @@ import { __, sprintf } from '~/locale';
...
@@ -18,7 +18,13 @@ import { __, sprintf } from '~/locale';
//
//
// This is an arbitrary number; Can be iterated upon when suitable.
// This is an arbitrary number; Can be iterated upon when suitable.
const
MAX_CHAR_LIMIT
=
5000
;
const
MAX_CHAR_LIMIT
=
2000
;
// Max # of mermaid blocks that can be rendered in a page.
const
MAX_MERMAID_BLOCK_LIMIT
=
50
;
// Keep a map of mermaid blocks we've already rendered.
const
elsProcessingMap
=
new
WeakMap
();
let
renderedMermaidBlocks
=
0
;
let
mermaidModule
=
{};
let
mermaidModule
=
{};
function
importMermaidModule
()
{
function
importMermaidModule
()
{
...
@@ -110,13 +116,22 @@ function renderMermaids($els) {
...
@@ -110,13 +116,22 @@ function renderMermaids($els) {
let
renderedChars
=
0
;
let
renderedChars
=
0
;
$els
.
each
((
i
,
el
)
=>
{
$els
.
each
((
i
,
el
)
=>
{
// Skipping all the elements which we've already queued in requestIdleCallback
if
(
elsProcessingMap
.
has
(
el
))
{
return
;
}
const
{
source
}
=
fixElementSource
(
el
);
const
{
source
}
=
fixElementSource
(
el
);
/**
/**
* Restrict the rendering to a certain amount of character
to
* Restrict the rendering to a certain amount of character
*
prevent mermaidjs from hanging up the entire thread and
*
and mermaid blocks to prevent mermaidjs from hanging
* causing a DoS.
*
up the entire thread and
causing a DoS.
*/
*/
if
((
source
&&
source
.
length
>
MAX_CHAR_LIMIT
)
||
renderedChars
>
MAX_CHAR_LIMIT
)
{
if
(
(
source
&&
source
.
length
>
MAX_CHAR_LIMIT
)
||
renderedChars
>
MAX_CHAR_LIMIT
||
renderedMermaidBlocks
>=
MAX_MERMAID_BLOCK_LIMIT
)
{
const
html
=
`
const
html
=
`
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
<div>
<div>
...
@@ -146,8 +161,13 @@ function renderMermaids($els) {
...
@@ -146,8 +161,13 @@ function renderMermaids($els) {
}
}
renderedChars
+=
source
.
length
;
renderedChars
+=
source
.
length
;
renderedMermaidBlocks
+=
1
;
const
requestId
=
window
.
requestIdleCallback
(()
=>
{
renderMermaidEl
(
el
);
});
renderMermaidEl
(
el
);
elsProcessingMap
.
set
(
el
,
requestId
);
});
});
})
})
.
catch
(
err
=>
{
.
catch
(
err
=>
{
...
...
app/controllers/explore/projects_controller.rb
View file @
52cfce63
...
@@ -8,6 +8,8 @@ class Explore::ProjectsController < Explore::ApplicationController
...
@@ -8,6 +8,8 @@ class Explore::ProjectsController < Explore::ApplicationController
include
SortingHelper
include
SortingHelper
include
SortingPreference
include
SortingPreference
MIN_SEARCH_LENGTH
=
3
before_action
:set_non_archived_param
before_action
:set_non_archived_param
before_action
:set_sorting
before_action
:set_sorting
...
@@ -72,7 +74,7 @@ class Explore::ProjectsController < Explore::ApplicationController
...
@@ -72,7 +74,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def
load_projects
def
load_projects
load_project_counts
load_project_counts
projects
=
ProjectsFinder
.
new
(
current_user:
current_user
,
params:
params
).
execute
projects
=
ProjectsFinder
.
new
(
current_user:
current_user
,
params:
params
.
merge
(
minimum_search_length:
MIN_SEARCH_LENGTH
)
).
execute
projects
=
preload_associations
(
projects
)
projects
=
preload_associations
(
projects
)
projects
=
projects
.
page
(
params
[
:page
]).
without_count
projects
=
projects
.
page
(
params
[
:page
]).
without_count
...
...
app/controllers/projects/feature_flags_controller.rb
View file @
52cfce63
...
@@ -76,7 +76,7 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
...
@@ -76,7 +76,7 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
end
end
else
else
respond_to
do
|
format
|
respond_to
do
|
format
|
format
.
json
{
render_error_json
(
result
[
:message
])
}
format
.
json
{
render_error_json
(
result
[
:message
]
,
result
[
:http_status
]
)
}
end
end
end
end
end
end
...
@@ -158,8 +158,8 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
...
@@ -158,8 +158,8 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
render
json:
feature_flag_json
(
feature_flag
),
status: :ok
render
json:
feature_flag_json
(
feature_flag
),
status: :ok
end
end
def
render_error_json
(
messages
)
def
render_error_json
(
messages
,
status
=
:bad_request
)
render
json:
{
message:
messages
},
render
json:
{
message:
messages
},
status:
:bad_request
status:
status
end
end
end
end
app/controllers/search_controller.rb
View file @
52cfce63
...
@@ -122,7 +122,6 @@ class SearchController < ApplicationController
...
@@ -122,7 +122,6 @@ class SearchController < ApplicationController
payload
[
:metadata
]
||=
{}
payload
[
:metadata
]
||=
{}
payload
[
:metadata
][
'meta.search.group_id'
]
=
params
[
:group_id
]
payload
[
:metadata
][
'meta.search.group_id'
]
=
params
[
:group_id
]
payload
[
:metadata
][
'meta.search.project_id'
]
=
params
[
:project_id
]
payload
[
:metadata
][
'meta.search.project_id'
]
=
params
[
:project_id
]
payload
[
:metadata
][
'meta.search.search'
]
=
params
[
:search
]
payload
[
:metadata
][
'meta.search.scope'
]
=
params
[
:scope
]
payload
[
:metadata
][
'meta.search.scope'
]
=
params
[
:scope
]
payload
[
:metadata
][
'meta.search.filters.confidential'
]
=
params
[
:confidential
]
payload
[
:metadata
][
'meta.search.filters.confidential'
]
=
params
[
:confidential
]
payload
[
:metadata
][
'meta.search.filters.state'
]
=
params
[
:state
]
payload
[
:metadata
][
'meta.search.filters.state'
]
=
params
[
:state
]
...
...
app/controllers/users_controller.rb
View file @
52cfce63
...
@@ -19,7 +19,7 @@ class UsersController < ApplicationController
...
@@ -19,7 +19,7 @@ class UsersController < ApplicationController
prepend_before_action
(
only:
[
:show
])
{
authenticate_sessionless_user!
(
:rss
)
}
prepend_before_action
(
only:
[
:show
])
{
authenticate_sessionless_user!
(
:rss
)
}
before_action
:user
,
except:
[
:exists
,
:suggests
]
before_action
:user
,
except:
[
:exists
,
:suggests
]
before_action
:authorize_read_user_profile!
,
before_action
:authorize_read_user_profile!
,
only:
[
:calendar
,
:calendar_activities
,
:groups
,
:projects
,
:contributed
_projects
,
:starred_projects
,
:snippets
]
only:
[
:calendar
,
:calendar_activities
,
:groups
,
:projects
,
:contributed
,
:starred
,
:snippets
]
feature_category
:users
feature_category
:users
...
...
app/finders/projects_finder.rb
View file @
52cfce63
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
# personal: boolean
# personal: boolean
# search: string
# search: string
# search_namespaces: boolean
# search_namespaces: boolean
# minimum_search_length: int
# non_archived: boolean
# non_archived: boolean
# archived: 'only' or boolean
# archived: 'only' or boolean
# min_access_level: integer
# min_access_level: integer
...
@@ -182,6 +183,9 @@ class ProjectsFinder < UnionFinder
...
@@ -182,6 +183,9 @@ class ProjectsFinder < UnionFinder
def
by_search
(
items
)
def
by_search
(
items
)
params
[
:search
]
||=
params
[
:name
]
params
[
:search
]
||=
params
[
:name
]
return
items
.
none
if
params
[
:search
].
present?
&&
params
[
:minimum_search_length
].
present?
&&
params
[
:search
].
length
<
params
[
:minimum_search_length
].
to_i
items
.
optionally_search
(
params
[
:search
],
include_namespace:
params
[
:search_namespaces
].
present?
)
items
.
optionally_search
(
params
[
:search
],
include_namespace:
params
[
:search_namespaces
].
present?
)
end
end
...
...
app/finders/starred_projects_finder.rb
View file @
52cfce63
# frozen_string_literal: true
# frozen_string_literal: true
class
StarredProjectsFinder
<
ProjectsFinder
class
StarredProjectsFinder
<
ProjectsFinder
include
Gitlab
::
Allowable
def
initialize
(
user
,
params:
{},
current_user:
nil
)
def
initialize
(
user
,
params:
{},
current_user:
nil
)
@user
=
user
super
(
super
(
params:
params
,
params:
params
,
current_user:
current_user
,
current_user:
current_user
,
project_ids_relation:
user
.
starred_projects
.
select
(
:id
)
project_ids_relation:
user
.
starred_projects
.
select
(
:id
)
)
)
end
end
def
execute
# Do not show starred projects if the user has a private profile.
return
Project
.
none
unless
can?
(
current_user
,
:read_user_profile
,
@user
)
super
end
end
end
app/graphql/types/user_type.rb
View file @
52cfce63
...
@@ -19,7 +19,8 @@ module Types
...
@@ -19,7 +19,8 @@ module Types
field
:state
,
Types
::
UserStateEnum
,
null:
false
,
field
:state
,
Types
::
UserStateEnum
,
null:
false
,
description:
'State of the user'
description:
'State of the user'
field
:email
,
GraphQL
::
STRING_TYPE
,
null:
true
,
field
:email
,
GraphQL
::
STRING_TYPE
,
null:
true
,
description:
'User email'
description:
'User email'
,
method: :public_email
,
deprecated:
{
reason:
'Use public_email'
,
milestone:
'13.7'
}
field
:public_email
,
GraphQL
::
STRING_TYPE
,
null:
true
,
field
:public_email
,
GraphQL
::
STRING_TYPE
,
null:
true
,
description:
"User's public email"
description:
"User's public email"
field
:avatar_url
,
GraphQL
::
STRING_TYPE
,
null:
true
,
field
:avatar_url
,
GraphQL
::
STRING_TYPE
,
null:
true
,
...
@@ -32,8 +33,7 @@ module Types
...
@@ -32,8 +33,7 @@ module Types
resolver:
Resolvers
::
TodoResolver
,
resolver:
Resolvers
::
TodoResolver
,
description:
'Todos of the user'
description:
'Todos of the user'
field
:group_memberships
,
Types
::
GroupMemberType
.
connection_type
,
null:
true
,
field
:group_memberships
,
Types
::
GroupMemberType
.
connection_type
,
null:
true
,
description:
'Group memberships of the user'
,
description:
'Group memberships of the user'
method: :group_members
field
:group_count
,
GraphQL
::
INT_TYPE
,
null:
true
,
field
:group_count
,
GraphQL
::
INT_TYPE
,
null:
true
,
resolver:
Resolvers
::
Users
::
GroupCountResolver
,
resolver:
Resolvers
::
Users
::
GroupCountResolver
,
description:
'Group count for the user'
,
description:
'Group count for the user'
,
...
@@ -43,8 +43,7 @@ module Types
...
@@ -43,8 +43,7 @@ module Types
field
:location
,
::
GraphQL
::
STRING_TYPE
,
null:
true
,
field
:location
,
::
GraphQL
::
STRING_TYPE
,
null:
true
,
description:
'The location of the user.'
description:
'The location of the user.'
field
:project_memberships
,
Types
::
ProjectMemberType
.
connection_type
,
null:
true
,
field
:project_memberships
,
Types
::
ProjectMemberType
.
connection_type
,
null:
true
,
description:
'Project memberships of the user'
,
description:
'Project memberships of the user'
method: :project_members
field
:starred_projects
,
Types
::
ProjectType
.
connection_type
,
null:
true
,
field
:starred_projects
,
Types
::
ProjectType
.
connection_type
,
null:
true
,
description:
'Projects starred by the user'
,
description:
'Projects starred by the user'
,
resolver:
Resolvers
::
UserStarredProjectsResolver
resolver:
Resolvers
::
UserStarredProjectsResolver
...
...
app/models/operations/feature_flags/user_list.rb
View file @
52cfce63
...
@@ -28,6 +28,11 @@ module Operations
...
@@ -28,6 +28,11 @@ module Operations
fuzzy_search
(
query
,
[
:name
],
use_minimum_char_limit:
false
)
fuzzy_search
(
query
,
[
:name
],
use_minimum_char_limit:
false
)
end
end
def
self
.
belongs_to?
(
project_id
,
user_list_ids
)
uniq_ids
=
user_list_ids
.
uniq
where
(
id:
uniq_ids
,
project_id:
project_id
).
count
==
uniq_ids
.
count
end
private
private
def
ensure_no_associated_strategies
def
ensure_no_associated_strategies
...
...
app/presenters/user_presenter.rb
View file @
52cfce63
...
@@ -2,4 +2,18 @@
...
@@ -2,4 +2,18 @@
class
UserPresenter
<
Gitlab
::
View
::
Presenter
::
Delegated
class
UserPresenter
<
Gitlab
::
View
::
Presenter
::
Delegated
presents
:user
presents
:user
def
group_memberships
should_be_private?
?
GroupMember
.
none
:
user
.
group_members
end
def
project_memberships
should_be_private?
?
ProjectMember
.
none
:
user
.
project_members
end
private
def
should_be_private?
!
can?
(
current_user
,
:read_user_profile
,
user
)
end
end
end
app/services/feature_flags/update_service.rb
View file @
52cfce63
...
@@ -10,6 +10,7 @@ module FeatureFlags
...
@@ -10,6 +10,7 @@ module FeatureFlags
def
execute
(
feature_flag
)
def
execute
(
feature_flag
)
return
error
(
'Access Denied'
,
403
)
unless
can_update?
(
feature_flag
)
return
error
(
'Access Denied'
,
403
)
unless
can_update?
(
feature_flag
)
return
error
(
'Not Found'
,
404
)
unless
valid_user_list_ids?
(
feature_flag
,
user_list_ids
(
params
))
ActiveRecord
::
Base
.
transaction
do
ActiveRecord
::
Base
.
transaction
do
feature_flag
.
assign_attributes
(
params
)
feature_flag
.
assign_attributes
(
params
)
...
@@ -87,5 +88,15 @@ module FeatureFlags
...
@@ -87,5 +88,15 @@ module FeatureFlags
def
can_update?
(
feature_flag
)
def
can_update?
(
feature_flag
)
Ability
.
allowed?
(
current_user
,
:update_feature_flag
,
feature_flag
)
Ability
.
allowed?
(
current_user
,
:update_feature_flag
,
feature_flag
)
end
end
def
user_list_ids
(
params
)
params
.
fetch
(
:strategies_attributes
,
[])
.
select
{
|
s
|
s
[
:user_list_id
].
present?
}
.
map
{
|
s
|
s
[
:user_list_id
]
}
end
def
valid_user_list_ids?
(
feature_flag
,
user_list_ids
)
user_list_ids
.
empty?
||
::
Operations
::
FeatureFlags
::
UserList
.
belongs_to?
(
feature_flag
.
project_id
,
user_list_ids
)
end
end
end
end
end
app/services/todos/destroy/entity_leave_service.rb
View file @
52cfce63
...
@@ -22,7 +22,7 @@ module Todos
...
@@ -22,7 +22,7 @@ module Todos
# if at least reporter, all entities including confidential issues can be accessed
# if at least reporter, all entities including confidential issues can be accessed
return
if
user_has_reporter_access?
return
if
user_has_reporter_access?
remove_confidential_
issu
e_todos
remove_confidential_
resourc
e_todos
if
entity
.
private?
if
entity
.
private?
remove_project_todos
remove_project_todos
...
@@ -40,7 +40,7 @@ module Todos
...
@@ -40,7 +40,7 @@ module Todos
end
end
end
end
def
remove_confidential_
issu
e_todos
def
remove_confidential_
resourc
e_todos
Todo
Todo
.
for_target
(
confidential_issues
.
select
(
:id
))
.
for_target
(
confidential_issues
.
select
(
:id
))
.
for_type
(
Issue
.
name
)
.
for_type
(
Issue
.
name
)
...
@@ -133,3 +133,5 @@ module Todos
...
@@ -133,3 +133,5 @@ module Todos
end
end
end
end
end
end
Todos
::
Destroy
::
EntityLeaveService
.
prepend_if_ee
(
'EE::Todos::Destroy::EntityLeaveService'
)
app/validators/zoom_url_validator.rb
View file @
52cfce63
...
@@ -5,8 +5,13 @@
...
@@ -5,8 +5,13 @@
# Custom validator for zoom urls
# Custom validator for zoom urls
#
#
class
ZoomUrlValidator
<
ActiveModel
::
EachValidator
class
ZoomUrlValidator
<
ActiveModel
::
EachValidator
ALLOWED_SCHEMES
=
%w(https)
.
freeze
def
validate_each
(
record
,
attribute
,
value
)
def
validate_each
(
record
,
attribute
,
value
)
return
if
Gitlab
::
ZoomLinkExtractor
.
new
(
value
).
links
.
size
==
1
links_count
=
Gitlab
::
ZoomLinkExtractor
.
new
(
value
).
links
.
size
valid
=
Gitlab
::
UrlSanitizer
.
valid?
(
value
,
allowed_schemes:
ALLOWED_SCHEMES
)
return
if
links_count
==
1
&&
valid
record
.
errors
.
add
(
:url
,
'must contain one valid Zoom URL'
)
record
.
errors
.
add
(
:url
,
'must contain one valid Zoom URL'
)
end
end
...
...
app/views/devise/confirmations/new.html.haml
View file @
52cfce63
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
=
render
"devise/shared/error_messages"
,
resource:
resource
=
render
"devise/shared/error_messages"
,
resource:
resource
.form-group
.form-group
=
f
.
label
:email
=
f
.
label
:email
=
f
.
email_field
:email
,
class:
"form-control"
,
required:
true
,
title:
'Please provide a valid email address.'
=
f
.
email_field
:email
,
class:
"form-control"
,
required:
true
,
title:
'Please provide a valid email address.'
,
value:
nil
.clearfix
.clearfix
=
f
.
submit
"Resend"
,
class:
'gl-button btn btn-success'
=
f
.
submit
"Resend"
,
class:
'gl-button btn btn-success'
...
...
app/views/explore/projects/_projects.html.haml
View file @
52cfce63
=
render
'shared/projects/list'
,
projects:
projects
,
user:
current_user
,
explore_page:
true
,
pipeline_status:
Feature
.
enabled?
(
:dashboard_pipeline_status
,
default_enabled:
true
)
-
if
params
[
:name
].
present?
&&
params
[
:name
].
size
<
Explore
::
ProjectsController
::
MIN_SEARCH_LENGTH
.nothing-here-block
%h5
=
_
(
'Enter at least three characters to search'
)
-
else
=
render
'shared/projects/list'
,
projects:
projects
,
user:
current_user
,
explore_page:
true
,
pipeline_status:
Feature
.
enabled?
(
:dashboard_pipeline_status
,
default_enabled:
true
)
config/application.rb
View file @
52cfce63
...
@@ -137,6 +137,7 @@ module Gitlab
...
@@ -137,6 +137,7 @@ module Gitlab
encrypted_key
encrypted_key
import_url
import_url
elasticsearch_url
elasticsearch_url
search
otp_attempt
otp_attempt
sentry_dsn
sentry_dsn
trace
trace
...
...
config/feature_categories.yml
View file @
52cfce63
...
@@ -43,6 +43,7 @@
...
@@ -43,6 +43,7 @@
-
dynamic_application_security_testing
-
dynamic_application_security_testing
-
editor_extension
-
editor_extension
-
epics
-
epics
-
epic_tracking
-
error_tracking
-
error_tracking
-
feature_flags
-
feature_flags
-
five_minute_production_app
-
five_minute_production_app
...
...
db/post_migrate/20201109114603_schedule_remove_inaccessible_epic_todos.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
class
ScheduleRemoveInaccessibleEpicTodos
<
ActiveRecord
::
Migration
[
6.0
]
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
INTERVAL
=
2
.
minutes
BATCH_SIZE
=
10
MIGRATION
=
'RemoveInaccessibleEpicTodos'
disable_ddl_transaction!
class
Epic
<
ActiveRecord
::
Base
include
EachBatch
end
def
up
return
unless
Gitlab
.
ee?
relation
=
Epic
.
where
(
confidential:
true
)
queue_background_migration_jobs_by_range_at_intervals
(
relation
,
MIGRATION
,
INTERVAL
,
batch_size:
BATCH_SIZE
)
end
def
down
# no-op
end
end
db/schema_migrations/20201109114603
0 → 100644
View file @
52cfce63
ae8034ec52df47ce2ce3397715dd18347e4d297a963c17c7b26321f414dfa632
\ No newline at end of file
doc/api/graphql/reference/gitlab_schema.graphql
View file @
52cfce63
...
@@ -23823,9 +23823,9 @@ type User {
...
@@ -23823,9 +23823,9 @@ type User {
avatarUrl
:
String
avatarUrl
:
String
"""
"""
User
email
User
email
.
Deprecated
in
13.7
:
Use
public_email
"""
"""
email
:
String
email
:
String
@
deprecated
(
reason
:
"
Use
public_email
.
Deprecated
in
13.7"
)
"""
"""
Group
count
for
the
user
.
Available
only
when
feature
flag
`
user_group_counts
`
is
enabled
Group
count
for
the
user
.
Available
only
when
feature
flag
`
user_group_counts
`
is
enabled
...
...
doc/api/graphql/reference/gitlab_schema.json
View file @
52cfce63
...
@@ -69304,7 +69304,7 @@
...
@@ -69304,7 +69304,7 @@
},
},
{
{
"name": "email",
"name": "email",
"description": "User email",
"description": "User email
. Deprecated in 13.7: Use public_email
",
"args": [
"args": [
],
],
...
@@ -69313,8 +69313,8 @@
...
@@ -69313,8 +69313,8 @@
"name": "String",
"name": "String",
"ofType": null
"ofType": null
},
},
"isDeprecated":
fals
e,
"isDeprecated":
tru
e,
"deprecationReason":
null
"deprecationReason":
"Use public_email. Deprecated in 13.7"
},
},
{
{
"name": "groupCount",
"name": "groupCount",
doc/api/graphql/reference/index.md
View file @
52cfce63
...
@@ -3596,7 +3596,7 @@ Autogenerated return type of UpdateSnippet.
...
@@ -3596,7 +3596,7 @@ Autogenerated return type of UpdateSnippet.
|
`assignedMergeRequests`
| MergeRequestConnection | Merge Requests assigned to the user |
|
`assignedMergeRequests`
| MergeRequestConnection | Merge Requests assigned to the user |
|
`authoredMergeRequests`
| MergeRequestConnection | Merge Requests authored by the user |
|
`authoredMergeRequests`
| MergeRequestConnection | Merge Requests authored by the user |
|
`avatarUrl`
| String | URL of the user's avatar |
|
`avatarUrl`
| String | URL of the user's avatar |
|
`email`
| String | User email
|
|
`email`
**{warning-solid}**
| String |
**Deprecated:**
Use public_email. Deprecated in 13.7
|
|
`groupCount`
| Int | Group count for the user. Available only when feature flag
`user_group_counts`
is enabled |
|
`groupCount`
| Int | Group count for the user. Available only when feature flag
`user_group_counts`
is enabled |
|
`groupMemberships`
| GroupMemberConnection | Group memberships of the user |
|
`groupMemberships`
| GroupMemberConnection | Group memberships of the user |
|
`id`
| ID! | ID of the user |
|
`id`
| ID! | ID of the user |
...
...
doc/user/todos.md
View file @
52cfce63
...
@@ -64,7 +64,7 @@ To-do item triggers aren't affected by [GitLab notification email settings](prof
...
@@ -64,7 +64,7 @@ To-do item triggers aren't affected by [GitLab notification email settings](prof
NOTE:
NOTE:
When a user no longer has access to a resource related to a to-do item (such as
When a user no longer has access to a resource related to a to-do item (such as
an issue, merge request, project, or group), for security reasons GitLab
an issue, merge request,
epic,
project, or group), for security reasons GitLab
deletes any related to-do items within the next hour. Deletion is delayed to
deletes any related to-do items within the next hour. Deletion is delayed to
prevent data loss, in the case where a user's access is accidentally revoked.
prevent data loss, in the case where a user's access is accidentally revoked.
...
...
ee/app/services/ee/todos/destroy/entity_leave_service.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
module
EE
module
Todos
module
Destroy
module
EntityLeaveService
extend
ActiveSupport
::
Concern
extend
::
Gitlab
::
Utils
::
Override
override
:remove_confidential_resource_todos
def
remove_confidential_resource_todos
super
return
unless
entity
.
is_a?
(
Namespace
)
::
Todo
.
for_target
(
confidential_epics
.
select
(
:id
))
.
for_type
(
::
Epic
.
name
)
.
for_user
(
user
)
.
delete_all
end
private
def
confidential_epics
::
Epic
.
in_selected_groups
(
non_authorized_reporter_groups
)
.
confidential
end
end
end
end
end
ee/app/services/epics/update_service.rb
View file @
52cfce63
...
@@ -34,6 +34,11 @@ module Epics
...
@@ -34,6 +34,11 @@ module Epics
end
end
todo_service
.
update_epic
(
epic
,
current_user
,
old_mentioned_users
)
todo_service
.
update_epic
(
epic
,
current_user
,
old_mentioned_users
)
if
epic
.
previous_changes
.
include?
(
'confidential'
)
&&
epic
.
confidential?
# don't enqueue immediately to prevent todos removal in case of a mistake
::
TodosDestroyer
::
ConfidentialEpicWorker
.
perform_in
(
::
Todo
::
WAIT_FOR_DELETE
,
epic
.
id
)
end
end
end
def
handle_task_changes
(
epic
)
def
handle_task_changes
(
epic
)
...
...
ee/app/services/todos/destroy/confidential_epic_service.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
module
Todos
module
Destroy
# Service class for deleting todos that belong to confidential epics.
# It deletes todos for users that are not at least reporters.
class
ConfidentialEpicService
<
::
Todos
::
Destroy
::
BaseService
extend
::
Gitlab
::
Utils
::
Override
attr_reader
:epic
def
initialize
(
epic_id
:)
@epic
=
::
Epic
.
find_by_id
(
epic_id
)
end
private
override
:todos
def
todos
epic
.
todos
end
override
:todos_to_remove?
def
todos_to_remove?
epic
&
.
confidential?
end
override
:authorized_users
def
authorized_users
epic
.
group
.
members_with_parents
.
non_guests
.
select
(
:user_id
)
end
end
end
end
ee/app/workers/all_queues.yml
View file @
52cfce63
...
@@ -581,6 +581,14 @@
...
@@ -581,6 +581,14 @@
:weight:
2
:weight:
2
:idempotent:
:idempotent:
:tags: []
:tags: []
-
:name: todos_destroyer:todos_destroyer_confidential_epic
:feature_category: :epic_tracking
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight:
1
:idempotent:
:tags: []
-
:name: adjourned_project_deletion
-
:name: adjourned_project_deletion
:feature_category: :authentication_and_authorization
:feature_category: :authentication_and_authorization
:has_external_dependencies:
:has_external_dependencies:
...
...
ee/app/workers/todos_destroyer/confidential_epic_worker.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
module
TodosDestroyer
class
ConfidentialEpicWorker
# rubocop:disable Scalability/IdempotentWorker
include
ApplicationWorker
queue_namespace
:todos_destroyer
feature_category
:epic_tracking
def
perform
(
epic_id
)
return
unless
epic_id
::
Todos
::
Destroy
::
ConfidentialEpicService
.
new
(
epic_id:
epic_id
).
execute
end
end
end
ee/lib/ee/gitlab/background_migration/remove_inaccessible_epic_todos.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module
EE
module
Gitlab
module
BackgroundMigration
module
RemoveInaccessibleEpicTodos
extend
::
Gitlab
::
Utils
::
Override
class
User
<
ActiveRecord
::
Base
end
class
Todo
<
ActiveRecord
::
Base
belongs_to
:epic
,
foreign_key: :target_id
belongs_to
:user
end
class
Member
<
ActiveRecord
::
Base
include
FromUnion
self
.
inheritance_column
=
:_type_disabled
end
class
GroupGroupLink
<
ActiveRecord
::
Base
end
class
Epic
<
ActiveRecord
::
Base
belongs_to
:group
def
can_read_confidential?
(
user
)
group
.
max_member_access_for_user
(
user
)
>=
::
Gitlab
::
Access
::
REPORTER
end
end
class
Group
<
ActiveRecord
::
Base
self
.
table_name
=
'namespaces'
self
.
inheritance_column
=
:_type_disabled
def
max_member_access_for_user
(
user
)
max_member_access
=
members_with_parents
.
where
(
user_id:
user
)
.
reorder
(
access_level: :desc
)
.
first
&
.
access_level
max_member_access
||
::
Gitlab
::
Access
::
NO_ACCESS
end
def
members_with_parents
group_hierarchy_members
=
Member
.
where
(
source_type:
'Namespace'
,
source_id:
source_ids
)
Member
.
from_union
([
group_hierarchy_members
,
members_from_self_and_ancestor_group_shares
])
end
# rubocop:disable Metrics/AbcSize
# this is taken from Group model, so instead of doing additional
# refactoring let's keep it close to the original
def
members_from_self_and_ancestor_group_shares
group_group_link_table
=
GroupGroupLink
.
arel_table
group_member_table
=
Member
.
arel_table
group_group_links_query
=
GroupGroupLink
.
where
(
shared_group_id:
source_ids
)
cte
=
::
Gitlab
::
SQL
::
CTE
.
new
(
:group_group_links_cte
,
group_group_links_query
)
cte_alias
=
cte
.
table
.
alias
(
GroupGroupLink
.
table_name
)
# Instead of members.access_level, we need to maximize that access_level at
# the respective group_group_links.group_access.
member_columns
=
Member
.
attribute_names
.
map
do
|
column_name
|
if
column_name
==
'access_level'
smallest_value_arel
([
cte_alias
[
:group_access
],
group_member_table
[
:access_level
]],
'access_level'
)
else
group_member_table
[
column_name
]
end
end
Member
.
with
(
cte
.
to_arel
)
.
select
(
*
member_columns
)
.
from
([
group_member_table
,
cte
.
alias_to
(
group_group_link_table
)])
.
where
(
group_member_table
[
:requested_at
].
eq
(
nil
))
.
where
(
group_member_table
[
:source_id
].
eq
(
group_group_link_table
[
:shared_with_group_id
]))
.
where
(
group_member_table
[
:source_type
].
eq
(
'Namespace'
))
end
# rubocop:enable Metrics/AbcSize
def
source_ids
return
id
unless
parent_id
::
Gitlab
::
ObjectHierarchy
.
new
(
self
.
class
.
where
(
id:
id
))
.
base_and_ancestors
.
reorder
(
nil
).
select
(
:id
)
end
def
smallest_value_arel
(
args
,
column_alias
)
Arel
::
Nodes
::
As
.
new
(
Arel
::
Nodes
::
NamedFunction
.
new
(
'LEAST'
,
args
),
Arel
::
Nodes
::
SqlLiteral
.
new
(
column_alias
))
end
end
override
:perform
def
perform
(
start_id
,
stop_id
)
confidential_epic_ids
=
Epic
.
where
(
confidential:
true
).
where
(
id:
start_id
..
stop_id
).
ids
epic_todos
=
Todo
.
where
(
target_type:
'Epic'
,
target_id:
confidential_epic_ids
)
.
includes
(
:epic
,
:user
)
ids_to_delete
=
not_readable_epic_todo_ids
(
epic_todos
)
logger
.
info
(
message:
'Deleting confidential epic todos'
,
todo_ids:
ids_to_delete
)
Todo
.
where
(
id:
ids_to_delete
).
delete_all
end
private
def
not_readable_epic_todo_ids
(
todos
)
todos
.
map
do
|
todo
|
next
todo
.
id
unless
todo
.
epic
next
if
todo
.
epic
.
can_read_confidential?
(
todo
.
user
)
todo
.
id
end
.
compact
end
def
logger
@logger
||=
::
Gitlab
::
BackgroundMigration
::
Logger
.
build
end
end
end
end
end
ee/spec/lib/ee/gitlab/background_migration/remove_inaccessible_epic_todos_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
BackgroundMigration
::
RemoveInaccessibleEpicTodos
,
schema:
20201109114603
do
include
MigrationHelpers
::
NamespacesHelpers
let
(
:users
)
{
table
(
:users
)
}
let
(
:todos
)
{
table
(
:todos
)
}
let
(
:epics
)
{
table
(
:epics
)
}
let
(
:members_table
)
{
table
(
:members
)
}
let
(
:group_group_links
)
{
table
(
:group_group_links
)
}
let
(
:author
)
{
users
.
create!
(
email:
'author@example.com'
,
projects_limit:
10
)
}
let
(
:user
)
{
users
.
create!
(
email:
'user@example.com'
,
projects_limit:
10
)
}
let
(
:group_root
)
{
create_namespace
(
'root'
,
Gitlab
::
VisibilityLevel
::
PUBLIC
)
}
let
(
:group_level1
)
{
create_namespace
(
'level1'
,
Gitlab
::
VisibilityLevel
::
PUBLIC
,
parent_id:
group_root
.
id
)
}
let
(
:epic_conf1
)
{
epics
.
create!
(
iid:
1
,
title:
'confidential1'
,
title_html:
'confidential1'
,
confidential:
true
,
group_id:
group_root
.
id
,
author_id:
author
.
id
)
}
let
(
:epic_conf2
)
{
epics
.
create!
(
iid:
1
,
title:
'confidential2'
,
title_html:
'confidential2'
,
confidential:
true
,
group_id:
group_level1
.
id
,
author_id:
author
.
id
)
}
let
(
:epic_public1
)
{
epics
.
create!
(
iid:
2
,
title:
'public1'
,
title_html:
'epic_public1'
,
group_id:
group_root
.
id
,
author_id:
author
.
id
)
}
let
(
:epic_public2
)
{
epics
.
create!
(
iid:
2
,
title:
'public1'
,
title_html:
'epic_public2'
,
group_id:
group_level1
.
id
,
author_id:
author
.
id
)
}
let!
(
:todo1
)
{
todos
.
create!
(
target_type:
'Epic'
,
target_id:
epic_conf1
.
id
,
user_id:
user
.
id
,
author_id:
user
.
id
,
action:
2
,
state:
0
)
}
let!
(
:todo2
)
{
todos
.
create!
(
target_type:
'Epic'
,
target_id:
epic_conf2
.
id
,
user_id:
user
.
id
,
author_id:
user
.
id
,
action:
2
,
state:
0
)
}
let!
(
:todo3
)
{
todos
.
create!
(
target_type:
'Epic'
,
target_id:
epic_public1
.
id
,
user_id:
user
.
id
,
author_id:
user
.
id
,
action:
2
,
state:
0
)
}
let!
(
:todo4
)
{
todos
.
create!
(
target_type:
'Epic'
,
target_id:
epic_public2
.
id
,
user_id:
user
.
id
,
author_id:
user
.
id
,
action:
2
,
state:
0
)
}
describe
'#perform'
do
subject
(
:perform
)
{
described_class
.
new
.
perform
(
epics
.
first
.
id
,
epics
.
last
.
id
)
}
def
expect_todos
(
preserved
:)
expect
{
subject
}.
to
change
{
todos
.
count
}.
by
(
preserved
.
count
-
4
)
existing_ids
=
todos
.
pluck
(
:id
)
expect
(
existing_ids
).
to
match_array
(
preserved
)
end
context
'when user is not member of related groups'
do
it
'deletes only todos referencing confidential epics'
do
expect_todos
(
preserved:
[
todo3
.
id
,
todo4
.
id
])
end
end
context
'when user is only guest member of related groups'
do
let!
(
:member
)
do
members_table
.
create!
(
user_id:
user
.
id
,
source_id:
group_root
.
id
,
source_type:
'Namespace'
,
type:
'GroupMember'
,
access_level:
10
,
notification_level:
3
)
end
it
'deletes todos referencing confidential epics'
do
expect_todos
(
preserved:
[
todo3
.
id
,
todo4
.
id
])
end
end
context
'when user is member of subgroup'
do
let!
(
:member
)
do
members_table
.
create!
(
user_id:
user
.
id
,
source_id:
group_level1
.
id
,
source_type:
'Namespace'
,
type:
'GroupMember'
,
access_level:
20
,
notification_level:
3
)
end
it
'deletes only epic todos in the root group'
do
expect_todos
(
preserved:
[
todo2
.
id
,
todo3
.
id
,
todo4
.
id
])
end
end
context
'when user is member of root group'
do
let!
(
:member
)
do
members_table
.
create!
(
user_id:
user
.
id
,
source_id:
group_root
.
id
,
source_type:
'Namespace'
,
type:
'GroupMember'
,
access_level:
20
,
notification_level:
3
)
end
it
'does not delete any todos'
do
expect_todos
(
preserved:
[
todo1
.
id
,
todo2
.
id
,
todo3
.
id
,
todo4
.
id
])
end
end
context
'when user is only guest on root group'
do
let!
(
:root_member
)
do
members_table
.
create!
(
user_id:
user
.
id
,
source_id:
group_root
.
id
,
source_type:
'Namespace'
,
type:
'GroupMember'
,
access_level:
10
,
notification_level:
3
)
end
let!
(
:subgroup_member
)
do
members_table
.
create!
(
user_id:
user
.
id
,
source_id:
group_level1
.
id
,
source_type:
'Namespace'
,
type:
'GroupMember'
,
access_level:
20
,
notification_level:
3
)
end
it
'deletes only root confidential epic todo'
do
expect_todos
(
preserved:
[
todo2
.
id
,
todo3
.
id
,
todo4
.
id
])
end
end
context
'when root group is shared with other group'
do
let!
(
:other_group
)
{
create_namespace
(
'other_group'
,
Gitlab
::
VisibilityLevel
::
PRIVATE
)
}
let!
(
:member
)
do
members_table
.
create!
(
user_id:
user
.
id
,
source_id:
other_group
.
id
,
source_type:
'Namespace'
,
type:
'GroupMember'
,
access_level:
20
,
notification_level:
3
)
end
let!
(
:group_link
)
do
group_group_links
.
create!
(
shared_group_id:
group_root
.
id
,
shared_with_group_id:
other_group
.
id
,
group_access:
20
)
end
it
'does not delete any todos'
do
expect_todos
(
preserved:
[
todo1
.
id
,
todo2
.
id
,
todo3
.
id
,
todo4
.
id
])
end
end
end
end
ee/spec/migrations/schedule_remove_inaccessible_epic_todos_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
require
Rails
.
root
.
join
(
'db'
,
'post_migrate'
,
'20201109114603_schedule_remove_inaccessible_epic_todos'
)
RSpec
.
describe
ScheduleRemoveInaccessibleEpicTodos
do
let
(
:group
)
{
table
(
:namespaces
).
create!
(
name:
'gitlab'
,
path:
'gitlab-org'
)
}
let
(
:user
)
{
table
(
:users
).
create!
(
email:
'user@example.com'
,
projects_limit:
10
)
}
let!
(
:epic1
)
{
table
(
:epics
).
create!
(
iid:
1
,
title:
'foo'
,
title_html:
'foo'
,
group_id:
group
.
id
,
author_id:
user
.
id
,
confidential:
true
)
}
let!
(
:epic2
)
{
table
(
:epics
).
create!
(
iid:
2
,
title:
'foo'
,
title_html:
'foo'
,
group_id:
group
.
id
,
author_id:
user
.
id
)
}
let!
(
:epic3
)
{
table
(
:epics
).
create!
(
iid:
3
,
title:
'foo'
,
title_html:
'foo'
,
group_id:
group
.
id
,
author_id:
user
.
id
,
confidential:
true
)
}
before
do
stub_const
(
"
#{
described_class
.
name
}
::BATCH_SIZE"
,
1
)
end
it
'schedules jobs for confidental epic todos'
do
Sidekiq
::
Testing
.
fake!
do
freeze_time
do
migrate!
expect
(
described_class
::
MIGRATION
).
to
be_scheduled_delayed_migration
(
2
.
minutes
,
epic1
.
id
,
epic1
.
id
)
expect
(
described_class
::
MIGRATION
).
to
be_scheduled_delayed_migration
(
4
.
minutes
,
epic3
.
id
,
epic3
.
id
)
expect
(
BackgroundMigrationWorker
.
jobs
.
size
).
to
eq
(
2
)
end
end
end
end
ee/spec/services/ee/todos/destroy/entity_leave_service_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Todos
::
Destroy
::
EntityLeaveService
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:subgroup
)
{
create
(
:group
,
parent:
group
)
}
let_it_be
(
:epic1
)
{
create
(
:epic
,
confidential:
true
,
group:
subgroup
)
}
let_it_be
(
:epic2
)
{
create
(
:epic
,
group:
subgroup
)
}
let!
(
:todo1
)
{
create
(
:todo
,
target:
epic1
,
user:
user
,
group:
subgroup
)
}
let!
(
:todo2
)
{
create
(
:todo
,
target:
epic2
,
user:
user
,
group:
subgroup
)
}
describe
'#execute'
do
subject
{
described_class
.
new
(
user
.
id
,
subgroup
.
id
,
'Group'
).
execute
}
shared_examples
'removes only confidential epics todos'
do
it
'removes todos targeting confidential epics in the group'
do
expect
{
subject
}.
to
change
{
Todo
.
count
}.
by
(
-
1
)
expect
(
user
.
reload
.
todos
.
ids
).
to
match_array
(
todo2
.
id
)
end
end
it_behaves_like
'removes only confidential epics todos'
context
'when user is still member of ancestor group'
do
before
do
group
.
add_reporter
(
user
)
end
it
'does not remove todos targeting confidential epics in the group'
do
expect
{
subject
}.
not_to
change
{
Todo
.
count
}
end
end
context
'when user role is downgraded to guest'
do
before
do
subgroup
.
add_guest
(
user
)
end
it_behaves_like
'removes only confidential epics todos'
end
end
end
ee/spec/services/epics/update_service_spec.rb
View file @
52cfce63
...
@@ -216,6 +216,12 @@ RSpec.describe Epics::UpdateService do
...
@@ -216,6 +216,12 @@ RSpec.describe Epics::UpdateService do
end
end
end
end
end
end
it
'schedules deletion of todos when epic becomes confidential'
do
expect
(
TodosDestroyer
::
ConfidentialEpicWorker
).
to
receive
(
:perform_in
).
with
(
Todo
::
WAIT_FOR_DELETE
,
epic
.
id
)
update_epic
(
confidential:
true
)
end
end
end
context
'when Epic has tasks'
do
context
'when Epic has tasks'
do
...
...
ee/spec/services/todos/destroy/confidential_epic_service_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Todos
::
Destroy
::
ConfidentialEpicService
do
let_it_be
(
:group
)
{
create
(
:group
,
:public
)
}
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:author
)
{
create
(
:user
)
}
let_it_be
(
:guest
)
{
create
(
:user
)
}
let_it_be
(
:group_member
)
{
create
(
:user
)
}
let_it_be
(
:shared_user
)
{
create
(
:user
)
}
let_it_be
(
:group_link
)
{
create
(
:group_group_link
,
shared_group:
group
)
}
let_it_be
(
:epic_1
,
reload:
true
)
{
create
(
:epic
,
:confidential
,
group:
group
,
author:
author
)
}
let!
(
:todos
)
do
[
# todos not to be deleted
create
(
:todo
,
user:
group_member
,
target:
epic_1
,
group:
group
),
create
(
:todo
,
user:
user
,
group:
group
),
create
(
:todo
,
user:
shared_user
,
target:
epic_1
,
group:
group
),
# Todos to be deleted
create
(
:todo
,
user:
guest
,
target:
epic_1
,
group:
group
),
create
(
:todo
,
user:
user
,
target:
epic_1
,
group:
group
)
]
end
describe
'#execute'
do
before
do
group
.
add_reporter
(
group_member
)
group
.
add_guest
(
guest
)
group_link
.
shared_with_group
.
add_reporter
(
shared_user
)
end
subject
{
described_class
.
new
(
epic_id:
epic_1
.
id
).
execute
}
it
'removes epic todos for users who can not access the confidential epic'
do
expect
{
subject
}.
to
change
{
Todo
.
count
}.
by
(
-
2
)
end
context
'when provided epic is not confidential'
do
before
do
epic_1
.
update!
(
confidential:
false
)
end
it
'does not remove any todos'
do
expect
{
subject
}.
not_to
change
{
Todo
.
count
}
end
end
end
end
ee/spec/workers/todos_destroyer/confidential_epic_worker_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
TodosDestroyer
::
ConfidentialEpicWorker
do
let
(
:service
)
{
double
}
it
'calls the Todos::Destroy::ConfidentialEpicService with epic_id parameter'
do
expect
(
::
Todos
::
Destroy
::
ConfidentialEpicService
).
to
receive
(
:new
).
with
(
epic_id:
100
).
and_return
(
service
)
expect
(
service
).
to
receive
(
:execute
)
described_class
.
new
.
perform
(
100
)
end
end
lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
module
Gitlab
module
BackgroundMigration
# rubocop:disable Style/Documentation
class
RemoveInaccessibleEpicTodos
def
perform
(
start_id
,
stop_id
)
end
end
end
end
Gitlab
::
BackgroundMigration
::
RemoveInaccessibleEpicTodos
.
prepend_if_ee
(
'EE::Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos'
)
spec/controllers/confirmations_controller_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
ConfirmationsController
do
include
DeviseHelpers
before
do
set_devise_mapping
(
context:
@request
)
end
describe
'#show'
do
render_views
subject
{
get
:show
,
params:
{
confirmation_token:
confirmation_token
}
}
context
'user is already confirmed'
do
let_it_be_with_reload
(
:user
)
{
create
(
:user
,
:unconfirmed
)
}
let
(
:confirmation_token
)
{
user
.
confirmation_token
}
before
do
user
.
confirm
subject
end
it
'renders `new`'
do
expect
(
response
).
to
render_template
(
:new
)
end
it
'displays an error message'
do
expect
(
response
.
body
).
to
include
(
'Email was already confirmed, please try signing in'
)
end
it
'does not display the email of the user'
do
expect
(
response
.
body
).
not_to
include
(
user
.
email
)
end
end
context
'user accesses the link after the expiry of confirmation token has passed'
do
let_it_be_with_reload
(
:user
)
{
create
(
:user
,
:unconfirmed
)
}
let
(
:confirmation_token
)
{
user
.
confirmation_token
}
before
do
allow
(
Devise
).
to
receive
(
:confirm_within
).
and_return
(
1
.
day
)
travel_to
(
3
.
days
.
from_now
)
do
subject
end
end
it
'renders `new`'
do
expect
(
response
).
to
render_template
(
:new
)
end
it
'displays an error message'
do
expect
(
response
.
body
).
to
include
(
'Email needs to be confirmed within 1 day, please request a new one below'
)
end
it
'does not display the email of the user'
do
expect
(
response
.
body
).
not_to
include
(
user
.
email
)
end
end
context
'with an invalid confirmation token'
do
let
(
:confirmation_token
)
{
'invalid_confirmation_token'
}
before
do
subject
end
it
'renders `new`'
do
expect
(
response
).
to
render_template
(
:new
)
end
it
'displays an error message'
do
expect
(
response
.
body
).
to
include
(
'Confirmation token is invalid'
)
end
end
end
end
spec/controllers/projects/feature_flags_controller_spec.rb
View file @
52cfce63
...
@@ -1419,6 +1419,40 @@ RSpec.describe Projects::FeatureFlagsController do
...
@@ -1419,6 +1419,40 @@ RSpec.describe Projects::FeatureFlagsController do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
it
'returns not found when trying to update a gitlabUserList strategy with a user list from another project'
do
user_list
=
create
(
:operations_feature_flag_user_list
,
project:
project
,
name:
'My List'
,
user_xids:
'user1,user2'
)
strategy
=
create
(
:operations_strategy
,
feature_flag:
new_version_flag
,
name:
'gitlabUserList'
,
parameters:
{},
user_list:
user_list
)
other_project
=
create
(
:project
)
other_user_list
=
create
(
:operations_feature_flag_user_list
,
project:
other_project
,
name:
'Other List'
,
user_xids:
'some,one'
)
put_request
(
new_version_flag
,
strategies_attributes:
[{
id:
strategy
.
id
,
user_list_id:
other_user_list
.
id
}])
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
expect
(
strategy
.
reload
.
user_list
).
to
eq
(
user_list
)
end
it
'allows setting multiple gitlabUserList strategies to the same user list'
do
user_list_a
=
create
(
:operations_feature_flag_user_list
,
project:
project
,
name:
'My List A'
,
user_xids:
'user1,user2'
)
user_list_b
=
create
(
:operations_feature_flag_user_list
,
project:
project
,
name:
'My List B'
,
user_xids:
'user3,user4'
)
strategy_a
=
create
(
:operations_strategy
,
feature_flag:
new_version_flag
,
name:
'gitlabUserList'
,
parameters:
{},
user_list:
user_list_a
)
strategy_b
=
create
(
:operations_strategy
,
feature_flag:
new_version_flag
,
name:
'gitlabUserList'
,
parameters:
{},
user_list:
user_list_a
)
put_request
(
new_version_flag
,
strategies_attributes:
[{
id:
strategy_a
.
id
,
user_list_id:
user_list_b
.
id
},
{
id:
strategy_b
.
id
,
user_list_id:
user_list_b
.
id
}])
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
strategy_a
.
reload
.
user_list
).
to
eq
(
user_list_b
)
expect
(
strategy_b
.
reload
.
user_list
).
to
eq
(
user_list_b
)
end
it
'updates an existing strategy'
do
it
'updates an existing strategy'
do
strategy
=
create
(
:operations_strategy
,
feature_flag:
new_version_flag
,
name:
'default'
,
parameters:
{})
strategy
=
create
(
:operations_strategy
,
feature_flag:
new_version_flag
,
name:
'default'
,
parameters:
{})
...
...
spec/controllers/search_controller_spec.rb
View file @
52cfce63
...
@@ -272,7 +272,7 @@ RSpec.describe SearchController do
...
@@ -272,7 +272,7 @@ RSpec.describe SearchController do
expect
(
last_payload
[
:metadata
][
'meta.search.group_id'
]).
to
eq
(
'123'
)
expect
(
last_payload
[
:metadata
][
'meta.search.group_id'
]).
to
eq
(
'123'
)
expect
(
last_payload
[
:metadata
][
'meta.search.project_id'
]).
to
eq
(
'456'
)
expect
(
last_payload
[
:metadata
][
'meta.search.project_id'
]).
to
eq
(
'456'
)
expect
(
last_payload
[
:metadata
]
[
'meta.search.search'
]).
to
eq
(
'hello world
'
)
expect
(
last_payload
[
:metadata
]
).
not_to
have_key
(
'meta.search.search
'
)
expect
(
last_payload
[
:metadata
][
'meta.search.scope'
]).
to
eq
(
'issues'
)
expect
(
last_payload
[
:metadata
][
'meta.search.scope'
]).
to
eq
(
'issues'
)
expect
(
last_payload
[
:metadata
][
'meta.search.force_search_results'
]).
to
eq
(
'true'
)
expect
(
last_payload
[
:metadata
][
'meta.search.force_search_results'
]).
to
eq
(
'true'
)
expect
(
last_payload
[
:metadata
][
'meta.search.filters.confidential'
]).
to
eq
(
'true'
)
expect
(
last_payload
[
:metadata
][
'meta.search.filters.confidential'
]).
to
eq
(
'true'
)
...
...
spec/controllers/users_controller_spec.rb
View file @
52cfce63
...
@@ -354,32 +354,99 @@ RSpec.describe UsersController do
...
@@ -354,32 +354,99 @@ RSpec.describe UsersController do
describe
'GET #contributed'
do
describe
'GET #contributed'
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let
(
:project
)
{
create
(
:project
,
:public
)
}
let
(
:current_user
)
{
create
(
:user
)
}
subject
do
get
:contributed
,
params:
{
username:
author
.
username
},
format:
format
end
before
do
before
do
sign_in
(
current_
user
)
sign_in
(
user
)
project
.
add_developer
(
public_user
)
project
.
add_developer
(
public_user
)
project
.
add_developer
(
private_user
)
project
.
add_developer
(
private_user
)
create
(
:push_event
,
project:
project
,
author:
author
)
subject
end
end
context
'with public profile
'
do
shared_examples_for
'renders contributed projects
'
do
it
'renders contributed projects'
do
it
'renders contributed projects'
do
create
(
:push_event
,
project:
project
,
author:
public_user
)
expect
(
assigns
[
:contributed_projects
]).
not_to
be_empty
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
end
end
get
:contributed
,
params:
{
username:
public_user
.
username
}
%i(html json)
.
each
do
|
format
|
context
"format:
#{
format
}
"
do
let
(
:format
)
{
format
}
expect
(
assigns
[
:contributed_projects
]).
not_to
be_empty
context
'with public profile'
do
let
(
:author
)
{
public_user
}
it_behaves_like
'renders contributed projects'
end
context
'with private profile'
do
let
(
:author
)
{
private_user
}
it
'returns 404'
do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
context
'with a user that has the ability to read private profiles'
,
:enable_admin_mode
do
let
(
:user
)
{
create
(
:admin
)
}
it_behaves_like
'renders contributed projects'
end
end
end
end
end
describe
'GET #starred'
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
subject
do
get
:starred
,
params:
{
username:
author
.
username
},
format:
format
end
before
do
author
.
toggle_star
(
project
)
sign_in
(
user
)
subject
end
shared_examples_for
'renders starred projects'
do
it
'renders starred projects'
do
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
assigns
[
:starred_projects
]).
not_to
be_empty
end
end
end
end
context
'with private profile'
do
%i(html json)
.
each
do
|
format
|
it
'does not render contributed projects'
do
context
"format:
#{
format
}
"
do
create
(
:push_event
,
project:
project
,
author:
private_user
)
let
(
:format
)
{
format
}
context
'with public profile'
do
let
(
:author
)
{
public_user
}
it_behaves_like
'renders starred projects'
end
context
'with private profile'
do
let
(
:author
)
{
private_user
}
it
'returns 404'
do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
get
:contributed
,
params:
{
username:
private_user
.
username
}
context
'with a user that has the ability to read private profiles'
,
:enable_admin_mode
do
let
(
:user
)
{
create
(
:admin
)
}
expect
(
assigns
[
:contributed_projects
]).
to
be_empty
it_behaves_like
'renders starred projects'
end
end
end
end
end
end
end
end
...
...
spec/features/explore/user_explores_projects_spec.rb
View file @
52cfce63
...
@@ -47,6 +47,14 @@ RSpec.describe 'User explores projects' do
...
@@ -47,6 +47,14 @@ RSpec.describe 'User explores projects' do
end
end
end
end
shared_examples
'minimum search length'
do
it
'shows a prompt to enter a longer search term'
,
:js
do
fill_in
'name'
,
with:
'z'
expect
(
page
).
to
have_content
(
'Enter at least three characters to search'
)
end
end
context
'when viewing public projects'
do
context
'when viewing public projects'
do
before
do
before
do
visit
(
explore_projects_path
)
visit
(
explore_projects_path
)
...
@@ -54,6 +62,7 @@ RSpec.describe 'User explores projects' do
...
@@ -54,6 +62,7 @@ RSpec.describe 'User explores projects' do
include_examples
'shows public and internal projects'
include_examples
'shows public and internal projects'
include_examples
'empty search results'
include_examples
'empty search results'
include_examples
'minimum search length'
end
end
context
'when viewing most starred projects'
do
context
'when viewing most starred projects'
do
...
@@ -63,6 +72,7 @@ RSpec.describe 'User explores projects' do
...
@@ -63,6 +72,7 @@ RSpec.describe 'User explores projects' do
include_examples
'shows public and internal projects'
include_examples
'shows public and internal projects'
include_examples
'empty search results'
include_examples
'empty search results'
include_examples
'minimum search length'
end
end
context
'when viewing trending projects'
do
context
'when viewing trending projects'
do
...
@@ -76,6 +86,7 @@ RSpec.describe 'User explores projects' do
...
@@ -76,6 +86,7 @@ RSpec.describe 'User explores projects' do
include_examples
'shows public projects'
include_examples
'shows public projects'
include_examples
'empty search results'
include_examples
'empty search results'
include_examples
'minimum search length'
end
end
end
end
end
end
...
...
spec/features/markdown/mermaid_spec.rb
View file @
52cfce63
...
@@ -19,6 +19,9 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -19,6 +19,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
wait_for_mermaid
%w[A B C D]
.
each
do
|
label
|
%w[A B C D]
.
each
do
|
label
|
expect
(
page
).
to
have_selector
(
'svg text'
,
text:
label
)
expect
(
page
).
to
have_selector
(
'svg text'
,
text:
label
)
end
end
...
@@ -39,6 +42,7 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -39,6 +42,7 @@ RSpec.describe 'Mermaid rendering', :js do
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
wait_for_requests
wait_for_mermaid
expected
=
'<text style=""><tspan xml:space="preserve" dy="1em" x="1">Line 1</tspan><tspan xml:space="preserve" dy="1em" x="1">Line 2</tspan></text>'
expected
=
'<text style=""><tspan xml:space="preserve" dy="1em" x="1">Line 1</tspan><tspan xml:space="preserve" dy="1em" x="1">Line 2</tspan></text>'
expect
(
page
.
html
.
scan
(
expected
).
count
).
to
be
(
4
)
expect
(
page
.
html
.
scan
(
expected
).
count
).
to
be
(
4
)
...
@@ -65,6 +69,9 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -65,6 +69,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
wait_for_mermaid
page
.
within
(
'.description'
)
do
page
.
within
(
'.description'
)
do
expect
(
page
).
to
have_selector
(
'svg'
)
expect
(
page
).
to
have_selector
(
'svg'
)
expect
(
page
).
to
have_selector
(
'pre.mermaid'
)
expect
(
page
).
to
have_selector
(
'pre.mermaid'
)
...
@@ -92,6 +99,9 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -92,6 +99,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
wait_for_mermaid
page
.
within
(
'.description'
)
do
page
.
within
(
'.description'
)
do
page
.
find
(
'summary'
).
click
page
.
find
(
'summary'
).
click
svg
=
page
.
find
(
'svg.mermaid'
)
svg
=
page
.
find
(
'svg.mermaid'
)
...
@@ -118,6 +128,9 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -118,6 +128,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit
project_issue_path
(
project
,
issue
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
wait_for_mermaid
expect
(
page
).
to
have_css
(
'svg.mermaid[style*="max-width"][width="100%"]'
)
expect
(
page
).
to
have_css
(
'svg.mermaid[style*="max-width"][width="100%"]'
)
end
end
...
@@ -147,6 +160,7 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -147,6 +160,7 @@ RSpec.describe 'Mermaid rendering', :js do
end
end
wait_for_requests
wait_for_requests
wait_for_mermaid
find
(
'.js-lazy-render-mermaid'
).
click
find
(
'.js-lazy-render-mermaid'
).
click
...
@@ -156,4 +170,55 @@ RSpec.describe 'Mermaid rendering', :js do
...
@@ -156,4 +170,55 @@ RSpec.describe 'Mermaid rendering', :js do
expect
(
page
).
not_to
have_selector
(
'.js-lazy-render-mermaid-container'
)
expect
(
page
).
not_to
have_selector
(
'.js-lazy-render-mermaid-container'
)
end
end
end
end
it
'does not render more than 50 mermaid blocks'
,
:js
,
quarantine:
{
issue:
'https://gitlab.com/gitlab-org/gitlab/-/issues/234081'
}
do
graph_edges
=
"A-->B;B-->A;"
description
=
<<~
MERMAID
```mermaid
graph LR
#{
graph_edges
}
```
MERMAID
description
*=
51
project
=
create
(
:project
,
:public
)
issue
=
create
(
:issue
,
project:
project
,
description:
description
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
wait_for_mermaid
page
.
within
(
'.description'
)
do
expect
(
page
).
to
have_selector
(
'svg'
)
expect
(
page
).
to
have_selector
(
'.lazy-alert-shown'
)
expect
(
page
).
to
have_selector
(
'.js-lazy-render-mermaid-container'
)
end
end
end
def
wait_for_mermaid
run_idle_callback
=
<<~
RUN_IDLE_CALLBACK
window.requestIdleCallback(() => {
window.__CAPYBARA_IDLE_CALLBACK_EXEC__ = 1;
})
RUN_IDLE_CALLBACK
page
.
evaluate_script
(
run_idle_callback
)
Timeout
.
timeout
(
Capybara
.
default_max_wait_time
)
do
loop
until
finished_rendering?
end
end
def
finished_rendering?
check_idle_callback
=
<<~
CHECK_IDLE_CALLBACK
window.__CAPYBARA_IDLE_CALLBACK_EXEC__
CHECK_IDLE_CALLBACK
page
.
evaluate_script
(
check_idle_callback
)
==
1
end
end
spec/finders/projects_finder_spec.rb
View file @
52cfce63
...
@@ -161,6 +161,29 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do
...
@@ -161,6 +161,29 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do
it
{
is_expected
.
to
eq
([
public_project
])
}
it
{
is_expected
.
to
eq
([
public_project
])
}
end
end
describe
'filter by search with minimum search length'
do
context
'when search term is shorter than minimum length'
do
let
(
:params
)
{
{
search:
'C'
,
minimum_search_length:
3
}
}
it
{
is_expected
.
to
be_empty
}
end
context
'when search term is longer than minimum length'
do
let
(
:project
)
{
create
(
:project
,
:public
,
group:
group
,
name:
'test_project'
)
}
let
(
:params
)
{
{
search:
'test'
,
minimum_search_length:
3
}
}
it
{
is_expected
.
to
eq
([
project
])
}
end
context
'when minimum length is invalid'
do
let
(
:params
)
{
{
search:
'C'
,
minimum_search_length:
'x'
}
}
it
'ignores the minimum length param'
do
is_expected
.
to
eq
([
public_project
])
end
end
end
describe
'filter by group name'
do
describe
'filter by group name'
do
let
(
:params
)
{
{
name:
group
.
name
,
search_namespaces:
true
}
}
let
(
:params
)
{
{
name:
group
.
name
,
search_namespaces:
true
}
}
...
...
spec/finders/starred_projects_finder_spec.rb
View file @
52cfce63
...
@@ -5,7 +5,7 @@ require 'spec_helper'
...
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec
.
describe
StarredProjectsFinder
do
RSpec
.
describe
StarredProjectsFinder
do
let
(
:project1
)
{
create
(
:project
,
:public
,
:empty_repo
)
}
let
(
:project1
)
{
create
(
:project
,
:public
,
:empty_repo
)
}
let
(
:project2
)
{
create
(
:project
,
:public
,
:empty_repo
)
}
let
(
:project2
)
{
create
(
:project
,
:public
,
:empty_repo
)
}
let
(
:
other_project
)
{
create
(
:project
,
:public
,
:empty_repo
)
}
let
(
:
private_project
)
{
create
(
:project
,
:private
,
:empty_repo
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:other_user
)
{
create
(
:user
)
}
let
(
:other_user
)
{
create
(
:user
)
}
...
@@ -13,6 +13,9 @@ RSpec.describe StarredProjectsFinder do
...
@@ -13,6 +13,9 @@ RSpec.describe StarredProjectsFinder do
before
do
before
do
user
.
toggle_star
(
project1
)
user
.
toggle_star
(
project1
)
user
.
toggle_star
(
project2
)
user
.
toggle_star
(
project2
)
private_project
.
add_maintainer
(
user
)
user
.
toggle_star
(
private_project
)
end
end
describe
'#execute'
do
describe
'#execute'
do
...
@@ -20,22 +23,56 @@ RSpec.describe StarredProjectsFinder do
...
@@ -20,22 +23,56 @@ RSpec.describe StarredProjectsFinder do
subject
{
finder
.
execute
}
subject
{
finder
.
execute
}
describe
'as same user'
do
context
'user has a public profile'
do
let
(
:current_user
)
{
user
}
describe
'as same user'
do
let
(
:current_user
)
{
user
}
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
)
}
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
,
private_project
)
}
end
end
describe
'as other user'
do
let
(
:current_user
)
{
other_user
}
describe
'as other user'
do
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
)
}
let
(
:current_user
)
{
other_user
}
end
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
)
}
describe
'as no user'
do
let
(
:current_user
)
{
nil
}
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
)
}
end
end
end
describe
'as no user'
do
context
'user has a private profile'
do
let
(
:current_user
)
{
nil
}
before
do
user
.
update!
(
private_profile:
true
)
end
describe
'as same user'
do
let
(
:current_user
)
{
user
}
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
,
private_project
)
}
end
describe
'as other user'
do
context
'user does not have access to view the private profile'
do
let
(
:current_user
)
{
other_user
}
it
{
is_expected
.
to
be_empty
}
end
context
'user has access to view the private profile'
,
:enable_admin_mode
do
let
(
:current_user
)
{
create
(
:admin
)
}
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
,
private_project
)
}
end
end
describe
'as no user'
do
let
(
:current_user
)
{
nil
}
it
{
is_expected
.
to
contain_exactly
(
project1
,
project2
)
}
it
{
is_expected
.
to
be_empty
}
end
end
end
end
end
end
end
spec/requests/api/graphql/user/starred_projects_query_spec.rb
View file @
52cfce63
...
@@ -70,4 +70,31 @@ RSpec.describe 'Getting starredProjects of the user' do
...
@@ -70,4 +70,31 @@ RSpec.describe 'Getting starredProjects of the user' do
)
)
end
end
end
end
context
'the user has a private profile'
do
before
do
user
.
update!
(
private_profile:
true
)
post_graphql
(
query
,
current_user:
current_user
)
end
context
'the current user does not have access to view the private profile of the user'
do
let
(
:current_user
)
{
create
(
:user
)
}
it
'finds no projects'
do
expect
(
starred_projects
).
to
be_empty
end
end
context
'the current user has access to view the private profile of the user'
do
let
(
:current_user
)
{
create
(
:admin
)
}
it
'finds all projects starred by the user, which the current user has access to'
do
expect
(
starred_projects
).
to
contain_exactly
(
a_hash_including
(
'id'
=>
global_id_of
(
project_a
)),
a_hash_including
(
'id'
=>
global_id_of
(
project_b
)),
a_hash_including
(
'id'
=>
global_id_of
(
project_c
))
)
end
end
end
end
end
spec/requests/api/graphql/user_query_spec.rb
View file @
52cfce63
...
@@ -82,7 +82,7 @@ RSpec.describe 'getting user information' do
...
@@ -82,7 +82,7 @@ RSpec.describe 'getting user information' do
'username'
=>
presenter
.
username
,
'username'
=>
presenter
.
username
,
'webUrl'
=>
presenter
.
web_url
,
'webUrl'
=>
presenter
.
web_url
,
'avatarUrl'
=>
presenter
.
avatar_url
,
'avatarUrl'
=>
presenter
.
avatar_url
,
'email'
=>
presenter
.
email
,
'email'
=>
presenter
.
public_
email
,
'publicEmail'
=>
presenter
.
public_email
'publicEmail'
=>
presenter
.
public_email
))
))
...
@@ -251,7 +251,7 @@ RSpec.describe 'getting user information' do
...
@@ -251,7 +251,7 @@ RSpec.describe 'getting user information' do
context
'the user is private'
do
context
'the user is private'
do
before
do
before
do
user
.
update
(
private_profile:
true
)
user
.
update
!
(
private_profile:
true
)
post_graphql
(
query
,
current_user:
current_user
)
post_graphql
(
query
,
current_user:
current_user
)
end
end
...
@@ -261,6 +261,50 @@ RSpec.describe 'getting user information' do
...
@@ -261,6 +261,50 @@ RSpec.describe 'getting user information' do
it_behaves_like
'a working graphql query'
it_behaves_like
'a working graphql query'
end
end
context
'we request the groupMemberships'
do
let_it_be
(
:membership_a
)
{
create
(
:group_member
,
user:
user
)
}
let
(
:group_memberships
)
{
graphql_data_at
(
:user
,
:group_memberships
,
:nodes
)
}
let
(
:user_fields
)
{
'groupMemberships { nodes { id } }'
}
it_behaves_like
'a working graphql query'
it
'cannot be found'
do
expect
(
group_memberships
).
to
be_empty
end
context
'the current user is the user'
do
let
(
:current_user
)
{
user
}
it
'can be found'
do
expect
(
group_memberships
).
to
include
(
a_hash_including
(
'id'
=>
global_id_of
(
membership_a
))
)
end
end
end
context
'we request the projectMemberships'
do
let_it_be
(
:membership_a
)
{
create
(
:project_member
,
user:
user
)
}
let
(
:project_memberships
)
{
graphql_data_at
(
:user
,
:project_memberships
,
:nodes
)
}
let
(
:user_fields
)
{
'projectMemberships { nodes { id } }'
}
it_behaves_like
'a working graphql query'
it
'cannot be found'
do
expect
(
project_memberships
).
to
be_empty
end
context
'the current user is the user'
do
let
(
:current_user
)
{
user
}
it
'can be found'
do
expect
(
project_memberships
).
to
include
(
a_hash_including
(
'id'
=>
global_id_of
(
membership_a
))
)
end
end
end
context
'we request the authoredMergeRequests'
do
context
'we request the authoredMergeRequests'
do
let
(
:user_fields
)
{
'authoredMergeRequests { nodes { id } }'
}
let
(
:user_fields
)
{
'authoredMergeRequests { nodes { id } }'
}
...
...
spec/requests/api/projects_spec.rb
View file @
52cfce63
...
@@ -1255,13 +1255,46 @@ RSpec.describe API::Projects do
...
@@ -1255,13 +1255,46 @@ RSpec.describe API::Projects do
expect
(
json_response
[
'message'
]).
to
eq
(
'404 User Not Found'
)
expect
(
json_response
[
'message'
]).
to
eq
(
'404 User Not Found'
)
end
end
it
'returns projects filtered by user'
do
context
'with a public profile'
do
get
api
(
"/users/
#{
user3
.
id
}
/starred_projects/"
,
user
)
it
'returns projects filtered by user'
do
get
api
(
"/users/
#{
user3
.
id
}
/starred_projects/"
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
include_pagination_headers
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
map
{
|
project
|
project
[
'id'
]
}).
to
contain_exactly
(
project
.
id
,
project2
.
id
,
project3
.
id
)
expect
(
json_response
.
map
{
|
project
|
project
[
'id'
]
})
.
to
contain_exactly
(
project
.
id
,
project2
.
id
,
project3
.
id
)
end
end
context
'with a private profile'
do
before
do
user3
.
update!
(
private_profile:
true
)
user3
.
reload
end
context
'user does not have access to view the private profile'
do
it
'returns no projects'
do
get
api
(
"/users/
#{
user3
.
id
}
/starred_projects/"
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
).
to
be_empty
end
end
context
'user has access to view the private profile'
do
it
'returns projects filtered by user'
do
get
api
(
"/users/
#{
user3
.
id
}
/starred_projects/"
,
admin
)
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
include_pagination_headers
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
map
{
|
project
|
project
[
'id'
]
})
.
to
contain_exactly
(
project
.
id
,
project2
.
id
,
project3
.
id
)
end
end
end
end
end
end
...
...
spec/validators/zoom_url_validator_spec.rb
0 → 100644
View file @
52cfce63
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
ZoomUrlValidator
do
let
(
:zoom_meeting
)
{
build
(
:zoom_meeting
)
}
describe
'validations'
do
context
'when zoom link starts with https'
do
it
'passes validation'
do
zoom_meeting
.
url
=
'https://zoom.us/j/123456789'
expect
(
zoom_meeting
.
valid?
).
to
eq
(
true
)
expect
(
zoom_meeting
.
errors
).
to
be_empty
end
end
shared_examples
'zoom link does not start with https'
do
|
url
|
it
'fails validation'
do
zoom_meeting
.
url
=
url
expect
(
zoom_meeting
.
valid?
).
to
eq
(
false
)
expect
(
zoom_meeting
.
errors
).
to
be_present
expect
(
zoom_meeting
.
errors
.
first
[
1
]).
to
eq
'must contain one valid Zoom URL'
end
end
context
'when zoom link does not start with https'
do
include_examples
'zoom link does not start with https'
,
'http://zoom.us/j/123456789'
context
'when zoom link does not start with a scheme'
do
include_examples
'zoom link does not start with https'
,
'testinghttp://zoom.us/j/123456789'
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment