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
0a1a4f0a
Commit
0a1a4f0a
authored
Jul 08, 2016
by
Drew Blessing
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Isolate EE LDAP library code in EE module (Part 1)
parent
b9f09018
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
628 additions
and
594 deletions
+628
-594
CHANGELOG-EE
CHANGELOG-EE
+1
-0
app/workers/ldap_group_sync_worker.rb
app/workers/ldap_group_sync_worker.rb
+1
-1
lib/ee/gitlab/ldap/access_levels.rb
lib/ee/gitlab/ldap/access_levels.rb
+19
-0
lib/ee/gitlab/ldap/adapter.rb
lib/ee/gitlab/ldap/adapter.rb
+44
-0
lib/ee/gitlab/ldap/group.rb
lib/ee/gitlab/ldap/group.rb
+80
-0
lib/ee/gitlab/ldap/group_sync.rb
lib/ee/gitlab/ldap/group_sync.rb
+393
-0
lib/gitlab/ldap/access_levels.rb
lib/gitlab/ldap/access_levels.rb
+0
-17
lib/gitlab/ldap/adapter.rb
lib/gitlab/ldap/adapter.rb
+6
-31
lib/gitlab/ldap/group.rb
lib/gitlab/ldap/group.rb
+0
-78
lib/gitlab/ldap/group_sync.rb
lib/gitlab/ldap/group_sync.rb
+0
-391
spec/lib/ee/gitlab/ldap/access_levels_spec.rb
spec/lib/ee/gitlab/ldap/access_levels_spec.rb
+2
-2
spec/lib/ee/gitlab/ldap/adapter_spec.rb
spec/lib/ee/gitlab/ldap/adapter_spec.rb
+9
-0
spec/lib/ee/gitlab/ldap/group_spec.rb
spec/lib/ee/gitlab/ldap/group_spec.rb
+1
-1
spec/lib/ee/gitlab/ldap/group_sync_spec.rb
spec/lib/ee/gitlab/ldap/group_sync_spec.rb
+72
-73
No files found.
CHANGELOG-EE
View file @
0a1a4f0a
...
...
@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased)
- Rename Git Hooks to Push Rules
- Fix EE keys fingerprint add index migration if came from CE
- Isolate EE LDAP library code in EE module (Part 1) !511
v 8.9.5
- Fix of quoted text in lock tooltip. !518
...
...
app/workers/ldap_group_sync_worker.rb
View file @
0a1a4f0a
...
...
@@ -5,7 +5,7 @@ class LdapGroupSyncWorker
def
perform
logger
.
info
'Started LDAP group sync'
Gitlab
::
LDAP
::
GroupSync
.
execute
EE
::
Gitlab
::
LDAP
::
GroupSync
.
execute
logger
.
info
'Finished LDAP group sync'
end
end
lib/ee/gitlab/ldap/access_levels.rb
0 → 100644
View file @
0a1a4f0a
module
EE
module
Gitlab
module
LDAP
# Create a hash map of member DNs to access levels. The highest
# access level is retained in cases where `set` is called multiple times
# for the same DN.
class
AccessLevels
<
Hash
def
set
(
dns
,
to
:)
dns
.
each
do
|
dn
|
current
=
self
[
dn
]
# Keep the higher of the access values.
self
[
dn
]
=
to
if
current
.
nil?
||
to
>
current
end
end
end
end
end
end
lib/ee/gitlab/ldap/adapter.rb
0 → 100644
View file @
0a1a4f0a
# LDAP connection adapter EE mixin
#
# This module is intended to encapsulate EE-specific adapter methods
# and be included in the `Gitlab::LDAP::Adapter` class.
module
EE
module
Gitlab
module
LDAP
module
Adapter
# Get LDAP groups from ou=Groups
#
# cn - filter groups by name
#
# Ex.
# groups("dev*") # return all groups start with 'dev'
#
def
groups
(
cn
=
"*"
,
size
=
nil
)
options
=
{
base:
config
.
group_base
,
filter:
Net
::
LDAP
::
Filter
.
eq
(
"cn"
,
cn
)
}
options
.
merge!
(
size:
size
)
if
size
ldap_search
(
options
).
map
do
|
entry
|
Group
.
new
(
entry
,
self
)
end
end
def
group
(
*
args
)
groups
(
*
args
).
first
end
def
dn_matches_filter?
(
dn
,
filter
)
ldap_search
(
base:
dn
,
filter:
filter
,
scope:
Net
::
LDAP
::
SearchScope_BaseObject
,
attributes:
%w{dn}
).
any?
end
end
end
end
end
lib/ee/gitlab/ldap/group.rb
0 → 100644
View file @
0a1a4f0a
module
EE
module
Gitlab
module
LDAP
class
Group
attr_accessor
:adapter
def
self
.
find_by_cn
(
cn
,
adapter
)
cn
=
Net
::
LDAP
::
Filter
.
escape
(
cn
)
adapter
.
group
(
cn
)
end
def
initialize
(
entry
,
adapter
=
nil
)
Rails
.
logger
.
debug
{
"Instantiating
#{
self
.
class
.
name
}
with LDIF:
\n
#{
entry
.
to_ldif
}
"
}
@entry
=
entry
@adapter
=
adapter
end
def
active_directory?
adapter
.
config
.
active_directory
end
def
cn
entry
.
cn
.
first
end
def
name
cn
end
def
path
name
.
parameterize
end
def
memberuid?
entry
.
respond_to?
:memberuid
end
def
member_uids
entry
.
memberuid
end
def
member_dns
dns
=
[]
# There's an edge-case with AD where sometimes a recursive search
# doesn't return all users at the top-level. Concat recursive results
# with regular results to be safe. See gitlab-ee#484
if
active_directory?
dns
=
adapter
.
dns_for_filter
(
active_directory_recursive_memberof_filter
)
end
if
(
entry
.
respond_to?
:member
)
&&
(
entry
.
respond_to?
:submember
)
dns
.
concat
(
entry
.
member
+
entry
.
submember
)
elsif
entry
.
respond_to?
:member
dns
.
concat
(
entry
.
member
)
elsif
entry
.
respond_to?
:uniquemember
dns
.
concat
(
entry
.
uniquemember
)
elsif
entry
.
respond_to?
:memberof
dns
.
concat
(
entry
.
memberof
)
else
Rails
.
logger
.
warn
(
"Could not find member DNs for LDAP group
#{
entry
.
inspect
}
"
)
end
dns
.
uniq
end
private
# We use the ActiveDirectory LDAP_MATCHING_RULE_IN_CHAIN matching rule; see
# http://msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx#code-snippet-5
def
active_directory_recursive_memberof_filter
Net
::
LDAP
::
Filter
.
ex
(
"memberOf:1.2.840.113556.1.4.1941"
,
entry
.
dn
)
end
def
entry
@entry
end
end
end
end
end
lib/ee/gitlab/ldap/group_sync.rb
0 → 100644
View file @
0a1a4f0a
require
'net/ldap/dn'
module
EE
module
Gitlab
module
LDAP
class
GroupSync
attr_reader
:provider
# Open a connection so we can run all queries through it.
# It's more efficient than the default of opening/closing per LDAP query.
def
self
.
open
(
provider
,
&
block
)
::
Gitlab
::
LDAP
::
Adapter
.
open
(
provider
)
do
|
adapter
|
block
.
call
(
self
.
new
(
provider
,
adapter
))
end
end
def
self
.
execute
# Shuffle providers to prevent a scenario where sync fails after a time
# and only the first provider or two get synced. This shuffles the order
# so subsequent syncs should eventually get to all providers. Obviously
# we should avoid failure, but this is an additional safeguard.
::
Gitlab
::
LDAP
::
Config
.
providers
.
shuffle
.
each
do
|
provider
|
self
.
open
(
provider
)
do
|
group_sync
|
group_sync
.
update_permissions
end
end
true
end
def
initialize
(
provider
,
adapter
=
nil
)
@adapter
=
adapter
@provider
=
provider
end
def
update_permissions
if
group_base
.
present?
logger
.
debug
{
"Performing LDAP group sync for '
#{
provider
}
' provider"
}
sync_groups
logger
.
debug
{
"Finished LDAP group sync for '
#{
provider
}
' provider"
}
else
logger
.
debug
{
"No `group_base` configured for '
#{
provider
}
' provider. Skipping"
}
end
if
admin_group
.
present?
logger
.
debug
{
"Syncing admin users for '
#{
provider
}
' provider"
}
sync_admin_users
logger
.
debug
{
"Finished syncing admin users for '
#{
provider
}
' provider"
}
else
logger
.
debug
{
"No `admin_group` configured for '
#{
provider
}
' provider. Skipping"
}
end
if
external_groups
.
empty?
logger
.
debug
{
"No `external_groups` configured for '
#{
provider
}
' provider. Skipping"
}
else
logger
.
debug
{
"Syncing external users for '
#{
provider
}
' provider"
}
sync_external_users
logger
.
debug
{
"Finished syncing external users for '
#{
provider
}
' provider"
}
end
nil
end
# Iterate of all GitLab groups with LDAP links. Build an access hash
# representing a user's highest access level among the LDAP links within
# the same GitLab group.
def
sync_groups
# Order results by last_ldap_sync_at ASC so groups with older last
# sync time are handled first
groups_where_group_links_with_provider_ordered
.
each
do
|
group
|
lease
=
::
Gitlab
::
ExclusiveLease
.
new
(
"ldap_group_sync:
#{
provider
}
:
#{
group
.
id
}
"
,
timeout:
3600
)
next
unless
lease
.
try_obtain
logger
.
debug
{
"Syncing '
#{
group
.
name
}
' group"
}
access_levels
=
AccessLevels
.
new
# Only iterate over group links for the current provider
group
.
ldap_group_links
.
with_provider
(
provider
).
each
do
|
group_link
|
if
member_dns
=
dns_for_group_cn
(
group_link
.
cn
)
access_levels
.
set
(
member_dns
,
to:
group_link
.
group_access
)
logger
.
debug
do
"Resolved '
#{
group
.
name
}
' group member access:
#{
access_levels
.
to_hash
}
"
end
end
end
update_existing_group_membership
(
group
,
access_levels
)
add_new_members
(
group
,
access_levels
)
group
.
update
(
last_ldap_sync_at:
Time
.
now
)
logger
.
debug
{
"Finished syncing '
#{
group
.
name
}
' group"
}
end
end
# Update global administrators based on the specified admin group CN
def
sync_admin_users
admin_group_member_dns
=
dns_for_group_cn
(
admin_group
)
current_admin_users
=
::
User
.
admins
.
with_provider
(
provider
)
verified_admin_users
=
[]
# Verify existing admin users and add new ones.
admin_group_member_dns
.
each
do
|
member_dn
|
user
=
::
Gitlab
::
LDAP
::
User
.
find_by_uid_and_provider
(
member_dn
,
provider
)
if
user
.
present?
user
.
admin
=
true
user
.
save
verified_admin_users
<<
user
else
logger
.
debug
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: User with DN `
#{
member_dn
}
` should have admin
access but there is no user in GitLab with that identity.
Membership will be updated once the user signs in for the first time.
MSG
end
end
end
# Revoke the unverified admins.
current_admin_users
.
each
do
|
user
|
unless
verified_admin_users
.
include?
(
user
)
user
.
admin
=
false
user
.
save
end
end
end
# Update external users based on the specified external groups CN
def
sync_external_users
current_external_users
=
::
User
.
external
.
with_provider
(
provider
)
verified_external_users
=
[]
external_groups
.
each
do
|
group
|
group_dns
=
dns_for_group_cn
(
group
)
group_dns
.
each
do
|
member_dn
|
user
=
::
Gitlab
::
LDAP
::
User
.
find_by_uid_and_provider
(
member_dn
,
provider
)
if
user
.
present?
user
.
external
=
true
user
.
save
verified_external_users
<<
user
else
logger
.
debug
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: User with DN `
#{
member_dn
}
` should be marked as
external but there is no user in GitLab with that identity.
Membership will be updated once the user signs in for the first time.
MSG
end
end
end
end
update_external_permissions
(
current_external_users
,
verified_external_users
)
end
private
# Cache LDAP group member DNs so we don't query LDAP groups more than once.
def
dns_for_group_cn
(
group_cn
)
@dns_for_group_cn
||=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
ldap_group_member_dns
(
k
)
}
@dns_for_group_cn
[
group_cn
]
end
# Cache user DN so we don't generate excess queries to map UID to DN
def
dn_for_uid
(
uid
)
@dn_for_uid
||=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
member_uid_to_dn
(
k
)
}
@dn_for_uid
[
uid
]
end
def
adapter
@adapter
||=
::
Gitlab
::
LDAP
::
Adapter
.
new
(
provider
)
end
def
config
@config
||=
::
Gitlab
::
LDAP
::
Config
.
new
(
provider
)
end
def
group_base
config
.
group_base
end
def
admin_group
config
.
admin_group
end
def
external_groups
config
.
external_groups
end
def
ldap_group_member_dns
(
ldap_group_cn
)
ldap_group
=
Group
.
find_by_cn
(
ldap_group_cn
,
adapter
)
unless
ldap_group
.
present?
logger
.
warn
{
"Cannot find LDAP group with CN '
#{
ldap_group_cn
}
'. Skipping"
}
return
[]
end
member_dns
=
ldap_group
.
member_dns
if
member_dns
.
empty?
# Group must be empty
return
[]
unless
ldap_group
.
memberuid?
members
=
ldap_group
.
member_uids
member_dns
=
members
.
map
{
|
uid
|
dn_for_uid
(
uid
)
}
end
ensure_full_dns!
(
member_dns
)
logger
.
debug
{
"Members in '
#{
ldap_group
.
name
}
' LDAP group:
#{
member_dns
}
"
}
# Various lookups in this method could return `nil` values.
# Compact the array to remove those entries
member_dns
.
compact
end
# At least one customer reported that their LDAP `member` values contain
# only `uid=username` and not the full DN. This method allows us to
# account for that. See gitlab-ee#442
def
ensure_full_dns!
(
dns
)
dns
.
map!
do
|
dn
|
begin
parsed_dn
=
Net
::
LDAP
::
DN
.
new
(
dn
).
to_a
rescue
RuntimeError
=>
e
# Net::LDAP raises a generic RuntimeError. Bad library! Bad!
logger
.
error
{
"Found malformed DN: '
#{
dn
}
'. Skipping.
#{
e
.
message
}
"
}
next
end
# If there is more than one key/value set we must have a full DN,
# or at least the probability is higher.
if
parsed_dn
.
count
>
2
dn
elsif
parsed_dn
[
0
]
==
'uid'
dn_for_uid
(
parsed_dn
[
1
])
else
logger
.
warn
{
"Found potentially malformed/incomplete DN: '
#{
dn
}
'"
}
dn
end
end
# Remove `nil` values generated by the rescue above.
dns
.
compact!
end
def
member_uid_to_dn
(
uid
)
identity
=
Identity
.
find_by
(
provider:
provider
,
secondary_extern_uid:
uid
)
if
identity
.
present?
# Use the DN on record in GitLab when it's available
identity
.
extern_uid
else
ldap_user
=
::
Gitlab
::
LDAP
::
Person
.
find_by_uid
(
uid
,
adapter
)
# Can't find a matching user for group entry
return
nil
unless
ldap_user
.
present?
# Update user identity so we don't have to go through this again
update_identity
(
ldap_user
.
dn
,
uid
)
ldap_user
.
dn
end
end
def
update_identity
(
dn
,
uid
)
identity
=
Identity
.
find_by
(
provider:
provider
,
extern_uid:
dn
)
# User may not exist in GitLab yet. Skip.
return
unless
identity
.
present?
identity
.
secondary_extern_uid
=
uid
identity
.
save
end
def
update_existing_group_membership
(
group
,
access_levels
)
logger
.
debug
{
"Updating existing membership for '
#{
group
.
name
}
' group"
}
select_and_preload_group_members
(
group
).
each
do
|
member
|
user
=
member
.
user
identity
=
user
.
identities
.
select
(
:id
,
:extern_uid
)
.
with_provider
(
provider
).
first
member_dn
=
identity
.
extern_uid
# Skip if this is not an LDAP user with a valid `extern_uid`.
next
unless
member_dn
.
present?
# Prevent shifting group membership, in case where user is a member
# of two LDAP groups from different providers linked to the same
# GitLab group. This is not ideal, but preserves existing behavior.
if
user
.
ldap_identity
.
id
!=
identity
.
id
access_levels
.
delete
(
member_dn
)
next
end
desired_access
=
access_levels
[
member_dn
]
# Don't do anything if the user already has the desired access level
if
member
.
access_level
==
desired_access
access_levels
.
delete
(
member_dn
)
next
end
# Check and update the access level. If `desired_access` is `nil`
# we need to delete the user from the group.
if
desired_access
.
present?
add_or_update_user_membership
(
user
,
group
,
desired_access
)
# Delete this entry from the hash now that we've acted on it
access_levels
.
delete
(
member_dn
)
elsif
group
.
last_owner?
(
user
)
warn_cannot_remove_last_owner
(
user
,
group
)
else
group
.
users
.
delete
(
user
)
end
end
end
def
update_external_permissions
(
users
,
verified
)
# Restore normal access to users no longer found in the external groups
users
.
each
do
|
user
|
unless
verified
.
include?
(
user
)
user
.
external
=
false
user
.
save
end
end
end
def
add_new_members
(
group
,
access_levels
)
logger
.
debug
{
"Adding new members to '
#{
group
.
name
}
' group"
}
access_levels
.
each
do
|
member_dn
,
access_level
|
user
=
::
Gitlab
::
LDAP
::
User
.
find_by_uid_and_provider
(
member_dn
,
provider
)
if
user
.
present?
add_or_update_user_membership
(
user
,
group
,
access_level
)
else
logger
.
debug
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: User with DN `
#{
member_dn
}
` should have access
to '
#{
group
.
name
}
' group but there is no user in GitLab with that
identity. Membership will be updated once the user signs in for
the first time.
MSG
end
end
end
end
def
add_or_update_user_membership
(
user
,
group
,
access
)
# Prevent the last owner of a group from being demoted
if
access
<
::
Gitlab
::
Access
::
OWNER
&&
group
.
last_owner?
(
user
)
warn_cannot_remove_last_owner
(
user
,
group
)
else
# If you pass the user object, instead of just user ID,
# it saves an extra user database query.
group
.
add_users
([
user
],
access
,
skip_notification:
true
)
end
end
def
warn_cannot_remove_last_owner
(
user
,
group
)
logger
.
warn
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: LDAP group sync cannot remove
#{
user
.
name
}
(
#{
user
.
id
}
) from group
#{
group
.
name
}
(
#{
group
.
id
}
) as this is
the group's last owner
MSG
end
end
def
select_and_preload_group_members
(
group
)
group
.
members
.
select_access_level_and_user
.
with_identity_provider
(
provider
).
preload
(
:user
)
end
def
groups_where_group_links_with_provider_ordered
::
Group
.
where_group_links_with_provider
(
provider
)
.
preload
(
:ldap_group_links
)
.
reorder
(
'last_ldap_sync_at ASC, namespaces.id ASC'
)
.
distinct
end
def
logger
Rails
.
logger
end
end
end
end
end
lib/gitlab/ldap/access_levels.rb
deleted
100644 → 0
View file @
b9f09018
module
Gitlab
module
LDAP
# Create a hash map of member DNs to access levels. The highest
# access level is retained in cases where `set` is called multiple times
# for the same DN.
class
AccessLevels
<
Hash
def
set
(
dns
,
to
:)
dns
.
each
do
|
dn
|
current
=
self
[
dn
]
# Keep the higher of the access values.
self
[
dn
]
=
to
if
current
.
nil?
||
to
>
current
end
end
end
end
end
lib/gitlab/ldap/adapter.rb
View file @
0a1a4f0a
# LDAP connection adapter
#
# Contains methods common to both GitLab CE and EE.
# All EE methods should be in `EE::Gitlab::LDAP::Adapter` only.
module
Gitlab
module
LDAP
class
Adapter
include
EE
::
Gitlab
::
LDAP
::
Adapter
attr_reader
:provider
,
:ldap
def
self
.
open
(
provider
,
&
block
)
...
...
@@ -22,30 +28,6 @@ module Gitlab
Gitlab
::
LDAP
::
Config
.
new
(
provider
)
end
# Get LDAP groups from ou=Groups
#
# cn - filter groups by name
#
# Ex.
# groups("dev*") # return all groups start with 'dev'
#
def
groups
(
cn
=
"*"
,
size
=
nil
)
options
=
{
base:
config
.
group_base
,
filter:
Net
::
LDAP
::
Filter
.
eq
(
"cn"
,
cn
)
}
options
.
merge!
(
size:
size
)
if
size
ldap_search
(
options
).
map
do
|
entry
|
Gitlab
::
LDAP
::
Group
.
new
(
entry
,
self
)
end
end
def
group
(
*
args
)
groups
(
*
args
).
first
end
def
users
(
field
,
value
,
limit
=
nil
)
if
field
.
to_sym
==
:dn
options
=
{
...
...
@@ -86,13 +68,6 @@ module Gitlab
users
(
*
args
).
first
end
def
dn_matches_filter?
(
dn
,
filter
)
ldap_search
(
base:
dn
,
filter:
filter
,
scope:
Net
::
LDAP
::
SearchScope_BaseObject
,
attributes:
%w{dn}
).
any?
end
def
dns_for_filter
(
filter
)
ldap_search
(
base:
config
.
base
,
...
...
lib/gitlab/ldap/group.rb
deleted
100644 → 0
View file @
b9f09018
module
Gitlab
module
LDAP
class
Group
attr_accessor
:adapter
def
self
.
find_by_cn
(
cn
,
adapter
)
cn
=
Net
::
LDAP
::
Filter
.
escape
(
cn
)
adapter
.
group
(
cn
)
end
def
initialize
(
entry
,
adapter
=
nil
)
Rails
.
logger
.
debug
{
"Instantiating
#{
self
.
class
.
name
}
with LDIF:
\n
#{
entry
.
to_ldif
}
"
}
@entry
=
entry
@adapter
=
adapter
end
def
active_directory?
adapter
.
config
.
active_directory
end
def
cn
entry
.
cn
.
first
end
def
name
cn
end
def
path
name
.
parameterize
end
def
memberuid?
entry
.
respond_to?
:memberuid
end
def
member_uids
entry
.
memberuid
end
def
member_dns
dns
=
[]
# There's an edge-case with AD where sometimes a recursive search
# doesn't return all users at the top-level. Concat recursive results
# with regular results to be safe. See gitlab-ee#484
if
active_directory?
dns
=
adapter
.
dns_for_filter
(
active_directory_recursive_memberof_filter
)
end
if
(
entry
.
respond_to?
:member
)
&&
(
entry
.
respond_to?
:submember
)
dns
.
concat
(
entry
.
member
+
entry
.
submember
)
elsif
entry
.
respond_to?
:member
dns
.
concat
(
entry
.
member
)
elsif
entry
.
respond_to?
:uniquemember
dns
.
concat
(
entry
.
uniquemember
)
elsif
entry
.
respond_to?
:memberof
dns
.
concat
(
entry
.
memberof
)
else
Rails
.
logger
.
warn
(
"Could not find member DNs for LDAP group
#{
entry
.
inspect
}
"
)
end
dns
.
uniq
end
private
# We use the ActiveDirectory LDAP_MATCHING_RULE_IN_CHAIN matching rule; see
# http://msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx#code-snippet-5
def
active_directory_recursive_memberof_filter
Net
::
LDAP
::
Filter
.
ex
(
"memberOf:1.2.840.113556.1.4.1941"
,
entry
.
dn
)
end
def
entry
@entry
end
end
end
end
lib/gitlab/ldap/group_sync.rb
deleted
100644 → 0
View file @
b9f09018
require
'net/ldap/dn'
module
Gitlab
module
LDAP
class
GroupSync
attr_reader
:provider
# Open a connection so we can run all queries through it.
# It's more efficient than the default of opening/closing per LDAP query.
def
self
.
open
(
provider
,
&
block
)
Gitlab
::
LDAP
::
Adapter
.
open
(
provider
)
do
|
adapter
|
block
.
call
(
self
.
new
(
provider
,
adapter
))
end
end
def
self
.
execute
# Shuffle providers to prevent a scenario where sync fails after a time
# and only the first provider or two get synced. This shuffles the order
# so subsequent syncs should eventually get to all providers. Obviously
# we should avoid failure, but this is an additional safeguard.
Gitlab
::
LDAP
::
Config
.
providers
.
shuffle
.
each
do
|
provider
|
self
.
open
(
provider
)
do
|
group_sync
|
group_sync
.
update_permissions
end
end
true
end
def
initialize
(
provider
,
adapter
=
nil
)
@adapter
=
adapter
@provider
=
provider
end
def
update_permissions
if
group_base
.
present?
logger
.
debug
{
"Performing LDAP group sync for '
#{
provider
}
' provider"
}
sync_groups
logger
.
debug
{
"Finished LDAP group sync for '
#{
provider
}
' provider"
}
else
logger
.
debug
{
"No `group_base` configured for '
#{
provider
}
' provider. Skipping"
}
end
if
admin_group
.
present?
logger
.
debug
{
"Syncing admin users for '
#{
provider
}
' provider"
}
sync_admin_users
logger
.
debug
{
"Finished syncing admin users for '
#{
provider
}
' provider"
}
else
logger
.
debug
{
"No `admin_group` configured for '
#{
provider
}
' provider. Skipping"
}
end
if
external_groups
.
empty?
logger
.
debug
{
"No `external_groups` configured for '
#{
provider
}
' provider. Skipping"
}
else
logger
.
debug
{
"Syncing external users for '
#{
provider
}
' provider"
}
sync_external_users
logger
.
debug
{
"Finished syncing external users for '
#{
provider
}
' provider"
}
end
nil
end
# Iterate of all GitLab groups with LDAP links. Build an access hash
# representing a user's highest access level among the LDAP links within
# the same GitLab group.
def
sync_groups
# Order results by last_ldap_sync_at ASC so groups with older last
# sync time are handled first
groups_where_group_links_with_provider_ordered
.
each
do
|
group
|
lease
=
Gitlab
::
ExclusiveLease
.
new
(
"ldap_group_sync:
#{
provider
}
:
#{
group
.
id
}
"
,
timeout:
3600
)
next
unless
lease
.
try_obtain
logger
.
debug
{
"Syncing '
#{
group
.
name
}
' group"
}
access_levels
=
Gitlab
::
LDAP
::
AccessLevels
.
new
# Only iterate over group links for the current provider
group
.
ldap_group_links
.
with_provider
(
provider
).
each
do
|
group_link
|
if
member_dns
=
dns_for_group_cn
(
group_link
.
cn
)
access_levels
.
set
(
member_dns
,
to:
group_link
.
group_access
)
logger
.
debug
do
"Resolved '
#{
group
.
name
}
' group member access:
#{
access_levels
.
to_hash
}
"
end
end
end
update_existing_group_membership
(
group
,
access_levels
)
add_new_members
(
group
,
access_levels
)
group
.
update
(
last_ldap_sync_at:
Time
.
now
)
logger
.
debug
{
"Finished syncing '
#{
group
.
name
}
' group"
}
end
end
# Update global administrators based on the specified admin group CN
def
sync_admin_users
admin_group_member_dns
=
dns_for_group_cn
(
admin_group
)
current_admin_users
=
::
User
.
admins
.
with_provider
(
provider
)
verified_admin_users
=
[]
# Verify existing admin users and add new ones.
admin_group_member_dns
.
each
do
|
member_dn
|
user
=
Gitlab
::
LDAP
::
User
.
find_by_uid_and_provider
(
member_dn
,
provider
)
if
user
.
present?
user
.
admin
=
true
user
.
save
verified_admin_users
<<
user
else
logger
.
debug
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: User with DN `
#{
member_dn
}
` should have admin
access but there is no user in GitLab with that identity.
Membership will be updated once the user signs in for the first time.
MSG
end
end
end
# Revoke the unverified admins.
current_admin_users
.
each
do
|
user
|
unless
verified_admin_users
.
include?
(
user
)
user
.
admin
=
false
user
.
save
end
end
end
# Update external users based on the specified external groups CN
def
sync_external_users
current_external_users
=
::
User
.
external
.
with_provider
(
provider
)
verified_external_users
=
[]
external_groups
.
each
do
|
group
|
group_dns
=
dns_for_group_cn
(
group
)
group_dns
.
each
do
|
member_dn
|
user
=
Gitlab
::
LDAP
::
User
.
find_by_uid_and_provider
(
member_dn
,
provider
)
if
user
.
present?
user
.
external
=
true
user
.
save
verified_external_users
<<
user
else
logger
.
debug
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: User with DN `
#{
member_dn
}
` should be marked as
external but there is no user in GitLab with that identity.
Membership will be updated once the user signs in for the first time.
MSG
end
end
end
end
update_external_permissions
(
current_external_users
,
verified_external_users
)
end
private
# Cache LDAP group member DNs so we don't query LDAP groups more than once.
def
dns_for_group_cn
(
group_cn
)
@dns_for_group_cn
||=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
ldap_group_member_dns
(
k
)
}
@dns_for_group_cn
[
group_cn
]
end
# Cache user DN so we don't generate excess queries to map UID to DN
def
dn_for_uid
(
uid
)
@dn_for_uid
||=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
member_uid_to_dn
(
k
)
}
@dn_for_uid
[
uid
]
end
def
adapter
@adapter
||=
Gitlab
::
LDAP
::
Adapter
.
new
(
provider
)
end
def
config
@config
||=
Gitlab
::
LDAP
::
Config
.
new
(
provider
)
end
def
group_base
config
.
group_base
end
def
admin_group
config
.
admin_group
end
def
external_groups
config
.
external_groups
end
def
ldap_group_member_dns
(
ldap_group_cn
)
ldap_group
=
Gitlab
::
LDAP
::
Group
.
find_by_cn
(
ldap_group_cn
,
adapter
)
unless
ldap_group
.
present?
logger
.
warn
{
"Cannot find LDAP group with CN '
#{
ldap_group_cn
}
'. Skipping"
}
return
[]
end
member_dns
=
ldap_group
.
member_dns
if
member_dns
.
empty?
# Group must be empty
return
[]
unless
ldap_group
.
memberuid?
members
=
ldap_group
.
member_uids
member_dns
=
members
.
map
{
|
uid
|
dn_for_uid
(
uid
)
}
end
ensure_full_dns!
(
member_dns
)
logger
.
debug
{
"Members in '
#{
ldap_group
.
name
}
' LDAP group:
#{
member_dns
}
"
}
# Various lookups in this method could return `nil` values.
# Compact the array to remove those entries
member_dns
.
compact
end
# At least one customer reported that their LDAP `member` values contain
# only `uid=username` and not the full DN. This method allows us to
# account for that. See gitlab-ee#442
def
ensure_full_dns!
(
dns
)
dns
.
map!
do
|
dn
|
begin
parsed_dn
=
Net
::
LDAP
::
DN
.
new
(
dn
).
to_a
rescue
RuntimeError
=>
e
# Net::LDAP raises a generic RuntimeError. Bad library! Bad!
logger
.
error
{
"Found malformed DN: '
#{
dn
}
'. Skipping.
#{
e
.
message
}
"
}
next
end
# If there is more than one key/value set we must have a full DN,
# or at least the probability is higher.
if
parsed_dn
.
count
>
2
dn
elsif
parsed_dn
[
0
]
==
'uid'
dn_for_uid
(
parsed_dn
[
1
])
else
logger
.
warn
{
"Found potentially malformed/incomplete DN: '
#{
dn
}
'"
}
dn
end
end
# Remove `nil` values generated by the rescue above.
dns
.
compact!
end
def
member_uid_to_dn
(
uid
)
identity
=
Identity
.
find_by
(
provider:
provider
,
secondary_extern_uid:
uid
)
if
identity
.
present?
# Use the DN on record in GitLab when it's available
identity
.
extern_uid
else
ldap_user
=
Gitlab
::
LDAP
::
Person
.
find_by_uid
(
uid
,
adapter
)
# Can't find a matching user for group entry
return
nil
unless
ldap_user
.
present?
# Update user identity so we don't have to go through this again
update_identity
(
ldap_user
.
dn
,
uid
)
ldap_user
.
dn
end
end
def
update_identity
(
dn
,
uid
)
identity
=
Identity
.
find_by
(
provider:
provider
,
extern_uid:
dn
)
# User may not exist in GitLab yet. Skip.
return
unless
identity
.
present?
identity
.
secondary_extern_uid
=
uid
identity
.
save
end
def
update_existing_group_membership
(
group
,
access_levels
)
logger
.
debug
{
"Updating existing membership for '
#{
group
.
name
}
' group"
}
select_and_preload_group_members
(
group
).
each
do
|
member
|
user
=
member
.
user
identity
=
user
.
identities
.
select
(
:id
,
:extern_uid
)
.
with_provider
(
provider
).
first
member_dn
=
identity
.
extern_uid
# Skip if this is not an LDAP user with a valid `extern_uid`.
next
unless
member_dn
.
present?
# Prevent shifting group membership, in case where user is a member
# of two LDAP groups from different providers linked to the same
# GitLab group. This is not ideal, but preserves existing behavior.
if
user
.
ldap_identity
.
id
!=
identity
.
id
access_levels
.
delete
(
member_dn
)
next
end
desired_access
=
access_levels
[
member_dn
]
# Don't do anything if the user already has the desired access level
if
member
.
access_level
==
desired_access
access_levels
.
delete
(
member_dn
)
next
end
# Check and update the access level. If `desired_access` is `nil`
# we need to delete the user from the group.
if
desired_access
.
present?
add_or_update_user_membership
(
user
,
group
,
desired_access
)
# Delete this entry from the hash now that we've acted on it
access_levels
.
delete
(
member_dn
)
elsif
group
.
last_owner?
(
user
)
warn_cannot_remove_last_owner
(
user
,
group
)
else
group
.
users
.
delete
(
user
)
end
end
end
def
update_external_permissions
(
users
,
verified
)
# Restore normal access to users no longer found in the external groups
users
.
each
do
|
user
|
unless
verified
.
include?
(
user
)
user
.
external
=
false
user
.
save
end
end
end
def
add_new_members
(
group
,
access_levels
)
logger
.
debug
{
"Adding new members to '
#{
group
.
name
}
' group"
}
access_levels
.
each
do
|
member_dn
,
access_level
|
user
=
Gitlab
::
LDAP
::
User
.
find_by_uid_and_provider
(
member_dn
,
provider
)
if
user
.
present?
add_or_update_user_membership
(
user
,
group
,
access_level
)
else
logger
.
debug
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: User with DN `
#{
member_dn
}
` should have access
to '
#{
group
.
name
}
' group but there is no user in GitLab with that
identity. Membership will be updated once the user signs in for
the first time.
MSG
end
end
end
end
def
add_or_update_user_membership
(
user
,
group
,
access
)
# Prevent the last owner of a group from being demoted
if
access
<
Gitlab
::
Access
::
OWNER
&&
group
.
last_owner?
(
user
)
warn_cannot_remove_last_owner
(
user
,
group
)
else
# If you pass the user object, instead of just user ID,
# it saves an extra user database query.
group
.
add_users
([
user
],
access
,
skip_notification:
true
)
end
end
def
warn_cannot_remove_last_owner
(
user
,
group
)
logger
.
warn
do
<<-
MSG
.
strip_heredoc
.
tr
(
"
\n
"
,
' '
)
#{
self
.
class
.
name
}
: LDAP group sync cannot remove
#{
user
.
name
}
(
#{
user
.
id
}
) from group
#{
group
.
name
}
(
#{
group
.
id
}
) as this is
the group's last owner
MSG
end
end
def
select_and_preload_group_members
(
group
)
group
.
members
.
select_access_level_and_user
.
with_identity_provider
(
provider
).
preload
(
:user
)
end
def
groups_where_group_links_with_provider_ordered
::
Group
.
where_group_links_with_provider
(
provider
)
.
preload
(
:ldap_group_links
)
.
reorder
(
'last_ldap_sync_at ASC, namespaces.id ASC'
)
.
distinct
end
def
logger
Rails
.
logger
end
end
end
end
spec/lib/gitlab/ldap/access_levels_spec.rb
→
spec/lib/
ee/
gitlab/ldap/access_levels_spec.rb
View file @
0a1a4f0a
require
'spec_helper'
describe
Gitlab
::
LDAP
::
AccessLevels
,
lib:
true
do
describe
EE
::
Gitlab
::
LDAP
::
AccessLevels
,
lib:
true
do
describe
'#set'
do
let
(
:access_levels
)
{
Gitlab
::
LDAP
::
AccessLevel
s
.
new
}
let
(
:access_levels
)
{
described_clas
s
.
new
}
let
(
:dns
)
do
%w(
uid=johndoe,ou=users,dc=example,dc=com
...
...
spec/lib/ee/gitlab/ldap/adapter_spec.rb
0 → 100644
View file @
0a1a4f0a
require
'spec_helper'
# Test things specific to the EE mixin, but run the actual tests
# against the main adapter class to ensure it's properly included
describe
Gitlab
::
LDAP
::
Adapter
,
lib:
true
do
subject
{
Gitlab
::
LDAP
::
Adapter
.
new
'ldapmain'
}
it
{
is_expected
.
to
include_module
(
EE
::
Gitlab
::
LDAP
::
Adapter
)
}
end
spec/lib/gitlab/ldap/group_spec.rb
→
spec/lib/
ee/
gitlab/ldap/group_spec.rb
View file @
0a1a4f0a
require
'spec_helper'
describe
Gitlab
::
LDAP
::
Group
,
lib:
true
do
describe
EE
::
Gitlab
::
LDAP
::
Group
,
lib:
true
do
describe
'#member_dns'
do
def
ldif
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
...
...
spec/lib/gitlab/ldap/group_sync_spec.rb
→
spec/lib/
ee/
gitlab/ldap/group_sync_spec.rb
View file @
0a1a4f0a
require
'spec_helper'
describe
Gitlab
::
LDAP
::
GroupSync
,
lib:
true
do
let
(
:group_sync
)
{
Gitlab
::
LDAP
::
GroupSync
.
new
(
'ldapmain'
)
}
describe
EE
::
Gitlab
::
LDAP
::
GroupSync
,
lib:
true
do
let
(
:group_sync
)
{
described_class
.
new
(
'ldapmain'
)
}
let
(
:config
)
{
double
(
:config
,
active_directory:
false
)
}
let
(
:adapter
)
{
double
(
:adapter
,
config:
config
)
}
subject
{
group_sync
}
before
do
allow_any_instance_of
(
Gitlab
::
ExclusiveLease
)
allow_any_instance_of
(
::
Gitlab
::
ExclusiveLease
)
.
to
receive
(
:try_obtain
).
and_return
(
true
)
end
...
...
@@ -59,7 +59,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
context
'with all functionality against one LDAP group type'
do
before
do
allow_any_instance_of
(
Gitlab
::
LDAP
::
Group
)
allow_any_instance_of
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:adapter
).
and_return
(
adapter
)
user1
.
identities
.
create
(
...
...
@@ -71,19 +71,19 @@ describe Gitlab::LDAP::GroupSync, lib: true do
extern_uid:
"uid=
#{
user2
.
username
}
,ou=users,dc=example,dc=com"
)
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'ldap_group1'
,
kind_of
(
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group1
))
.
with
(
'ldap_group1'
,
kind_of
(
::
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group1
))
group1
.
ldap_group_links
.
create
(
cn:
'ldap_group1'
,
group_access:
Gitlab
::
Access
::
DEVELOPER
,
group_access:
::
Gitlab
::
Access
::
DEVELOPER
,
provider:
'ldapmain'
)
group2
.
ldap_group_links
.
create
(
cn:
'ldap_group1'
,
group_access:
Gitlab
::
Access
::
OWNER
,
group_access:
::
Gitlab
::
Access
::
OWNER
,
provider:
'ldapmain'
)
end
...
...
@@ -92,9 +92,9 @@ describe Gitlab::LDAP::GroupSync, lib: true do
before
do
# Pre-populate the group with some users
group1
.
add_users
([
user1
.
id
],
Gitlab
::
Access
::
MASTER
,
skip_notification:
true
)
::
Gitlab
::
Access
::
MASTER
,
skip_notification:
true
)
group2
.
add_users
([
user2
.
id
],
Gitlab
::
Access
::
DEVELOPER
,
skip_notification:
true
)
::
Gitlab
::
Access
::
DEVELOPER
,
skip_notification:
true
)
end
it
'adds new members'
do
...
...
@@ -108,7 +108,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
.
to
change
{
group1
.
members
.
where
(
user_id:
user1
.
id
,
access_level:
Gitlab
::
Access
::
DEVELOPER
access_level:
::
Gitlab
::
Access
::
DEVELOPER
).
any?
}.
from
(
false
).
to
(
true
)
end
...
...
@@ -118,7 +118,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
.
to
change
{
group2
.
members
.
where
(
user_id:
user2
.
id
,
access_level:
Gitlab
::
Access
::
OWNER
access_level:
::
Gitlab
::
Access
::
OWNER
).
any?
}.
from
(
false
).
to
(
true
)
end
...
...
@@ -136,9 +136,9 @@ describe Gitlab::LDAP::GroupSync, lib: true do
.
create
(
provider:
group_sync
.
provider
,
extern_uid:
"uid=johndoe,ou=users,dc=example,dc=com"
)
group1
.
add_users
([
user_without_group
.
id
],
Gitlab
::
Access
::
MASTER
,
skip_notification:
true
)
::
Gitlab
::
Access
::
MASTER
,
skip_notification:
true
)
group2
.
add_users
([
user_without_group
.
id
],
Gitlab
::
Access
::
OWNER
,
skip_notification:
true
)
::
Gitlab
::
Access
::
OWNER
,
skip_notification:
true
)
end
it
'removes the user from the group'
do
...
...
@@ -157,27 +157,26 @@ describe Gitlab::LDAP::GroupSync, lib: true do
before
do
group1
.
ldap_group_links
.
create
(
cn:
'ldap_group1'
,
group_access:
Gitlab
::
Access
::
DEVELOPER
,
group_access:
::
Gitlab
::
Access
::
DEVELOPER
,
provider:
'ldapmain'
)
group1
.
add_users
([
user1
.
id
,
user2
.
id
],
Gitlab
::
Access
::
OWNER
,
skip_notification:
true
)
::
Gitlab
::
Access
::
OWNER
,
skip_notification:
true
)
end
# Check two users in a loop to uncover any stale group owner data
it
'downgrades one user but not the other'
do
group_sync
.
sync_groups
expect
(
group1
.
members
.
pluck
(
:access_level
).
sort
)
.
to
eq
([
Gitlab
::
Access
::
DEVELOPER
,
Gitlab
::
Access
::
OWNER
])
.
to
eq
([
::
Gitlab
::
Access
::
DEVELOPER
,
::
Gitlab
::
Access
::
OWNER
])
end
context
'when user is a member of two groups from different providers'
do
let
(
:config
)
{
double
(
:config
,
active_directory:
false
,
provider:
'ldapsecondary'
)
}
let
(
:adapter
)
{
double
(
:adapter
,
config:
config
)
}
let
(
:secondary_group_sync
)
do
Gitlab
::
LDAP
::
GroupSync
.
new
(
'ldapsecondary'
,
adapter
)
described_class
.
new
(
'ldapsecondary'
,
adapter
)
end
let
(
:ldap_secondary_group1
)
do
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
...
...
@@ -194,14 +193,14 @@ describe Gitlab::LDAP::GroupSync, lib: true do
let
(
:user_w_multiple_ids
)
{
create
(
:user
)
}
before
do
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'ldap_group1'
,
any_args
)
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group1
))
allow
(
Gitlab
::
LDAP
::
Group
)
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group1
))
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'ldap_secondary_group1'
,
any_args
)
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
ldap_secondary_group1
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
ldap_secondary_group1
))
user_w_multiple_ids
.
identities
.
create
(
[
{
...
...
@@ -216,16 +215,16 @@ describe Gitlab::LDAP::GroupSync, lib: true do
)
group1
.
ldap_group_links
.
create
(
cn:
'ldap_group1'
,
group_access:
Gitlab
::
Access
::
DEVELOPER
,
group_access:
::
Gitlab
::
Access
::
DEVELOPER
,
provider:
'ldapprimary'
)
group1
.
ldap_group_links
.
create
(
cn:
'ldap_secondary_group1'
,
group_access:
Gitlab
::
Access
::
OWNER
,
group_access:
::
Gitlab
::
Access
::
OWNER
,
provider:
'ldapsecondary'
)
group1
.
add_users
([
user_w_multiple_ids
.
id
],
Gitlab
::
Access
::
DEVELOPER
,
skip_notification:
true
)
::
Gitlab
::
Access
::
DEVELOPER
,
skip_notification:
true
)
end
it
'does not change user permissions for secondary group link'
do
...
...
@@ -233,7 +232,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
.
not_to
change
{
group1
.
members
.
where
(
user_id:
user_w_multiple_ids
.
id
,
access_level:
Gitlab
::
Access
::
OWNER
access_level:
::
Gitlab
::
Access
::
OWNER
).
any?
}
end
...
...
@@ -252,7 +251,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
objectclass: groupOfNames
EOS
allow_any_instance_of
(
Gitlab
::
LDAP
::
Group
)
allow_any_instance_of
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:adapter
).
and_return
(
adapter
)
user1
.
identities
.
create
(
...
...
@@ -264,36 +263,36 @@ describe Gitlab::LDAP::GroupSync, lib: true do
extern_uid:
"uid=
#{
user2
.
username
}
,ou=users,dc=example,dc=com"
)
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'ldap_group1'
,
kind_of
(
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group1
))
allow
(
Gitlab
::
LDAP
::
Group
)
.
with
(
'ldap_group1'
,
kind_of
(
::
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group1
))
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'ldap_group2'
,
kind_of
(
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group2
))
.
with
(
'ldap_group2'
,
kind_of
(
::
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
ldap_group2
))
group1
.
members
.
destroy_all
group1
.
ldap_group_links
.
destroy_all
group1
.
ldap_group_links
.
create
(
cn:
'ldap_group1'
,
group_access:
Gitlab
::
Access
::
DEVELOPER
,
group_access:
::
Gitlab
::
Access
::
DEVELOPER
,
provider:
'ldapmain'
)
group2
.
members
.
destroy_all
group2
.
ldap_group_links
.
destroy_all
group2
.
ldap_group_links
.
create
(
cn:
'ldap_group2'
,
group_access:
Gitlab
::
Access
::
MASTER
,
group_access:
::
Gitlab
::
Access
::
MASTER
,
provider:
'ldapmain'
)
group_sync
.
sync_groups
expect
(
group1
.
members
.
pluck
(
:user_id
).
sort
).
to
eq
([
user1
.
id
,
user2
.
id
].
sort
)
expect
(
group1
.
members
.
pluck
(
:access_level
).
uniq
).
to
eq
([
Gitlab
::
Access
::
DEVELOPER
])
expect
(
group1
.
members
.
pluck
(
:access_level
).
uniq
).
to
eq
([
::
Gitlab
::
Access
::
DEVELOPER
])
expect
(
group2
.
members
.
pluck
(
:user_id
)).
to
eq
([
user2
.
id
])
expect
(
group2
.
members
.
pluck
(
:access_level
).
uniq
).
to
eq
([
Gitlab
::
Access
::
MASTER
])
expect
(
group2
.
members
.
pluck
(
:access_level
).
uniq
).
to
eq
([
::
Gitlab
::
Access
::
MASTER
])
end
end
end
...
...
@@ -303,9 +302,9 @@ describe Gitlab::LDAP::GroupSync, lib: true do
let
(
:secondary_extern_uid
)
{
nil
}
before
do
allow_any_instance_of
(
Gitlab
::
LDAP
::
Group
)
allow_any_instance_of
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:adapter
).
and_return
(
adapter
)
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
ldap_group
.
cn
,
any_args
)
.
and_return
(
ldap_group
)
...
...
@@ -316,7 +315,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
)
group1
.
ldap_group_links
.
create
(
cn:
ldap_group
.
cn
,
group_access:
Gitlab
::
Access
::
DEVELOPER
,
group_access:
::
Gitlab
::
Access
::
DEVELOPER
,
provider:
'ldapmain'
)
end
...
...
@@ -324,7 +323,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
# GroupOfNames - OpenLDAP
context
'with groupOfNames style LDAP group'
do
let
(
:ldap_group
)
do
Gitlab
::
LDAP
::
Group
.
new
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
dn: cn=ldap_group1,ou=groups,dc=example,dc=com
cn: ldap_group1
...
...
@@ -346,7 +345,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
# posixGroup - Apple Open Directory
context
'with posixGroup style LDAP group'
do
let
(
:ldap_group
)
do
Gitlab
::
LDAP
::
Group
.
new
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
dn: cn=ldap_group1,ou=groups,dc=example,dc=com
cn: ldap_group1
...
...
@@ -358,7 +357,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
)
end
let
(
:ldap_user
)
do
Gitlab
::
LDAP
::
Person
.
new
(
::
Gitlab
::
LDAP
::
Person
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
"dn: uid=
#{
user1
.
username
}
,ou=users,dc=example,dc=com"
),
...
...
@@ -367,7 +366,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
end
before
do
allow
(
Gitlab
::
LDAP
::
Person
)
allow
(
::
Gitlab
::
LDAP
::
Person
)
.
to
receive
(
:find_by_uid
)
.
with
(
user1
.
username
,
any_args
)
.
and_return
(
ldap_user
)
...
...
@@ -379,8 +378,8 @@ describe Gitlab::LDAP::GroupSync, lib: true do
.
from
(
false
).
to
(
true
)
end
it
'expects Gitlab::LDAP::Person to be called'
do
expect
(
Gitlab
::
LDAP
::
Person
).
to
receive
(
:find_by_uid
)
it
'expects
::
Gitlab::LDAP::Person to be called'
do
expect
(
::
Gitlab
::
LDAP
::
Person
).
to
receive
(
:find_by_uid
)
group_sync
.
sync_groups
end
...
...
@@ -398,8 +397,8 @@ describe Gitlab::LDAP::GroupSync, lib: true do
context
'when the uid is stored in the database'
do
let
(
:secondary_extern_uid
)
{
user1
.
username
}
it
'expects Gitlab::LDAP::Person will not be called'
do
expect
(
Gitlab
::
LDAP
::
Person
)
it
'expects
::
Gitlab::LDAP::Person will not be called'
do
expect
(
::
Gitlab
::
LDAP
::
Person
)
.
not_to
receive
(
:find_by_uid
)
.
with
(
user1
.
username
,
any_args
)
...
...
@@ -414,7 +413,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
# Group 1 link was created above. Create another here.
group2
.
ldap_group_links
.
create
(
cn:
ldap_group
.
cn
,
group_access:
Gitlab
::
Access
::
DEVELOPER
,
group_access:
::
Gitlab
::
Access
::
DEVELOPER
,
provider:
'ldapmain'
)
end
...
...
@@ -429,8 +428,8 @@ describe Gitlab::LDAP::GroupSync, lib: true do
group_sync
.
sync_groups
end
it
'expects Gitlab::LDAP::Person will not be called'
do
expect
(
Gitlab
::
LDAP
::
Person
)
it
'expects
::
Gitlab::LDAP::Person will not be called'
do
expect
(
::
Gitlab
::
LDAP
::
Person
)
.
not_to
receive
(
:find_by_uid
)
.
with
(
user1
.
username
,
any_args
)
...
...
@@ -441,7 +440,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
context
'with groupOfUniqueNames style LDAP group'
do
let
(
:ldap_group
)
do
Gitlab
::
LDAP
::
Group
.
new
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
dn: cn=ldap_group1,ou=groups,dc=example,dc=com
cn: ldap_group1
...
...
@@ -462,7 +461,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
context
'with an empty LDAP group'
do
let
(
:ldap_group
)
do
Gitlab
::
LDAP
::
Group
.
new
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
dn: cn=ldap_group1,ou=groups,dc=example,dc=com
cn: ldap_group1
...
...
@@ -482,7 +481,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
# See gitlab-ee#442 and comment in GroupSync#ensure_full_dns!
context
'with uid=username member format'
do
let
(
:ldap_group
)
do
Gitlab
::
LDAP
::
Group
.
new
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
dn: cn=ldap_group1,ou=groups,dc=example,dc=com
cn: ldap_group1
...
...
@@ -494,7 +493,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
)
end
let
(
:ldap_user
)
do
Gitlab
::
LDAP
::
Person
.
new
(
::
Gitlab
::
LDAP
::
Person
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
"dn: uid=
#{
user1
.
username
}
,ou=users,dc=example,dc=com"
),
...
...
@@ -503,7 +502,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
end
before
do
allow
(
Gitlab
::
LDAP
::
Person
)
allow
(
::
Gitlab
::
LDAP
::
Person
)
.
to
receive
(
:find_by_uid
)
.
with
(
user1
.
username
,
any_args
)
.
and_return
(
ldap_user
)
...
...
@@ -515,8 +514,8 @@ describe Gitlab::LDAP::GroupSync, lib: true do
.
from
(
false
).
to
(
true
)
end
it
'expects Gitlab::LDAP::Person to be called'
do
expect
(
Gitlab
::
LDAP
::
Person
).
to
receive
(
:find_by_uid
)
it
'expects
::
Gitlab::LDAP::Person to be called'
do
expect
(
::
Gitlab
::
LDAP
::
Person
).
to
receive
(
:find_by_uid
)
group_sync
.
sync_groups
end
...
...
@@ -534,7 +533,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
context
'with invalid DNs in the LDAP group'
do
let
(
:ldap_group
)
do
Gitlab
::
LDAP
::
Group
.
new
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
Net
::
LDAP
::
Entry
.
from_single_ldif_string
(
<<-
EOS
.
strip_heredoc
)
dn: cn=ldap_group1,ou=groups,dc=example,dc=com
cn: ldap_group1
...
...
@@ -587,14 +586,14 @@ describe Gitlab::LDAP::GroupSync, lib: true do
user1
.
update_attribute
(
:admin
,
true
)
user3
.
update_attribute
(
:admin
,
true
)
allow_any_instance_of
(
Gitlab
::
LDAP
::
Group
)
allow_any_instance_of
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:adapter
).
and_return
(
adapter
)
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
).
with
(
admin_group
.
cn
,
any_args
)
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'admin_group'
,
kind_of
(
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
admin_group
))
.
with
(
'admin_group'
,
kind_of
(
::
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
admin_group
))
user1
.
identities
.
create
(
provider:
'ldapmain'
,
...
...
@@ -658,16 +657,16 @@ describe Gitlab::LDAP::GroupSync, lib: true do
user3
.
update_attribute
(
:external
,
true
)
user4
.
update_attribute
(
:external
,
true
)
allow_any_instance_of
(
Gitlab
::
LDAP
::
Group
)
allow_any_instance_of
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:adapter
).
and_return
(
adapter
)
allow
(
Gitlab
::
LDAP
::
Group
)
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'external_group1'
,
kind_of
(
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
external_group1
))
allow
(
Gitlab
::
LDAP
::
Group
)
.
with
(
'external_group1'
,
kind_of
(
::
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
external_group1
))
allow
(
EE
::
Gitlab
::
LDAP
::
Group
)
.
to
receive
(
:find_by_cn
)
.
with
(
'external_group2'
,
kind_of
(
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
Gitlab
::
LDAP
::
Group
.
new
(
external_group2
))
.
with
(
'external_group2'
,
kind_of
(
::
Gitlab
::
LDAP
::
Adapter
))
.
and_return
(
EE
::
Gitlab
::
LDAP
::
Group
.
new
(
external_group2
))
user1
.
identities
.
create
(
provider:
'ldapmain'
,
...
...
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