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
b86a9b14
Commit
b86a9b14
authored
Oct 30, 2019
by
Imre Farkas
Committed by
Yorick Peterse
Oct 30, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Share groups with groups (BE)
It's implemented in a similar fashion as sharing a project with a group.
parent
d455f3ba
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
740 additions
and
33 deletions
+740
-33
app/controllers/groups/group_links_controller.rb
app/controllers/groups/group_links_controller.rb
+34
-0
app/models/group.rb
app/models/group.rb
+30
-5
app/models/group_group_link.rb
app/models/group_group_link.rb
+23
-0
app/services/groups/group_links/create_service.rb
app/services/groups/group_links/create_service.rb
+29
-0
changelogs/unreleased/33054-share_groups_with_groups.yml
changelogs/unreleased/33054-share_groups_with_groups.yml
+5
-0
config/routes/group.rb
config/routes/group.rb
+2
-0
db/migrate/20180902070406_create_group_group_links.rb
db/migrate/20180902070406_create_group_group_links.rb
+32
-0
db/schema.rb
db/schema.rb
+13
-0
lib/gitlab/project_authorizations.rb
lib/gitlab/project_authorizations.rb
+27
-3
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/controllers/groups/group_links_controller_spec.rb
spec/controllers/groups/group_links_controller_spec.rb
+114
-0
spec/factories/group_group_links.rb
spec/factories/group_group_links.rb
+9
-0
spec/lib/gitlab/project_authorizations_spec.rb
spec/lib/gitlab/project_authorizations_spec.rb
+142
-25
spec/models/group_group_link_spec.rb
spec/models/group_group_link_spec.rb
+36
-0
spec/models/group_spec.rb
spec/models/group_spec.rb
+122
-0
spec/services/groups/group_links/create_service_spec.rb
spec/services/groups/group_links/create_service_spec.rb
+119
-0
No files found.
app/controllers/groups/group_links_controller.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
class
Groups::GroupLinksController
<
Groups
::
ApplicationController
before_action
:check_feature_flag!
before_action
:authorize_admin_group!
def
create
shared_with_group
=
Group
.
find
(
params
[
:shared_with_group_id
])
if
params
[
:shared_with_group_id
].
present?
if
shared_with_group
result
=
Groups
::
GroupLinks
::
CreateService
.
new
(
shared_with_group
,
current_user
,
group_link_create_params
)
.
execute
(
group
)
return
render_404
if
result
[
:http_status
]
==
404
flash
[
:alert
]
=
result
[
:message
]
if
result
[
:status
]
==
:error
else
flash
[
:alert
]
=
_
(
'Please select a group.'
)
end
redirect_to
group_group_members_path
(
group
)
end
private
def
group_link_create_params
params
.
permit
(
:shared_group_access
,
:expires_at
)
end
def
check_feature_flag!
render_404
unless
Feature
.
enabled?
(
:share_group_with_group
)
end
end
app/models/group.rb
View file @
b86a9b14
...
@@ -30,6 +30,10 @@ class Group < Namespace
...
@@ -30,6 +30,10 @@ class Group < Namespace
has_many
:members_and_requesters
,
as: :source
,
class_name:
'GroupMember'
has_many
:members_and_requesters
,
as: :source
,
class_name:
'GroupMember'
has_many
:milestones
has_many
:milestones
has_many
:shared_group_links
,
foreign_key: :shared_with_group_id
,
class_name:
'GroupGroupLink'
has_many
:shared_with_group_links
,
foreign_key: :shared_group_id
,
class_name:
'GroupGroupLink'
has_many
:shared_groups
,
through: :shared_group_links
,
source: :shared_group
has_many
:shared_with_groups
,
through: :shared_with_group_links
,
source: :shared_with_group
has_many
:project_group_links
,
dependent: :destroy
# rubocop:disable Cop/ActiveRecordDependent
has_many
:project_group_links
,
dependent: :destroy
# rubocop:disable Cop/ActiveRecordDependent
has_many
:shared_projects
,
through: :project_group_links
,
source: :project
has_many
:shared_projects
,
through: :project_group_links
,
source: :project
...
@@ -376,11 +380,12 @@ class Group < Namespace
...
@@ -376,11 +380,12 @@ class Group < Namespace
return
GroupMember
::
OWNER
if
user
.
admin?
return
GroupMember
::
OWNER
if
user
.
admin?
members_with_parents
max_member_access
=
members_with_parents
.
where
(
user_id:
user
)
.
where
(
user_id:
user
)
.
reorder
(
access_level: :desc
)
.
reorder
(
access_level: :desc
)
.
first
.
first
&
.
&
.
access_level
access_level
||
GroupMember
::
NO_ACCESS
max_member_access
||
max_member_access_for_user_from_shared_groups
(
user
)
||
GroupMember
::
NO_ACCESS
end
end
def
mattermost_team_params
def
mattermost_team_params
...
@@ -474,6 +479,26 @@ class Group < Namespace
...
@@ -474,6 +479,26 @@ class Group < Namespace
errors
.
add
(
:visibility_level
,
"
#{
visibility
}
is not allowed since there are sub-groups with higher visibility."
)
errors
.
add
(
:visibility_level
,
"
#{
visibility
}
is not allowed since there are sub-groups with higher visibility."
)
end
end
def
max_member_access_for_user_from_shared_groups
(
user
)
return
unless
Feature
.
enabled?
(
:share_group_with_group
)
group_group_link_table
=
GroupGroupLink
.
arel_table
group_member_table
=
GroupMember
.
arel_table
group_group_links_query
=
GroupGroupLink
.
where
(
shared_group_id:
self_and_ancestors_ids
)
cte
=
Gitlab
::
SQL
::
CTE
.
new
(
:group_group_links_cte
,
group_group_links_query
)
link
=
GroupGroupLink
.
with
(
cte
.
to_arel
)
.
from
([
group_member_table
,
cte
.
alias_to
(
group_group_link_table
)])
.
where
(
group_member_table
[
:user_id
].
eq
(
user
.
id
))
.
where
(
group_member_table
[
:source_id
].
eq
(
group_group_link_table
[
:shared_with_group_id
]))
.
reorder
(
Arel
::
Nodes
::
Descending
.
new
(
group_group_link_table
[
:group_access
]))
.
first
link
&
.
group_access
end
def
self
.
groups_including_descendants_by
(
group_ids
)
def
self
.
groups_including_descendants_by
(
group_ids
)
Gitlab
::
ObjectHierarchy
Gitlab
::
ObjectHierarchy
.
new
(
Group
.
where
(
id:
group_ids
))
.
new
(
Group
.
where
(
id:
group_ids
))
...
...
app/models/group_group_link.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
class
GroupGroupLink
<
ApplicationRecord
include
Expirable
belongs_to
:shared_group
,
class_name:
'Group'
,
foreign_key: :shared_group_id
belongs_to
:shared_with_group
,
class_name:
'Group'
,
foreign_key: :shared_with_group_id
validates
:shared_group
,
presence:
true
validates
:shared_group_id
,
uniqueness:
{
scope:
[
:shared_with_group_id
],
message:
_
(
'The group has already been shared with this group'
)
}
validates
:shared_with_group
,
presence:
true
validates
:group_access
,
inclusion:
{
in:
Gitlab
::
Access
.
values
},
presence:
true
def
self
.
access_options
Gitlab
::
Access
.
options
end
def
self
.
default_access
Gitlab
::
Access
::
DEVELOPER
end
end
app/services/groups/group_links/create_service.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
module
Groups
module
GroupLinks
class
CreateService
<
BaseService
def
execute
(
shared_group
)
unless
group
&&
shared_group
&&
can?
(
current_user
,
:admin_group
,
shared_group
)
&&
can?
(
current_user
,
:read_group
,
group
)
return
error
(
'Not Found'
,
404
)
end
link
=
GroupGroupLink
.
new
(
shared_group:
shared_group
,
shared_with_group:
group
,
group_access:
params
[
:shared_group_access
],
expires_at:
params
[
:expires_at
]
)
if
link
.
save
group
.
refresh_members_authorized_projects
success
(
link:
link
)
else
error
(
link
.
errors
.
full_messages
.
to_sentence
,
409
)
end
end
end
end
end
changelogs/unreleased/33054-share_groups_with_groups.yml
0 → 100644
View file @
b86a9b14
---
title
:
Share groups with groups
merge_request
:
17117
author
:
type
:
added
config/routes/group.rb
View file @
b86a9b14
...
@@ -62,6 +62,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
...
@@ -62,6 +62,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
delete
:leave
,
on: :collection
delete
:leave
,
on: :collection
end
end
resources
:group_links
,
only:
[
:index
,
:create
,
:update
,
:destroy
],
constraints:
{
id:
/\d+/
}
resources
:uploads
,
only:
[
:create
]
do
resources
:uploads
,
only:
[
:create
]
do
collection
do
collection
do
get
":secret/:filename"
,
action: :show
,
as: :show
,
constraints:
{
filename:
%r{[^/]+}
}
get
":secret/:filename"
,
action: :show
,
as: :show
,
constraints:
{
filename:
%r{[^/]+}
}
...
...
db/migrate/20180902070406_create_group_group_links.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
class
CreateGroupGroupLinks
<
ActiveRecord
::
Migration
[
5.2
]
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
def
up
create_table
:group_group_links
do
|
t
|
t
.
timestamps_with_timezone
null:
false
t
.
references
:shared_group
,
null:
false
,
index:
false
,
foreign_key:
{
on_delete: :cascade
,
to_table: :namespaces
}
t
.
references
:shared_with_group
,
null:
false
,
foreign_key:
{
on_delete: :cascade
,
to_table: :namespaces
}
t
.
date
:expires_at
t
.
index
[
:shared_group_id
,
:shared_with_group_id
],
{
unique:
true
,
name:
'index_group_group_links_on_shared_group_and_shared_with_group'
}
t
.
integer
:group_access
,
{
limit:
2
,
default:
30
,
# Gitlab::Access::DEVELOPER
null:
false
}
end
end
def
down
drop_table
:group_group_links
end
end
db/schema.rb
View file @
b86a9b14
...
@@ -1820,6 +1820,17 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
...
@@ -1820,6 +1820,17 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
t
.
index
[
"key"
,
"value"
],
name:
"index_group_custom_attributes_on_key_and_value"
t
.
index
[
"key"
,
"value"
],
name:
"index_group_custom_attributes_on_key_and_value"
end
end
create_table
"group_group_links"
,
force: :cascade
do
|
t
|
t
.
datetime_with_timezone
"created_at"
,
null:
false
t
.
datetime_with_timezone
"updated_at"
,
null:
false
t
.
bigint
"shared_group_id"
,
null:
false
t
.
bigint
"shared_with_group_id"
,
null:
false
t
.
date
"expires_at"
t
.
integer
"group_access"
,
limit:
2
,
default:
30
,
null:
false
t
.
index
[
"shared_group_id"
,
"shared_with_group_id"
],
name:
"index_group_group_links_on_shared_group_and_shared_with_group"
,
unique:
true
t
.
index
[
"shared_with_group_id"
],
name:
"index_group_group_links_on_shared_with_group_id"
end
create_table
"historical_data"
,
id: :serial
,
force: :cascade
do
|
t
|
create_table
"historical_data"
,
id: :serial
,
force: :cascade
do
|
t
|
t
.
date
"date"
,
null:
false
t
.
date
"date"
,
null:
false
t
.
integer
"active_user_count"
t
.
integer
"active_user_count"
...
@@ -4220,6 +4231,8 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
...
@@ -4220,6 +4231,8 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
add_foreign_key
"gpg_signatures"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"gpg_signatures"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"grafana_integrations"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"grafana_integrations"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"group_custom_attributes"
,
"namespaces"
,
column:
"group_id"
,
on_delete: :cascade
add_foreign_key
"group_custom_attributes"
,
"namespaces"
,
column:
"group_id"
,
on_delete: :cascade
add_foreign_key
"group_group_links"
,
"namespaces"
,
column:
"shared_group_id"
,
on_delete: :cascade
add_foreign_key
"group_group_links"
,
"namespaces"
,
column:
"shared_with_group_id"
,
on_delete: :cascade
add_foreign_key
"identities"
,
"saml_providers"
,
name:
"fk_aade90f0fc"
,
on_delete: :cascade
add_foreign_key
"identities"
,
"saml_providers"
,
name:
"fk_aade90f0fc"
,
on_delete: :cascade
add_foreign_key
"import_export_uploads"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"import_export_uploads"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"index_statuses"
,
"projects"
,
name:
"fk_74b2492545"
,
on_delete: :cascade
add_foreign_key
"index_statuses"
,
"projects"
,
name:
"fk_74b2492545"
,
on_delete: :cascade
...
...
lib/gitlab/project_authorizations.rb
View file @
b86a9b14
...
@@ -57,7 +57,7 @@ module Gitlab
...
@@ -57,7 +57,7 @@ module Gitlab
private
private
# Builds a recursive CTE that gets all the groups the current user has
# Builds a recursive CTE that gets all the groups the current user has
# access to, including any nested groups.
# access to, including any nested groups
and any shared groups
.
def
recursive_cte
def
recursive_cte
cte
=
Gitlab
::
SQL
::
RecursiveCTE
.
new
(
:namespaces_cte
)
cte
=
Gitlab
::
SQL
::
RecursiveCTE
.
new
(
:namespaces_cte
)
members
=
Member
.
arel_table
members
=
Member
.
arel_table
...
@@ -68,20 +68,27 @@ module Gitlab
...
@@ -68,20 +68,27 @@ module Gitlab
.
select
([
namespaces
[
:id
],
members
[
:access_level
]])
.
select
([
namespaces
[
:id
],
members
[
:access_level
]])
.
except
(
:order
)
.
except
(
:order
)
if
Feature
.
enabled?
(
:share_group_with_group
)
# Namespaces shared with any of the group
cte
<<
Group
.
select
([
namespaces
[
:id
],
'group_group_links.group_access AS access_level'
])
.
joins
(
join_group_group_links
)
.
joins
(
join_members_on_group_group_links
)
end
# Sub groups of any groups the user is a member of.
# Sub groups of any groups the user is a member of.
cte
<<
Group
.
select
([
cte
<<
Group
.
select
([
namespaces
[
:id
],
namespaces
[
:id
],
greatest
(
members
[
:access_level
],
cte
.
table
[
:access_level
],
'access_level'
)
greatest
(
members
[
:access_level
],
cte
.
table
[
:access_level
],
'access_level'
)
])
])
.
joins
(
join_cte
(
cte
))
.
joins
(
join_cte
(
cte
))
.
joins
(
join_members
)
.
joins
(
join_members
_on_namespaces
)
.
except
(
:order
)
.
except
(
:order
)
cte
cte
end
end
# Builds a LEFT JOIN to join optional memberships onto the CTE.
# Builds a LEFT JOIN to join optional memberships onto the CTE.
def
join_members
def
join_members
_on_namespaces
members
=
Member
.
arel_table
members
=
Member
.
arel_table
namespaces
=
Namespace
.
arel_table
namespaces
=
Namespace
.
arel_table
...
@@ -94,6 +101,23 @@ module Gitlab
...
@@ -94,6 +101,23 @@ module Gitlab
Arel
::
Nodes
::
OuterJoin
.
new
(
members
,
Arel
::
Nodes
::
On
.
new
(
cond
))
Arel
::
Nodes
::
OuterJoin
.
new
(
members
,
Arel
::
Nodes
::
On
.
new
(
cond
))
end
end
def
join_group_group_links
group_group_links
=
GroupGroupLink
.
arel_table
namespaces
=
Namespace
.
arel_table
cond
=
group_group_links
[
:shared_group_id
].
eq
(
namespaces
[
:id
])
Arel
::
Nodes
::
InnerJoin
.
new
(
group_group_links
,
Arel
::
Nodes
::
On
.
new
(
cond
))
end
def
join_members_on_group_group_links
group_group_links
=
GroupGroupLink
.
arel_table
members
=
Member
.
arel_table
cond
=
group_group_links
[
:shared_with_group_id
].
eq
(
members
[
:source_id
])
.
and
(
members
[
:user_id
].
eq
(
user
.
id
))
Arel
::
Nodes
::
InnerJoin
.
new
(
members
,
Arel
::
Nodes
::
On
.
new
(
cond
))
end
# Builds an INNER JOIN to join namespaces onto the CTE.
# Builds an INNER JOIN to join namespaces onto the CTE.
def
join_cte
(
cte
)
def
join_cte
(
cte
)
namespaces
=
Namespace
.
arel_table
namespaces
=
Namespace
.
arel_table
...
...
locale/gitlab.pot
View file @
b86a9b14
...
@@ -16437,6 +16437,9 @@ msgstr ""
...
@@ -16437,6 +16437,9 @@ msgstr ""
msgid "The group and its projects can only be viewed by members."
msgid "The group and its projects can only be viewed by members."
msgstr ""
msgstr ""
msgid "The group has already been shared with this group"
msgstr ""
msgid "The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}."
msgid "The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}."
msgstr ""
msgstr ""
...
...
spec/controllers/groups/group_links_controller_spec.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
require
'spec_helper'
describe
Groups
::
GroupLinksController
do
let
(
:shared_with_group
)
{
create
(
:group
,
:private
)
}
let
(
:shared_group
)
{
create
(
:group
,
:private
)
}
let
(
:user
)
{
create
(
:user
)
}
before
do
sign_in
(
user
)
end
describe
'#create'
do
let
(
:shared_with_group_id
)
{
shared_with_group
.
id
}
subject
do
post
(
:create
,
params:
{
group_id:
shared_group
,
shared_with_group_id:
shared_with_group_id
,
shared_group_access:
GroupGroupLink
.
default_access
})
end
context
'when user has correct access to both groups'
do
let
(
:group_member
)
{
create
(
:user
)
}
before
do
shared_with_group
.
add_developer
(
user
)
shared_group
.
add_owner
(
user
)
shared_with_group
.
add_developer
(
group_member
)
end
it
'links group with selected group'
do
expect
{
subject
}.
to
change
{
shared_with_group
.
shared_groups
.
include?
(
shared_group
)
}.
from
(
false
).
to
(
true
)
end
it
'redirects to group links page'
do
subject
expect
(
response
).
to
(
redirect_to
(
group_group_members_path
(
shared_group
)))
end
it
'allows access for group member'
do
expect
{
subject
}.
to
change
{
group_member
.
can?
(
:read_group
,
shared_group
)
}.
from
(
false
).
to
(
true
)
end
context
'when shared with group id is not present'
do
let
(
:shared_with_group_id
)
{
nil
}
it
'redirects to group links page'
do
subject
expect
(
response
).
to
(
redirect_to
(
group_group_members_path
(
shared_group
)))
expect
(
flash
[
:alert
]).
to
eq
(
'Please select a group.'
)
end
end
context
'when link is not persisted in the database'
do
before
do
allow
(
::
Groups
::
GroupLinks
::
CreateService
).
to
(
receive_message_chain
(
:new
,
:execute
)
.
and_return
({
status: :error
,
http_status:
409
,
message:
'error'
}))
end
it
'redirects to group links page'
do
subject
expect
(
response
).
to
(
redirect_to
(
group_group_members_path
(
shared_group
)))
expect
(
flash
[
:alert
]).
to
eq
(
'error'
)
end
end
context
'when feature flag is disabled'
do
before
do
stub_feature_flags
(
share_group_with_group:
false
)
end
it
'renders 404'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
end
end
context
'when user does not have access to the group'
do
before
do
shared_group
.
add_owner
(
user
)
end
it
'renders 404'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
end
context
'when user does not have admin access to the shared group'
do
before
do
shared_with_group
.
add_developer
(
user
)
shared_group
.
add_developer
(
user
)
end
it
'renders 404'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
end
end
end
spec/factories/group_group_links.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
FactoryBot
.
define
do
factory
:group_group_link
do
shared_group
{
create
(
:group
)
}
shared_with_group
{
create
(
:group
)
}
group_access
{
GroupMember
::
DEVELOPER
}
end
end
spec/lib/gitlab/project_authorizations_spec.rb
View file @
b86a9b14
...
@@ -3,48 +3,55 @@
...
@@ -3,48 +3,55 @@
require
'spec_helper'
require
'spec_helper'
describe
Gitlab
::
ProjectAuthorizations
do
describe
Gitlab
::
ProjectAuthorizations
do
let
(
:group
)
{
create
(
:group
)
}
let!
(
:owned_project
)
{
create
(
:project
)
}
let!
(
:other_project
)
{
create
(
:project
)
}
let!
(
:group_project
)
{
create
(
:project
,
namespace:
group
)
}
let
(
:user
)
{
owned_project
.
namespace
.
owner
}
def
map_access_levels
(
rows
)
def
map_access_levels
(
rows
)
rows
.
each_with_object
({})
do
|
row
,
hash
|
rows
.
each_with_object
({})
do
|
row
,
hash
|
hash
[
row
.
project_id
]
=
row
.
access_level
hash
[
row
.
project_id
]
=
row
.
access_level
end
end
end
end
before
do
subject
(
:authorizations
)
do
other_project
.
add_reporter
(
user
)
group
.
add_developer
(
user
)
end
let
(
:authorizations
)
do
described_class
.
new
(
user
).
calculate
described_class
.
new
(
user
).
calculate
end
end
it
'returns the correct number of authorizations'
do
context
'user added to group and project'
do
expect
(
authorizations
.
length
).
to
eq
(
3
)
let
(
:group
)
{
create
(
:group
)
}
end
let!
(
:other_project
)
{
create
(
:project
)
}
let!
(
:group_project
)
{
create
(
:project
,
namespace:
group
)
}
let!
(
:owned_project
)
{
create
(
:project
)
}
let
(
:user
)
{
owned_project
.
namespace
.
owner
}
it
'includes the correct projects'
do
before
do
expect
(
authorizations
.
pluck
(
:project_id
))
other_project
.
add_reporter
(
user
)
.
to
include
(
owned_project
.
id
,
other_project
.
id
,
group_project
.
id
)
group
.
add_developer
(
user
)
end
end
it
'returns the correct number of authorizations'
do
expect
(
authorizations
.
length
).
to
eq
(
3
)
end
it
'includes the correct access levels'
do
it
'includes the correct projects'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
authorizations
.
pluck
(
:project_id
))
.
to
include
(
owned_project
.
id
,
other_project
.
id
,
group_project
.
id
)
end
it
'includes the correct access levels'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
owned_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
MAINTAINER
)
expect
(
mapping
[
owned_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
MAINTAINER
)
expect
(
mapping
[
other_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
REPORTER
)
expect
(
mapping
[
other_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
REPORTER
)
expect
(
mapping
[
group_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
DEVELOPER
)
expect
(
mapping
[
group_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
DEVELOPER
)
end
end
end
context
'with nested groups'
do
context
'with nested groups'
do
let
(
:group
)
{
create
(
:group
)
}
let!
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let!
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let!
(
:nested_project
)
{
create
(
:project
,
namespace:
nested_group
)
}
let!
(
:nested_project
)
{
create
(
:project
,
namespace:
nested_group
)
}
let
(
:user
)
{
create
(
:user
)
}
before
do
group
.
add_developer
(
user
)
end
it
'includes nested groups'
do
it
'includes nested groups'
do
expect
(
authorizations
.
pluck
(
:project_id
)).
to
include
(
nested_project
.
id
)
expect
(
authorizations
.
pluck
(
:project_id
)).
to
include
(
nested_project
.
id
)
...
@@ -64,4 +71,114 @@ describe Gitlab::ProjectAuthorizations do
...
@@ -64,4 +71,114 @@ describe Gitlab::ProjectAuthorizations do
expect
(
mapping
[
nested_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
MAINTAINER
)
expect
(
mapping
[
nested_project
.
id
]).
to
eq
(
Gitlab
::
Access
::
MAINTAINER
)
end
end
end
end
context
'with shared groups'
do
let
(
:parent_group_user
)
{
create
(
:user
)
}
let
(
:group_user
)
{
create
(
:user
)
}
let
(
:child_group_user
)
{
create
(
:user
)
}
set
(
:group_parent
)
{
create
(
:group
,
:private
)
}
set
(
:group
)
{
create
(
:group
,
:private
,
parent:
group_parent
)
}
set
(
:group_child
)
{
create
(
:group
,
:private
,
parent:
group
)
}
set
(
:shared_group_parent
)
{
create
(
:group
,
:private
)
}
set
(
:shared_group
)
{
create
(
:group
,
:private
,
parent:
shared_group_parent
)
}
set
(
:shared_group_child
)
{
create
(
:group
,
:private
,
parent:
shared_group
)
}
set
(
:project_parent
)
{
create
(
:project
,
group:
shared_group_parent
)
}
set
(
:project
)
{
create
(
:project
,
group:
shared_group
)
}
set
(
:project_child
)
{
create
(
:project
,
group:
shared_group_child
)
}
before
do
group_parent
.
add_owner
(
parent_group_user
)
group
.
add_owner
(
group_user
)
group_child
.
add_owner
(
child_group_user
)
create
(
:group_group_link
,
shared_group:
shared_group
,
shared_with_group:
group
)
end
context
'when feature flag share_group_with_group is enabled'
do
before
do
stub_feature_flags
(
share_group_with_group:
true
)
end
context
'group user'
do
let
(
:user
)
{
group_user
}
it
'creates proper authorizations'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
project_parent
.
id
]).
to
be_nil
expect
(
mapping
[
project
.
id
]).
to
eq
(
Gitlab
::
Access
::
DEVELOPER
)
expect
(
mapping
[
project_child
.
id
]).
to
eq
(
Gitlab
::
Access
::
DEVELOPER
)
end
end
context
'parent group user'
do
let
(
:user
)
{
parent_group_user
}
it
'creates proper authorizations'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
project_parent
.
id
]).
to
be_nil
expect
(
mapping
[
project
.
id
]).
to
be_nil
expect
(
mapping
[
project_child
.
id
]).
to
be_nil
end
end
context
'child group user'
do
let
(
:user
)
{
child_group_user
}
it
'creates proper authorizations'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
project_parent
.
id
]).
to
be_nil
expect
(
mapping
[
project
.
id
]).
to
be_nil
expect
(
mapping
[
project_child
.
id
]).
to
be_nil
end
end
end
context
'when feature flag share_group_with_group is disabled'
do
before
do
stub_feature_flags
(
share_group_with_group:
false
)
end
context
'group user'
do
let
(
:user
)
{
group_user
}
it
'creates proper authorizations'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
project_parent
.
id
]).
to
be_nil
expect
(
mapping
[
project
.
id
]).
to
be_nil
expect
(
mapping
[
project_child
.
id
]).
to
be_nil
end
end
context
'parent group user'
do
let
(
:user
)
{
parent_group_user
}
it
'creates proper authorizations'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
project_parent
.
id
]).
to
be_nil
expect
(
mapping
[
project
.
id
]).
to
be_nil
expect
(
mapping
[
project_child
.
id
]).
to
be_nil
end
end
context
'child group user'
do
let
(
:user
)
{
child_group_user
}
it
'creates proper authorizations'
do
mapping
=
map_access_levels
(
authorizations
)
expect
(
mapping
[
project_parent
.
id
]).
to
be_nil
expect
(
mapping
[
project
.
id
]).
to
be_nil
expect
(
mapping
[
project_child
.
id
]).
to
be_nil
end
end
end
end
end
end
spec/models/group_group_link_spec.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
require
'spec_helper'
describe
GroupGroupLink
do
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:shared_group
)
{
create
(
:group
)
}
let_it_be
(
:group_group_link
)
do
create
(
:group_group_link
,
shared_group:
shared_group
,
shared_with_group:
group
)
end
describe
'relations'
do
it
{
is_expected
.
to
belong_to
(
:shared_group
)
}
it
{
is_expected
.
to
belong_to
(
:shared_with_group
)
}
end
describe
'validation'
do
it
{
is_expected
.
to
validate_presence_of
(
:shared_group
)
}
it
do
is_expected
.
to
(
validate_uniqueness_of
(
:shared_group_id
)
.
scoped_to
(
:shared_with_group_id
)
.
with_message
(
'The group has already been shared with this group'
))
end
it
{
is_expected
.
to
validate_presence_of
(
:shared_with_group
)
}
it
{
is_expected
.
to
validate_presence_of
(
:group_access
)
}
it
do
is_expected
.
to
(
validate_inclusion_of
(
:group_access
).
in_array
(
Gitlab
::
Access
.
values
))
end
end
end
spec/models/group_spec.rb
View file @
b86a9b14
...
@@ -525,6 +525,128 @@ describe Group do
...
@@ -525,6 +525,128 @@ describe Group do
it
{
expect
(
subject
.
parent
).
to
be_kind_of
(
described_class
)
}
it
{
expect
(
subject
.
parent
).
to
be_kind_of
(
described_class
)
}
end
end
describe
'#max_member_access_for_user'
do
context
'group shared with another group'
do
let
(
:parent_group_user
)
{
create
(
:user
)
}
let
(
:group_user
)
{
create
(
:user
)
}
let
(
:child_group_user
)
{
create
(
:user
)
}
set
(
:group_parent
)
{
create
(
:group
,
:private
)
}
set
(
:group
)
{
create
(
:group
,
:private
,
parent:
group_parent
)
}
set
(
:group_child
)
{
create
(
:group
,
:private
,
parent:
group
)
}
set
(
:shared_group_parent
)
{
create
(
:group
,
:private
)
}
set
(
:shared_group
)
{
create
(
:group
,
:private
,
parent:
shared_group_parent
)
}
set
(
:shared_group_child
)
{
create
(
:group
,
:private
,
parent:
shared_group
)
}
before
do
group_parent
.
add_owner
(
parent_group_user
)
group
.
add_owner
(
group_user
)
group_child
.
add_owner
(
child_group_user
)
create
(
:group_group_link
,
{
shared_with_group:
group
,
shared_group:
shared_group
,
group_access:
GroupMember
::
DEVELOPER
})
end
context
'when feature flag share_group_with_group is enabled'
do
before
do
stub_feature_flags
(
share_group_with_group:
true
)
end
context
'with user in the group'
do
let
(
:user
)
{
group_user
}
it
'returns correct access level'
do
expect
(
shared_group_parent
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
DEVELOPER
)
expect
(
shared_group_child
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
DEVELOPER
)
end
end
context
'with user in the parent group'
do
let
(
:user
)
{
parent_group_user
}
it
'returns correct access level'
do
expect
(
shared_group_parent
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group_child
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
end
end
context
'with user in the child group'
do
let
(
:user
)
{
child_group_user
}
it
'returns correct access level'
do
expect
(
shared_group_parent
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group_child
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
end
end
end
context
'when feature flag share_group_with_group is disabled'
do
before
do
stub_feature_flags
(
share_group_with_group:
false
)
end
context
'with user in the group'
do
let
(
:user
)
{
group_user
}
it
'returns correct access level'
do
expect
(
shared_group_parent
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group_child
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
end
end
context
'with user in the parent group'
do
let
(
:user
)
{
parent_group_user
}
it
'returns correct access level'
do
expect
(
shared_group_parent
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group_child
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
end
end
context
'with user in the child group'
do
let
(
:user
)
{
child_group_user
}
it
'returns correct access level'
do
expect
(
shared_group_parent
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
expect
(
shared_group_child
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
NO_ACCESS
)
end
end
end
end
context
'multiple groups shared with group'
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
,
:private
)
}
let
(
:shared_group_parent
)
{
create
(
:group
,
:private
)
}
let
(
:shared_group
)
{
create
(
:group
,
:private
,
parent:
shared_group_parent
)
}
before
do
stub_feature_flags
(
share_group_with_group:
true
)
group
.
add_owner
(
user
)
create
(
:group_group_link
,
{
shared_with_group:
group
,
shared_group:
shared_group
,
group_access:
GroupMember
::
DEVELOPER
})
create
(
:group_group_link
,
{
shared_with_group:
group
,
shared_group:
shared_group_parent
,
group_access:
GroupMember
::
MAINTAINER
})
end
it
'returns correct access level'
do
expect
(
shared_group
.
max_member_access_for_user
(
user
)).
to
eq
(
Gitlab
::
Access
::
MAINTAINER
)
end
end
end
describe
'#members_with_parents'
do
describe
'#members_with_parents'
do
let!
(
:group
)
{
create
(
:group
,
:nested
)
}
let!
(
:group
)
{
create
(
:group
,
:nested
)
}
let!
(
:maintainer
)
{
group
.
parent
.
add_user
(
create
(
:user
),
GroupMember
::
MAINTAINER
)
}
let!
(
:maintainer
)
{
group
.
parent
.
add_user
(
create
(
:user
),
GroupMember
::
MAINTAINER
)
}
...
...
spec/services/groups/group_links/create_service_spec.rb
0 → 100644
View file @
b86a9b14
# frozen_string_literal: true
require
'spec_helper'
describe
Groups
::
GroupLinks
::
CreateService
,
'#execute'
do
let
(
:parent_group_user
)
{
create
(
:user
)
}
let
(
:group_user
)
{
create
(
:user
)
}
let
(
:child_group_user
)
{
create
(
:user
)
}
set
(
:group_parent
)
{
create
(
:group
,
:private
)
}
set
(
:group
)
{
create
(
:group
,
:private
,
parent:
group_parent
)
}
set
(
:group_child
)
{
create
(
:group
,
:private
,
parent:
group
)
}
set
(
:shared_group_parent
)
{
create
(
:group
,
:private
)
}
set
(
:shared_group
)
{
create
(
:group
,
:private
,
parent:
shared_group_parent
)
}
set
(
:shared_group_child
)
{
create
(
:group
,
:private
,
parent:
shared_group
)
}
set
(
:project_parent
)
{
create
(
:project
,
group:
shared_group_parent
)
}
set
(
:project
)
{
create
(
:project
,
group:
shared_group
)
}
set
(
:project_child
)
{
create
(
:project
,
group:
shared_group_child
)
}
let
(
:opts
)
do
{
shared_group_access:
Gitlab
::
Access
::
DEVELOPER
,
expires_at:
nil
}
end
let
(
:user
)
{
group_user
}
subject
{
described_class
.
new
(
group
,
user
,
opts
)
}
before
do
group
.
add_guest
(
group_user
)
shared_group
.
add_owner
(
group_user
)
end
it
'adds group to another group'
do
expect
{
subject
.
execute
(
shared_group
)
}.
to
change
{
group
.
shared_group_links
.
count
}.
from
(
0
).
to
(
1
)
end
it
'returns false if shared group is blank'
do
expect
{
subject
.
execute
(
nil
)
}.
not_to
change
{
group
.
shared_group_links
.
count
}
end
context
'user does not have access to group'
do
let
(
:user
)
{
create
(
:user
)
}
before
do
shared_group
.
add_owner
(
user
)
end
it
'returns error'
do
result
=
subject
.
execute
(
shared_group
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:http_status
]).
to
eq
(
404
)
end
end
context
'user does not have admin access to shared group'
do
let
(
:user
)
{
create
(
:user
)
}
before
do
group
.
add_guest
(
user
)
shared_group
.
add_developer
(
user
)
end
it
'returns error'
do
result
=
subject
.
execute
(
shared_group
)
expect
(
result
[
:status
]).
to
eq
(
:error
)
expect
(
result
[
:http_status
]).
to
eq
(
404
)
end
end
context
'group hierarchies'
do
before
do
group_parent
.
add_owner
(
parent_group_user
)
group
.
add_owner
(
group_user
)
group_child
.
add_owner
(
child_group_user
)
end
context
'group user'
do
let
(
:user
)
{
group_user
}
it
'create proper authorizations'
do
subject
.
execute
(
shared_group
)
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project_parent
)).
to
be_falsey
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project
)).
to
be_truthy
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project_child
)).
to
be_truthy
end
end
context
'parent group user'
do
let
(
:user
)
{
parent_group_user
}
it
'create proper authorizations'
do
subject
.
execute
(
shared_group
)
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project_parent
)).
to
be_falsey
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project
)).
to
be_falsey
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project_child
)).
to
be_falsey
end
end
context
'child group user'
do
let
(
:user
)
{
child_group_user
}
it
'create proper authorizations'
do
subject
.
execute
(
shared_group
)
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project_parent
)).
to
be_falsey
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project
)).
to
be_falsey
expect
(
Ability
.
allowed?
(
user
,
:read_project
,
project_child
)).
to
be_falsey
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