1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
# frozen_string_literal: true
module Groups
class AcceptingProjectCreationsFinder
def initialize(current_user)
@current_user = current_user
end
def execute
groups_accepting_project_creations =
[
current_user
.manageable_groups(include_groups_with_developer_maintainer_access: true)
.project_creation_allowed,
owner_maintainer_groups_originating_from_group_shares
.project_creation_allowed,
*developer_groups_originating_from_group_shares
]
# We move the UNION query into a materialized CTE to improve query performance during text search.
union_query = ::Group.from_union(groups_accepting_project_creations)
cte = Gitlab::SQL::CTE.new(:my_union_cte, union_query)
Group.with(cte.to_arel).from(cte.alias_to(Group.arel_table)) # rubocop: disable CodeReuse/ActiveRecord
end
private
attr_reader :current_user
def owner_maintainer_groups_originating_from_group_shares
GroupGroupLink
.with_owner_or_maintainer_access
.groups_accessible_via(
groups_that_user_has_owner_or_maintainer_access_via_direct_membership
.select(:id)
)
end
def groups_that_user_has_owner_or_maintainer_access_via_direct_membership
current_user.owned_or_maintainers_groups
end
def developer_groups_originating_from_group_shares
# Example:
#
# Group A -----shared to---> Group B
#
# Now, there are 2 ways a user in Group A can get "Developer" access to Group B (and it's subgroups)
[
# 1. User has Developer or above access in Group A,
# but the group_group_link has MAX access level set to Developer
GroupGroupLink
.with_developer_access
.groups_accessible_via(
groups_that_user_has_developer_access_and_above_via_direct_membership
.select(:id)
).with_project_creation_levels(project_creations_levels_allowing_developers_to_create_projects),
# 2. User has exactly Developer access in Group A,
# but the group_group_link has MAX access level set to Developer or above.
GroupGroupLink
.with_developer_maintainer_owner_access
.groups_accessible_via(
groups_that_user_has_developer_access_via_direct_membership
.select(:id)
).with_project_creation_levels(project_creations_levels_allowing_developers_to_create_projects)
]
# Lastly, we should make sure that such groups indeed allow Developers to create projects in them,
# based on the value of `groups.project_creation_level`,
# which is why we use the scope .with_project_creation_levels on each set.
end
def groups_that_user_has_developer_access_and_above_via_direct_membership
current_user.developer_maintainer_owned_groups
end
def groups_that_user_has_developer_access_via_direct_membership
current_user.developer_groups
end
def project_creations_levels_allowing_developers_to_create_projects
project_creation_levels = [::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS]
# When the value of application_settings.default_project_creation is set to `DEVELOPER_MAINTAINER_PROJECT_ACCESS`,
# it means that a `nil` value for `groups.project_creation_level` is telling us:
# such groups also have `project_creation_level` implicitly set to `DEVELOPER_MAINTAINER_PROJECT_ACCESS`.
# ie, `nil` is a placeholder value for inheriting the value from the ApplicationSetting.
# So we will include `nil` in the list,
# when the application_setting's value is `DEVELOPER_MAINTAINER_PROJECT_ACCESS`
if ::Gitlab::CurrentSettings.default_project_creation == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
project_creation_levels << nil
end
project_creation_levels
end
end
end
|