Commit 77dcfee2 authored by Douwe Maan's avatar Douwe Maan

Move EE specific LDAP, OAuth and SAML classes under Gitlab::Auth

parent 07900bac
...@@ -1064,7 +1064,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -1064,7 +1064,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Adds abitlity to render deploy boards in the frontend side. !1233 - Adds abitlity to render deploy boards in the frontend side. !1233
- Add filtered search to MR page. !1243 - Add filtered search to MR page. !1243
- Update project list API returns with approvals_before_merge attribute. !1245 (Geoff Webster) - Update project list API returns with approvals_before_merge attribute. !1245 (Geoff Webster)
- Catch Net::LDAP::DN exceptions in EE::Gitlab::LDAP::Group. !1260 - Catch Net::LDAP::DN exceptions in EE::Gitlab::Auth::LDAP::Group. !1260
- API: Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource. !1274 (Robert Schilling) - API: Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource. !1274 (Robert Schilling)
- API: Remove deprecated fields Notes#upvotes and Notes#downvotes. !1275 (Robert Schilling) - API: Remove deprecated fields Notes#upvotes and Notes#downvotes. !1275 (Robert Schilling)
- Deploy board backend. !1278 - Deploy board backend. !1278
......
...@@ -71,7 +71,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -71,7 +71,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
continue_login_process continue_login_process
end end
rescue Gitlab::Auth::OAuth::SignupDisabledError rescue Gitlab::Auth::OAuth::User::SignupDisabledError
handle_signup_error handle_signup_error
end end
......
...@@ -25,7 +25,7 @@ module SelectsHelper ...@@ -25,7 +25,7 @@ module SelectsHelper
def ldap_server_select_options def ldap_server_select_options
options_from_collection_for_select( options_from_collection_for_select(
Gitlab::LDAP::Config.available_servers, Gitlab::Auth::LDAP::Config.available_servers,
'provider_name', 'provider_name',
'label' 'label'
) )
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
= f.submit 'Save changes', class: "btn btn-save" = f.submit 'Save changes', class: "btn btn-save"
= link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel" = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
- if @group.persisted? && Gitlab::LDAP::Config.group_sync_enabled? - if @group.persisted? && Gitlab::Auth::LDAP::Config.group_sync_enabled?
%h3.page-title LDAP synchronizations %h3.page-title LDAP synchronizations
= render 'ldap_group_links/form', group: @group = render 'ldap_group_links/form', group: @group
= render 'ldap_group_links/ldap_group_links', group: @group = render 'ldap_group_links/ldap_group_links', group: @group
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
= render partial: "namespaces/shared_runner_status", locals: { namespace: @group } = render partial: "namespaces/shared_runner_status", locals: { namespace: @group }
- if Gitlab::LDAP::Config.group_sync_enabled? && @group.ldap_synced? - if Gitlab::Auth::LDAP::Config.group_sync_enabled? && @group.ldap_synced?
.panel.panel-default .panel.panel-default
.panel-heading Active synchronizations .panel-heading Active synchronizations
%ul.well-list %ul.well-list
......
...@@ -327,7 +327,7 @@ step of the sync. ...@@ -327,7 +327,7 @@ step of the sync.
1. Run a group sync for this particular group. 1. Run a group sync for this particular group.
```ruby ```ruby
EE::Gitlab::LDAP::Sync::Group.execute_all_providers(group) EE::Gitlab::Auth::LDAP::Sync::Group.execute_all_providers(group)
``` ```
1. Look through the output of the sync. See [example log output](#example-log-output) 1. Look through the output of the sync. See [example log output](#example-log-output)
below for more information about the output. below for more information about the output.
...@@ -336,11 +336,11 @@ step of the sync. ...@@ -336,11 +336,11 @@ step of the sync.
run the following query: run the following query:
```ruby ```ruby
adapter = Gitlab::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider adapter = Gitlab::Auth::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider
ldap_group = EE::Gitlab::LDAP::Group.find_by_cn('group_cn_here', adapter) ldap_group = EE::Gitlab::Auth::LDAP::Group.find_by_cn('group_cn_here', adapter)
# Output # Output
=> #<EE::Gitlab::LDAP::Group:0x007fcbdd0bb6d8 => #<EE::Gitlab::Auth::LDAP::Group:0x007fcbdd0bb6d8
``` ```
1. Query the LDAP group's member DNs and see if the user's DN is in the list. 1. Query the LDAP group's member DNs and see if the user's DN is in the list.
One of the DNs here should match the 'Identifier' from the LDAP identity One of the DNs here should match the 'Identifier' from the LDAP identity
......
...@@ -17,6 +17,6 @@ class Groups::LdapsController < Groups::ApplicationController ...@@ -17,6 +17,6 @@ class Groups::LdapsController < Groups::ApplicationController
private private
def check_enabled_extras! def check_enabled_extras!
render_404 unless Gitlab::LDAP::Config.group_sync_enabled? render_404 unless Gitlab::Auth::LDAP::Config.group_sync_enabled?
end end
end end
...@@ -29,14 +29,14 @@ class LdapGroupLink < ActiveRecord::Base ...@@ -29,14 +29,14 @@ class LdapGroupLink < ActiveRecord::Base
end end
def config def config
Gitlab::LDAP::Config.new(provider) Gitlab::Auth::LDAP::Config.new(provider)
rescue Gitlab::LDAP::Config::InvalidProvider rescue Gitlab::Auth::LDAP::Config::InvalidProvider
nil nil
end end
# default to the first LDAP server # default to the first LDAP server
def provider def provider
read_attribute(:provider) || Gitlab::LDAP::Config.providers.first read_attribute(:provider) || Gitlab::Auth::LDAP::Config.providers.first
end end
def provider_label def provider_label
......
- if Gitlab::LDAP::Config.group_sync_enabled? && can?(current_user, :admin_ldap_group_links, @group) - if Gitlab::Auth::LDAP::Config.group_sync_enabled? && can?(current_user, :admin_ldap_group_links, @group)
= nav_link(path: 'ldap_group_links#index') do = nav_link(path: 'ldap_group_links#index') do
= link_to group_ldap_group_links_path(@group), title: 'LDAP Group' do = link_to group_ldap_group_links_path(@group), title: 'LDAP Group' do
%span %span
......
...@@ -3,10 +3,10 @@ class LdapAllGroupsSyncWorker ...@@ -3,10 +3,10 @@ class LdapAllGroupsSyncWorker
include CronjobQueue include CronjobQueue
def perform def perform
return unless Gitlab::LDAP::Config.group_sync_enabled? return unless Gitlab::Auth::LDAP::Config.group_sync_enabled?
logger.info 'Started LDAP group sync' logger.info 'Started LDAP group sync'
EE::Gitlab::LDAP::Sync::Groups.execute EE::Gitlab::Auth::LDAP::Sync::Groups.execute
logger.info 'Finished LDAP group sync' logger.info 'Finished LDAP group sync'
end end
end end
...@@ -2,12 +2,12 @@ class LdapGroupSyncWorker ...@@ -2,12 +2,12 @@ class LdapGroupSyncWorker
include ApplicationWorker include ApplicationWorker
def perform(group_ids, provider = nil) def perform(group_ids, provider = nil)
return unless Gitlab::LDAP::Config.group_sync_enabled? return unless Gitlab::Auth::LDAP::Config.group_sync_enabled?
groups = Group.where(id: Array(group_ids)) groups = Group.where(id: Array(group_ids))
if provider if provider
EE::Gitlab::LDAP::Sync::Proxy.open(provider) do |proxy| EE::Gitlab::Auth::LDAP::Sync::Proxy.open(provider) do |proxy|
sync_groups(groups, proxy: proxy) sync_groups(groups, proxy: proxy)
end end
else else
...@@ -23,9 +23,9 @@ class LdapGroupSyncWorker ...@@ -23,9 +23,9 @@ class LdapGroupSyncWorker
logger.info "Started LDAP group sync for group #{group.name} (#{group.id})" logger.info "Started LDAP group sync for group #{group.name} (#{group.id})"
if proxy if proxy
EE::Gitlab::LDAP::Sync::Group.execute(group, proxy) EE::Gitlab::Auth::LDAP::Sync::Group.execute(group, proxy)
else else
EE::Gitlab::LDAP::Sync::Group.execute_all_providers(group) EE::Gitlab::Auth::LDAP::Sync::Group.execute_all_providers(group)
end end
logger.info "Finished LDAP group sync for group #{group.name} (#{group.id})" logger.info "Finished LDAP group sync for group #{group.name} (#{group.id})"
......
...@@ -3,14 +3,14 @@ class LdapSyncWorker ...@@ -3,14 +3,14 @@ class LdapSyncWorker
include CronjobQueue include CronjobQueue
def perform def perform
return unless Gitlab::LDAP::Config.group_sync_enabled? return unless Gitlab::Auth::LDAP::Config.group_sync_enabled?
Rails.logger.info "Performing daily LDAP sync task." Rails.logger.info "Performing daily LDAP sync task."
User.ldap.find_each(batch_size: 100).each do |ldap_user| User.ldap.find_each(batch_size: 100).each do |ldap_user|
Rails.logger.debug "Syncing user #{ldap_user.username}, #{ldap_user.email}" Rails.logger.debug "Syncing user #{ldap_user.username}, #{ldap_user.email}"
# Use the 'update_ldap_group_links_synchronously' option to avoid creating a ton # Use the 'update_ldap_group_links_synchronously' option to avoid creating a ton
# of new Sidekiq jobs all at once. # of new Sidekiq jobs all at once.
Gitlab::LDAP::Access.allowed?(ldap_user, update_ldap_group_links_synchronously: true) Gitlab::Auth::LDAP::Access.allowed?(ldap_user, update_ldap_group_links_synchronously: true)
end end
end end
end end
# rubocop:disable Rails/ReversibleMigration # rubocop:disable Rails/ReversibleMigration
class UpdateGroupLinks < ActiveRecord::Migration class UpdateGroupLinks < ActiveRecord::Migration
def change def change
provider = quote_string(Gitlab::LDAP::Config.providers.first) provider = quote_string(Gitlab::Auth::LDAP::Config.providers.first)
execute("UPDATE ldap_group_links SET provider = '#{provider}' WHERE provider IS NULL") execute("UPDATE ldap_group_links SET provider = '#{provider}' WHERE provider IS NULL")
end end
end end
...@@ -6,7 +6,7 @@ module API ...@@ -6,7 +6,7 @@ module API
helpers do helpers do
def get_group_list(provider, search) def get_group_list(provider, search)
search = Net::LDAP::Filter.escape(search) search = Net::LDAP::Filter.escape(search)
Gitlab::LDAP::Adapter.new(provider).groups("#{search}*", 20) Gitlab::Auth::LDAP::Adapter.new(provider).groups("#{search}*", 20)
end end
params :search_params do params :search_params do
...@@ -21,7 +21,7 @@ module API ...@@ -21,7 +21,7 @@ module API
use :search_params use :search_params
end end
get 'groups' do get 'groups' do
provider = Gitlab::LDAP::Config.available_servers.first['provider_name'] provider = Gitlab::Auth::LDAP::Config.available_servers.first['provider_name']
groups = get_group_list(provider, params[:search]) groups = get_group_list(provider, params[:search])
present groups, with: Entities::LdapGroup present groups, with: Entities::LdapGroup
end end
......
...@@ -30,7 +30,7 @@ module Audit ...@@ -30,7 +30,7 @@ module Audit
when :remove when :remove
"Removed #{value}" "Removed #{value}"
when :failed_login when :failed_login
"Failed to login with #{Gitlab::OAuth::Provider.label_for(value).upcase} authentication" "Failed to login with #{Gitlab::Auth::OAuth::Provider.label_for(value).upcase} authentication"
when :custom_message when :custom_message
value value
else else
......
module EE
module Gitlab
module Auth
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
end
# LDAP connection adapter EE mixin
#
# This module is intended to encapsulate EE-specific adapter methods
# and be **prepended** in the `Gitlab::Auth::LDAP::Adapter` class.
module EE
module Gitlab
module Auth
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),
attributes: %w(dn cn memberuid member submember uniquemember memberof)
}
options[:size] = size if size
ldap_search(options).map do |entry|
LDAP::Group.new(entry, self)
end
end
def group(*args)
groups(*args).first
end
def group_members_in_range(dn, range_start)
ldap_search(
base: dn,
scope: Net::LDAP::SearchScope_BaseObject,
attributes: ["member;range=#{range_start}-*"]
).first
end
def nested_groups(parent_dn)
options = {
base: config.group_base,
filter: Net::LDAP::Filter.join(
Net::LDAP::Filter.eq('objectClass', 'group'),
Net::LDAP::Filter.eq('memberOf', parent_dn)
)
}
ldap_search(options).map do |entry|
LDAP::Group.new(entry, self)
end
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
module Config
extend ActiveSupport::Concern
class_methods do
def group_sync_enabled?
enabled? && ::License.feature_available?(:ldap_group_sync)
end
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
class Group
attr_accessor :adapter
attr_reader :entry
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
@member_uids ||= entry.memberuid.map do |uid|
::Gitlab::Auth::LDAP::Person.normalize_uid(uid)
end
end
delegate :dn, to: :entry
def member_dns(nested_groups_to_skip = [])
dns = []
if active_directory? && adapter
dns.concat(active_directory_members(entry, nested_groups_to_skip))
end
dns.concat(entry_member_dns(entry))
dns.uniq
end
private
# Active Directory range member methods
def has_member_range?(entry)
member_range_attribute(entry).present?
end
def member_range_attribute(entry)
entry.attribute_names.find { |a| a.to_s.start_with?("member;range=")}.to_s
end
def active_directory_members(entry, nested_groups_to_skip)
require 'net/ldap/dn'
members = []
# Retrieve all member pages/ranges
members.concat(ranged_members(entry)) if has_member_range?(entry)
# Process nested group members
members.concat(nested_members(nested_groups_to_skip))
# Clean dns of groups and users outside the base
members.reject! { |dn| nested_groups_to_skip.include?(dn) }
return [] if members.empty?
# Only return members within our given base
members_within_base(members)
end
# AD requires use of range retrieval for groups with more than 1500 members
# cf. https://msdn.microsoft.com/en-us/library/aa367017(v=vs.85).aspx
def ranged_members(entry)
members = []
# Concatenate the members in the current range
dns = entry[member_range_attribute(entry)]
dns = normalize_dns(dns)
members.concat(dns)
# Recursively concatenate members until end of ranges
if has_more_member_ranges?(entry)
next_entry = adapter.group_members_in_range(dn, next_member_range_start(entry))
members.concat(ranged_members(next_entry))
end
members
end
# Process any AD nested groups. Use a manual process because
# the AD recursive member of filter is too slow and uses too
# much CPU on the AD server.
def nested_members(nested_groups_to_skip)
# Ignore this group if we see it again in a nested group.
# Prevents infinite loops.
nested_groups_to_skip << dn
members = []
nested_groups = adapter.nested_groups(dn)
nested_groups.each do |nested_group|
next if nested_groups_to_skip.include?(nested_group.dn)
members.concat(nested_group.member_dns(nested_groups_to_skip))
end
members
end
def has_more_member_ranges?(entry)
next_member_range_start(entry).present?
end
def next_member_range_start(entry)
match = member_range_attribute(entry).match /^member;range=\d+-(\d+|\*)$/
match[1].to_i + 1 if match.present? && match[1] != '*'
end
# The old AD recursive member filter would exclude any members that
# were outside the given search base. To maintain that behavior,
# we need to do the same.
#
# Split the base and each member DN into pairs. Compare the last
# base N pairs of the member DN. If they match, the user is within
# the base DN.
#
# Ex.
# - Member DN: 'uid=user,ou=users,dc=example,dc=com'
# - Base DN: 'dc=example,dc=com'
#
# Base has 2 pairs ([dc,example], [dc,com]). If the last 2 pairs of
# the user DN match, profit!
def members_within_base(members)
begin
base = ::Gitlab::Auth::LDAP::DN.new(adapter.config.base).to_a
rescue ::Gitlab::Auth::LDAP::DN::FormatError => e
Rails.logger.error "Configured LDAP `base` is invalid: '#{adapter.config.base}'. Error: \"#{e.message}\""
return []
end
members.select do |dn|
begin
::Gitlab::Auth::LDAP::DN.new(dn).to_a.last(base.length) == base
rescue ::Gitlab::Auth::LDAP::DN::FormatError => e
Rails.logger.warn "Received invalid member DN from LDAP group '#{cn}': '#{dn}'. Error: \"#{e.message}\". Skipping"
end
end
end
def normalize_dns(dns)
dns.map do |dn|
::Gitlab::Auth::LDAP::Person.normalize_dn(dn)
end
end
def entry_member_dns(entry)
dns = entry.try(:member) || entry.try(:uniquemember) || entry.try(:memberof)
dns&.concat(entry.try(:submember) || [])
if dns
normalize_dns(dns)
else
Rails.logger.warn("Could not find member DNs for LDAP group #{entry.inspect}")
[]
end
end
end
end
end
end
end
require 'net/ldap/dn'
module EE
module Gitlab
module Auth
module LDAP
module Person
extend ActiveSupport::Concern
class_methods do
def find_by_email(email, adapter)
email_attributes = Array(adapter.config.attributes['email'])
email_attributes.each do |possible_attribute|
found_user = adapter.user(possible_attribute, email)
return found_user if found_user
end
nil
end
def find_by_kerberos_principal(principal, adapter)
uid, domain = principal.split('@', 2)
return nil unless uid && domain
# In multi-forest setups, there may be several users with matching
# uids but differing DNs, so skip adapters configured to connect to
# non-matching domains
return unless domain.casecmp(domain_from_dn(adapter.config.base)) == 0
find_by_uid(uid, adapter)
end
# Extracts the rightmost unbroken set of domain components from an
# LDAP DN and constructs a domain name from them
def domain_from_dn(dn)
dn_components = []
::Gitlab::Auth::LDAP::DN.new(dn).each_pair { |name, value| dn_components << { name: name, value: value } }
dn_components
.reverse
.take_while { |rdn| rdn[:name].casecmp('DC').zero? } # Domain Component
.map { |rdn| rdn[:value] }
.reverse
.join('.')
end
def ldap_attributes(config)
attributes = super + [
'memberof',
(config.sync_ssh_keys if config.sync_ssh_keys.is_a?(String))
]
attributes.compact.uniq
end
end
def ssh_keys
if config.sync_ssh_keys? && entry.respond_to?(config.sync_ssh_keys)
entry[config.sync_ssh_keys.to_sym]
.map { |key| key[/(ssh|ecdsa)-[^ ]+ [^\s]+/] }
.compact
else
[]
end
end
# We assume that the Kerberos username matches the configured uid
# attribute in LDAP. For Active Directory, this is `sAMAccountName`
def kerberos_principal
return nil unless uid
uid + '@' + self.class.domain_from_dn(dn).upcase
end
def memberof
return [] unless entry.attribute_names.include?(:memberof)
entry.memberof
end
def group_cns
memberof.map { |memberof_value| cn_from_memberof(memberof_value) }
end
def cn_from_memberof(memberof)
# Only get the first CN value of the string, that's the one that contains
# the group name
memberof.match(/(?:cn=([\w\s]+))/i)&.captures&.first
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
module Sync
class AdminUsers
attr_reader :provider, :proxy
def self.execute(proxy)
self.new(proxy).update_permissions
end
def initialize(proxy)
@provider = proxy.provider
@proxy = proxy
end
def update_permissions
return if admin_group.empty?
admin_group_member_dns = proxy.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::Auth::LDAP::User.find_by_uid_and_provider(member_dn, provider)
if user.present?
user.admin = true
user.save
verified_admin_users << user
else
Rails.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
private
def admin_group
proxy.adapter.config.admin_group
end
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
module Sync
class ExternalUsers
attr_reader :provider, :proxy
def self.execute(proxy)
self.new(proxy).update_permissions
end
def initialize(proxy)
@provider = proxy.provider
@proxy = proxy
end
def update_permissions
return unless external_groups.any?
current_external_users = ::User.external.with_provider(provider)
verified_external_users = []
external_groups.each do |group|
group_dns = proxy.dns_for_group_cn(group)
group_dns.each do |member_dn|
user = ::Gitlab::Auth::LDAP::User.find_by_uid_and_provider(member_dn, provider)
if user.present?
user.external = true
user.save
verified_external_users << user
else
Rails.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
# Restore normal access to users no longer found in the external groups
current_external_users.each do |user|
unless verified_external_users.include?(user)
user.external = false
user.save
end
end
end
private
def external_groups
proxy.adapter.config.external_groups
end
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
module Sync
class Group
attr_reader :provider, :group, :proxy
class << self
# Sync members across all providers for the given group.
def execute_all_providers(group)
return unless ldap_sync_ready?(group)
group.start_ldap_sync
Rails.logger.debug { "Started syncing all providers for '#{group.name}' group" }
# 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::Auth::LDAP::Config.providers.shuffle.each do |provider|
Sync::Proxy.open(provider) do |proxy|
new(group, proxy).update_permissions
end
end
group.finish_ldap_sync
Rails.logger.debug { "Finished syncing all providers for '#{group.name}' group" }
end
# Sync members across a single provider for the given group.
def execute(group, proxy)
return unless ldap_sync_ready?(group)
group.start_ldap_sync
Rails.logger.debug { "Started syncing '#{proxy.provider}' provider for '#{group.name}' group" }
sync_group = new(group, proxy)
sync_group.update_permissions
group.finish_ldap_sync
Rails.logger.debug { "Finished syncing '#{proxy.provider}' provider for '#{group.name}' group" }
end
def ldap_sync_ready?(group)
fail_stuck_group(group)
return true unless group.ldap_sync_started?
Rails.logger.warn "Group '#{group.name}' is not ready for LDAP sync. Skipping"
false
end
def fail_stuck_group(group)
return unless group.ldap_sync_started?
if group.ldap_sync_last_sync_at < 1.hour.ago
group.mark_ldap_sync_as_failed('The sync took too long to complete.')
end
end
end
def initialize(group, proxy)
@provider = proxy.provider
@group = group
@proxy = proxy
end
def update_permissions
unless group.ldap_sync_started?
logger.warn "Group '#{group.name}' LDAP sync status must be 'started' before updating permissions"
return
end
access_levels = AccessLevels.new
# Only iterate over group links for the current provider
group.ldap_group_links.with_provider(provider).each do |group_link|
next unless group_link.active?
update_access_levels(access_levels, group_link)
end
update_existing_group_membership(group, access_levels)
add_new_members(group, access_levels)
end
private
def update_access_levels(access_levels, group_link)
if member_dns = get_member_dns(group_link)
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
def get_member_dns(group_link)
group_link.cn ? dns_for_group_cn(group_link.cn) : UserFilter.filter(@proxy, group_link.filter)
end
def dns_for_group_cn(group_cn)
if config.group_base.blank?
logger.debug { "No `group_base` configured for '#{provider}' provider and group link CN #{group_cn}. Skipping" }
return nil
end
proxy.dns_for_group_cn(group_cn)
end
def dn_for_uid(uid)
proxy.dn_for_uid(uid)
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.downcase
# 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]
# Skip validations and callbacks. We have a limited set of attrs
# due to the `select` lookup, and we need to be efficient.
# Low risk, because the member should already be valid.
member.update_column(:ldap, true) unless member.ldap?
# 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?
# Delete this entry from the hash now that we're acting on it
access_levels.delete(member_dn)
next if member.ldap? && member.override?
add_or_update_user_membership(
user,
group,
desired_access
)
elsif group.last_owner?(user)
warn_cannot_remove_last_owner(user, group)
else
group.users.destroy(user)
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::Auth::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, current_user: nil)
# 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_user(
user,
access,
current_user: current_user,
ldap: 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(:id, :access_level, :user_id, :ldap, :override)
.with_identity_provider(provider).preload(:user)
end
def logger
Rails.logger
end
def config
@proxy.adapter.config
end
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
module Sync
class Groups
attr_reader :provider, :proxy
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::Auth::LDAP::Config.providers.shuffle.each do |provider|
Sync::Proxy.open(provider) do |proxy|
group_sync = self.new(proxy)
group_sync.update_permissions
end
end
true
end
def initialize(proxy)
@provider = proxy.provider
@proxy = proxy
end
def update_permissions
logger.debug { "Performing LDAP group sync for '#{provider}' provider" }
sync_groups
logger.debug { "Finished LDAP group sync for '#{provider}' provider" }
if config.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 config.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
private
def sync_groups
groups_where_group_links_with_provider_ordered.each do |group|
Sync::Group.execute(group, proxy)
end
end
def sync_admin_users
Sync::AdminUsers.execute(proxy)
end
def sync_external_users
Sync::ExternalUsers.execute(proxy)
end
def groups_where_group_links_with_provider_ordered
::Group.where_group_links_with_provider(provider)
.preload(:ldap_group_links)
.reorder('ldap_sync_last_successful_update_at ASC, namespaces.id ASC')
.distinct
end
def config
proxy.adapter.config
end
def logger
Rails.logger
end
end
end
end
end
end
end
require 'net/ldap/dn'
module EE
module Gitlab
module Auth
module LDAP
module Sync
class Proxy
attr_reader :provider, :adapter
# Open a connection and run all queries through it.
# It's more efficient than the default of opening/closing per LDAP query.
def self.open(provider, &block)
::Gitlab::Auth::LDAP::Adapter.open(provider) do |adapter|
block.call(self.new(provider, adapter))
end
end
def initialize(provider, adapter)
@adapter = adapter
@provider = provider
end
# 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
private
def ldap_group_member_dns(ldap_group_cn)
ldap_group = 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
# Various lookups in this method could return `nil` values.
# Compact the array to remove those entries
member_dns.compact!
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
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 = ::Gitlab::Auth::LDAP::DN.new(dn).to_a
rescue ::Gitlab::Auth::LDAP::DN::FormatError => e
logger.error { "Found malformed DN: '#{dn}'. Skipping. Error: \"#{e.message}\"" }
next
end
final_dn =
# 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.count == 0
logger.warn { "Found null DN. Skipping." }
nil
elsif parsed_dn[0] == 'uid'
dn_for_uid(parsed_dn[1])
else
logger.warn { "Found potentially malformed/incomplete DN: '#{dn}'" }
dn
end
clean_encoding(final_dn)
end
# Remove `nil` values generated by the rescue above.
dns.compact!
end
# net-ldap only returns ASCII-8BIT and does not support UTF-8 out-of-the-box:
# https://github.com/ruby-ldap/ruby-net-ldap/issues/4
def clean_encoding(dn)
return dn unless dn.present?
dn.force_encoding('UTF-8')
rescue
dn
end
def member_uid_to_dn(uid)
identity = ::Identity.with_secondary_extern_uid(provider, uid).take
if identity.present?
# Use the DN on record in GitLab when it's available
identity.extern_uid
else
ldap_user = ::Gitlab::Auth::LDAP::Person.find_by_uid(uid, adapter)
# Can't find a matching user
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.with_extern_uid(provider, dn).take
# User may not exist in GitLab yet. Skip.
return unless identity.present?
identity.secondary_extern_uid = uid
identity.save
end
def logger
Rails.logger
end
end
end
end
end
end
end
# LDAP User EE mixin
#
# This module is intended to encapsulate EE-specific User methods
# and be **prepended** in the `Gitlab::Auth::LDAP::User` class.
module EE
module Gitlab
module Auth
module LDAP
module User
def initialize(auth_hash)
super
set_external_with_external_groups
end
private
# Intended to be called during #initialize, and #save should be called
# after initialize.
def set_external_with_external_groups
return if ldap_config.external_groups.empty?
gl_user.external = in_any_external_group?
end
# Returns true if the User is found in an external group listed in the
# config.
def in_any_external_group?
with_proxy do |proxy|
external_groups = proxy.adapter.config.external_groups
external_groups.any? do |group_cn|
in_group?(group_cn, proxy)
end
end
end
# Returns true if the User is a member of the group.
def in_group?(group_cn, proxy)
member_dns = proxy.dns_for_group_cn(group_cn)
member_dns.include?(auth_hash.uid)
end
def with_proxy(&block)
::EE::Gitlab::Auth::LDAP::Sync::Proxy.open(auth_hash.provider, &block)
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module LDAP
class UserFilter
def self.filter(*args)
new(*args).filter
end
def initialize(proxy, filter)
@proxy = proxy
@filter = filter
end
def filter
logger.debug "Running filter #{@filter} against #{@proxy.provider}"
@proxy.adapter.ldap_search(options).map(&:dn).tap do |dns|
logger.debug "Found #{dns.count} mathing users for filter #{@filter}"
end
end
private
def options
{ base: config.base, filter: construct_filter }
end
def construct_filter
Net::LDAP::Filter.construct(@filter)
end
def config
@proxy.adapter.config
end
def logger
Rails.logger
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module OAuth
module AuthHash
include ::Gitlab::Utils::StrongMemoize
def kerberos_default_realm
::Gitlab::Kerberos::Authentication.kerberos_default_realm
end
def uid
strong_memoize(:ee_uid) do
ee_uid = super
# For Kerberos, usernames `principal` and `principal@DEFAULT.REALM`
# are equivalent and may be used indifferently, but omniauth_kerberos
# does not normalize them as of version 0.3.0, so add the default
# realm ourselves if appropriate
if provider == 'kerberos' && ee_uid.present?
ee_uid += "@#{kerberos_default_realm}" unless ee_uid.include?('@')
end
ee_uid
end
end
end
end
end
end
end
module EE
module Gitlab
module Auth
module OAuth
module User
protected
def find_ldap_person(auth_hash, adapter)
if auth_hash.provider == 'kerberos'
::Gitlab::Auth::LDAP::Person.find_by_kerberos_principal(auth_hash.uid, adapter)
else
super
end
end
end
end
end
end
end
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
# LDAP connection adapter EE mixin
#
# This module is intended to encapsulate EE-specific adapter methods
# and be **prepended** 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),
attributes: %w(dn cn memberuid member submember uniquemember memberof)
}
options[:size] = size if size
ldap_search(options).map do |entry|
LDAP::Group.new(entry, self)
end
end
def group(*args)
groups(*args).first
end
def group_members_in_range(dn, range_start)
ldap_search(
base: dn,
scope: Net::LDAP::SearchScope_BaseObject,
attributes: ["member;range=#{range_start}-*"]
).first
end
def nested_groups(parent_dn)
options = {
base: config.group_base,
filter: Net::LDAP::Filter.join(
Net::LDAP::Filter.eq('objectClass', 'group'),
Net::LDAP::Filter.eq('memberOf', parent_dn)
)
}
ldap_search(options).map do |entry|
LDAP::Group.new(entry, self)
end
end
end
end
end
end
module EE
module Gitlab
module LDAP
module Config
extend ActiveSupport::Concern
class_methods do
def group_sync_enabled?
enabled? && ::License.feature_available?(:ldap_group_sync)
end
end
end
end
end
end
module EE
module Gitlab
module LDAP
class Group
attr_accessor :adapter
attr_reader :entry
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
@member_uids ||= entry.memberuid.map do |uid|
::Gitlab::LDAP::Person.normalize_uid(uid)
end
end
delegate :dn, to: :entry
def member_dns(nested_groups_to_skip = [])
dns = []
if active_directory? && adapter
dns.concat(active_directory_members(entry, nested_groups_to_skip))
end
dns.concat(entry_member_dns(entry))
dns.uniq
end
private
# Active Directory range member methods
def has_member_range?(entry)
member_range_attribute(entry).present?
end
def member_range_attribute(entry)
entry.attribute_names.find { |a| a.to_s.start_with?("member;range=")}.to_s
end
def active_directory_members(entry, nested_groups_to_skip)
require 'net/ldap/dn'
members = []
# Retrieve all member pages/ranges
members.concat(ranged_members(entry)) if has_member_range?(entry)
# Process nested group members
members.concat(nested_members(nested_groups_to_skip))
# Clean dns of groups and users outside the base
members.reject! { |dn| nested_groups_to_skip.include?(dn) }
return [] if members.empty?
# Only return members within our given base
members_within_base(members)
end
# AD requires use of range retrieval for groups with more than 1500 members
# cf. https://msdn.microsoft.com/en-us/library/aa367017(v=vs.85).aspx
def ranged_members(entry)
members = []
# Concatenate the members in the current range
dns = entry[member_range_attribute(entry)]
dns = normalize_dns(dns)
members.concat(dns)
# Recursively concatenate members until end of ranges
if has_more_member_ranges?(entry)
next_entry = adapter.group_members_in_range(dn, next_member_range_start(entry))
members.concat(ranged_members(next_entry))
end
members
end
# Process any AD nested groups. Use a manual process because
# the AD recursive member of filter is too slow and uses too
# much CPU on the AD server.
def nested_members(nested_groups_to_skip)
# Ignore this group if we see it again in a nested group.
# Prevents infinite loops.
nested_groups_to_skip << dn
members = []
nested_groups = adapter.nested_groups(dn)
nested_groups.each do |nested_group|
next if nested_groups_to_skip.include?(nested_group.dn)
members.concat(nested_group.member_dns(nested_groups_to_skip))
end
members
end
def has_more_member_ranges?(entry)
next_member_range_start(entry).present?
end
def next_member_range_start(entry)
match = member_range_attribute(entry).match /^member;range=\d+-(\d+|\*)$/
match[1].to_i + 1 if match.present? && match[1] != '*'
end
# The old AD recursive member filter would exclude any members that
# were outside the given search base. To maintain that behavior,
# we need to do the same.
#
# Split the base and each member DN into pairs. Compare the last
# base N pairs of the member DN. If they match, the user is within
# the base DN.
#
# Ex.
# - Member DN: 'uid=user,ou=users,dc=example,dc=com'
# - Base DN: 'dc=example,dc=com'
#
# Base has 2 pairs ([dc,example], [dc,com]). If the last 2 pairs of
# the user DN match, profit!
def members_within_base(members)
begin
base = ::Gitlab::LDAP::DN.new(adapter.config.base).to_a
rescue ::Gitlab::LDAP::DN::FormatError => e
Rails.logger.error "Configured LDAP `base` is invalid: '#{adapter.config.base}'. Error: \"#{e.message}\""
return []
end
members.select do |dn|
begin
::Gitlab::LDAP::DN.new(dn).to_a.last(base.length) == base
rescue ::Gitlab::LDAP::DN::FormatError => e
Rails.logger.warn "Received invalid member DN from LDAP group '#{cn}': '#{dn}'. Error: \"#{e.message}\". Skipping"
end
end
end
def normalize_dns(dns)
dns.map do |dn|
::Gitlab::LDAP::Person.normalize_dn(dn)
end
end
def entry_member_dns(entry)
dns = entry.try(:member) || entry.try(:uniquemember) || entry.try(:memberof)
dns&.concat(entry.try(:submember) || [])
if dns
normalize_dns(dns)
else
Rails.logger.warn("Could not find member DNs for LDAP group #{entry.inspect}")
[]
end
end
end
end
end
end
require 'net/ldap/dn'
module EE
module Gitlab
module LDAP
module Person
extend ActiveSupport::Concern
class_methods do
def find_by_email(email, adapter)
email_attributes = Array(adapter.config.attributes['email'])
email_attributes.each do |possible_attribute|
found_user = adapter.user(possible_attribute, email)
return found_user if found_user
end
nil
end
def find_by_kerberos_principal(principal, adapter)
uid, domain = principal.split('@', 2)
return nil unless uid && domain
# In multi-forest setups, there may be several users with matching
# uids but differing DNs, so skip adapters configured to connect to
# non-matching domains
return unless domain.casecmp(domain_from_dn(adapter.config.base)) == 0
find_by_uid(uid, adapter)
end
# Extracts the rightmost unbroken set of domain components from an
# LDAP DN and constructs a domain name from them
def domain_from_dn(dn)
dn_components = []
::Gitlab::LDAP::DN.new(dn).each_pair { |name, value| dn_components << { name: name, value: value } }
dn_components
.reverse
.take_while { |rdn| rdn[:name].casecmp('DC').zero? } # Domain Component
.map { |rdn| rdn[:value] }
.reverse
.join('.')
end
def ldap_attributes(config)
attributes = super + [
'memberof',
(config.sync_ssh_keys if config.sync_ssh_keys.is_a?(String))
]
attributes.compact.uniq
end
end
def ssh_keys
if config.sync_ssh_keys? && entry.respond_to?(config.sync_ssh_keys)
entry[config.sync_ssh_keys.to_sym]
.map { |key| key[/(ssh|ecdsa)-[^ ]+ [^\s]+/] }
.compact
else
[]
end
end
# We assume that the Kerberos username matches the configured uid
# attribute in LDAP. For Active Directory, this is `sAMAccountName`
def kerberos_principal
return nil unless uid
uid + '@' + self.class.domain_from_dn(dn).upcase
end
def memberof
return [] unless entry.attribute_names.include?(:memberof)
entry.memberof
end
def group_cns
memberof.map { |memberof_value| cn_from_memberof(memberof_value) }
end
def cn_from_memberof(memberof)
# Only get the first CN value of the string, that's the one that contains
# the group name
memberof.match(/(?:cn=([\w\s]+))/i)&.captures&.first
end
end
end
end
end
module EE
module Gitlab
module LDAP
module Sync
class AdminUsers
attr_reader :provider, :proxy
def self.execute(proxy)
self.new(proxy).update_permissions
end
def initialize(proxy)
@provider = proxy.provider
@proxy = proxy
end
def update_permissions
return if admin_group.empty?
admin_group_member_dns = proxy.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
Rails.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
private
def admin_group
proxy.adapter.config.admin_group
end
end
end
end
end
end
module EE
module Gitlab
module LDAP
module Sync
class ExternalUsers
attr_reader :provider, :proxy
def self.execute(proxy)
self.new(proxy).update_permissions
end
def initialize(proxy)
@provider = proxy.provider
@proxy = proxy
end
def update_permissions
return unless external_groups.any?
current_external_users = ::User.external.with_provider(provider)
verified_external_users = []
external_groups.each do |group|
group_dns = proxy.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
Rails.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
# Restore normal access to users no longer found in the external groups
current_external_users.each do |user|
unless verified_external_users.include?(user)
user.external = false
user.save
end
end
end
private
def external_groups
proxy.adapter.config.external_groups
end
end
end
end
end
end
module EE
module Gitlab
module LDAP
module Sync
class Group
attr_reader :provider, :group, :proxy
class << self
# Sync members across all providers for the given group.
def execute_all_providers(group)
return unless ldap_sync_ready?(group)
group.start_ldap_sync
Rails.logger.debug { "Started syncing all providers for '#{group.name}' group" }
# 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|
Sync::Proxy.open(provider) do |proxy|
new(group, proxy).update_permissions
end
end
group.finish_ldap_sync
Rails.logger.debug { "Finished syncing all providers for '#{group.name}' group" }
end
# Sync members across a single provider for the given group.
def execute(group, proxy)
return unless ldap_sync_ready?(group)
group.start_ldap_sync
Rails.logger.debug { "Started syncing '#{proxy.provider}' provider for '#{group.name}' group" }
sync_group = new(group, proxy)
sync_group.update_permissions
group.finish_ldap_sync
Rails.logger.debug { "Finished syncing '#{proxy.provider}' provider for '#{group.name}' group" }
end
def ldap_sync_ready?(group)
fail_stuck_group(group)
return true unless group.ldap_sync_started?
Rails.logger.warn "Group '#{group.name}' is not ready for LDAP sync. Skipping"
false
end
def fail_stuck_group(group)
return unless group.ldap_sync_started?
if group.ldap_sync_last_sync_at < 1.hour.ago
group.mark_ldap_sync_as_failed('The sync took too long to complete.')
end
end
end
def initialize(group, proxy)
@provider = proxy.provider
@group = group
@proxy = proxy
end
def update_permissions
unless group.ldap_sync_started?
logger.warn "Group '#{group.name}' LDAP sync status must be 'started' before updating permissions"
return
end
access_levels = AccessLevels.new
# Only iterate over group links for the current provider
group.ldap_group_links.with_provider(provider).each do |group_link|
next unless group_link.active?
update_access_levels(access_levels, group_link)
end
update_existing_group_membership(group, access_levels)
add_new_members(group, access_levels)
end
private
def update_access_levels(access_levels, group_link)
if member_dns = get_member_dns(group_link)
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
def get_member_dns(group_link)
group_link.cn ? dns_for_group_cn(group_link.cn) : UserFilter.filter(@proxy, group_link.filter)
end
def dns_for_group_cn(group_cn)
if config.group_base.blank?
logger.debug { "No `group_base` configured for '#{provider}' provider and group link CN #{group_cn}. Skipping" }
return nil
end
proxy.dns_for_group_cn(group_cn)
end
def dn_for_uid(uid)
proxy.dn_for_uid(uid)
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.downcase
# 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]
# Skip validations and callbacks. We have a limited set of attrs
# due to the `select` lookup, and we need to be efficient.
# Low risk, because the member should already be valid.
member.update_column(:ldap, true) unless member.ldap?
# 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?
# Delete this entry from the hash now that we're acting on it
access_levels.delete(member_dn)
next if member.ldap? && member.override?
add_or_update_user_membership(
user,
group,
desired_access
)
elsif group.last_owner?(user)
warn_cannot_remove_last_owner(user, group)
else
group.users.destroy(user)
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, current_user: nil)
# 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_user(
user,
access,
current_user: current_user,
ldap: 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(:id, :access_level, :user_id, :ldap, :override)
.with_identity_provider(provider).preload(:user)
end
def logger
Rails.logger
end
def config
@proxy.adapter.config
end
end
end
end
end
end
module EE
module Gitlab
module LDAP
module Sync
class Groups
attr_reader :provider, :proxy
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|
Sync::Proxy.open(provider) do |proxy|
group_sync = self.new(proxy)
group_sync.update_permissions
end
end
true
end
def initialize(proxy)
@provider = proxy.provider
@proxy = proxy
end
def update_permissions
logger.debug { "Performing LDAP group sync for '#{provider}' provider" }
sync_groups
logger.debug { "Finished LDAP group sync for '#{provider}' provider" }
if config.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 config.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
private
def sync_groups
groups_where_group_links_with_provider_ordered.each do |group|
Sync::Group.execute(group, proxy)
end
end
def sync_admin_users
Sync::AdminUsers.execute(proxy)
end
def sync_external_users
Sync::ExternalUsers.execute(proxy)
end
def groups_where_group_links_with_provider_ordered
::Group.where_group_links_with_provider(provider)
.preload(:ldap_group_links)
.reorder('ldap_sync_last_successful_update_at ASC, namespaces.id ASC')
.distinct
end
def config
proxy.adapter.config
end
def logger
Rails.logger
end
end
end
end
end
end
require 'net/ldap/dn'
module EE
module Gitlab
module LDAP
module Sync
class Proxy
attr_reader :provider, :adapter
# Open a connection and 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 initialize(provider, adapter)
@adapter = adapter
@provider = provider
end
# 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
private
def ldap_group_member_dns(ldap_group_cn)
ldap_group = 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
# Various lookups in this method could return `nil` values.
# Compact the array to remove those entries
member_dns.compact!
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
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 = ::Gitlab::LDAP::DN.new(dn).to_a
rescue ::Gitlab::LDAP::DN::FormatError => e
logger.error { "Found malformed DN: '#{dn}'. Skipping. Error: \"#{e.message}\"" }
next
end
final_dn =
# 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.count == 0
logger.warn { "Found null DN. Skipping." }
nil
elsif parsed_dn[0] == 'uid'
dn_for_uid(parsed_dn[1])
else
logger.warn { "Found potentially malformed/incomplete DN: '#{dn}'" }
dn
end
clean_encoding(final_dn)
end
# Remove `nil` values generated by the rescue above.
dns.compact!
end
# net-ldap only returns ASCII-8BIT and does not support UTF-8 out-of-the-box:
# https://github.com/ruby-ldap/ruby-net-ldap/issues/4
def clean_encoding(dn)
return dn unless dn.present?
dn.force_encoding('UTF-8')
rescue
dn
end
def member_uid_to_dn(uid)
identity = ::Identity.with_secondary_extern_uid(provider, uid).take
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
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.with_extern_uid(provider, dn).take
# User may not exist in GitLab yet. Skip.
return unless identity.present?
identity.secondary_extern_uid = uid
identity.save
end
def logger
Rails.logger
end
end
end
end
end
end
# LDAP User EE mixin
#
# This module is intended to encapsulate EE-specific User methods
# and be **prepended** in the `Gitlab::LDAP::User` class.
module EE
module Gitlab
module LDAP
module User
def initialize(auth_hash)
super
set_external_with_external_groups
end
private
# Intended to be called during #initialize, and #save should be called
# after initialize.
def set_external_with_external_groups
return if ldap_config.external_groups.empty?
gl_user.external = in_any_external_group?
end
# Returns true if the User is found in an external group listed in the
# config.
def in_any_external_group?
with_proxy do |proxy|
external_groups = proxy.adapter.config.external_groups
external_groups.any? do |group_cn|
in_group?(group_cn, proxy)
end
end
end
# Returns true if the User is a member of the group.
def in_group?(group_cn, proxy)
member_dns = proxy.dns_for_group_cn(group_cn)
member_dns.include?(auth_hash.uid)
end
def with_proxy(&block)
::EE::Gitlab::LDAP::Sync::Proxy.open(auth_hash.provider, &block)
end
end
end
end
end
module EE
module Gitlab
module LDAP
class UserFilter
def self.filter(*args)
new(*args).filter
end
def initialize(proxy, filter)
@proxy = proxy
@filter = filter
end
def filter
logger.debug "Running filter #{@filter} against #{@proxy.provider}"
@proxy.adapter.ldap_search(options).map(&:dn).tap do |dns|
logger.debug "Found #{dns.count} mathing users for filter #{@filter}"
end
end
private
def options
{ base: config.base, filter: construct_filter }
end
def construct_filter
Net::LDAP::Filter.construct(@filter)
end
def config
@proxy.adapter.config
end
def logger
Rails.logger
end
end
end
end
end
module EE
module Gitlab
module OAuth
module AuthHash
include ::Gitlab::Utils::StrongMemoize
def kerberos_default_realm
::Gitlab::Kerberos::Authentication.kerberos_default_realm
end
def uid
strong_memoize(:ee_uid) do
ee_uid = super
# For Kerberos, usernames `principal` and `principal@DEFAULT.REALM`
# are equivalent and may be used indifferently, but omniauth_kerberos
# does not normalize them as of version 0.3.0, so add the default
# realm ourselves if appropriate
if provider == 'kerberos' && ee_uid.present?
ee_uid += "@#{kerberos_default_realm}" unless ee_uid.include?('@')
end
ee_uid
end
end
end
end
end
end
module EE
module Gitlab
module OAuth
module User
protected
def find_ldap_person(auth_hash, adapter)
if auth_hash.provider == 'kerberos'
::Gitlab::LDAP::Person.find_by_kerberos_principal(auth_hash.uid, adapter)
else
super
end
end
end
end
end
end
desc "GITLAB | migrate provider names to multiple ldap setup" desc "GITLAB | migrate provider names to multiple ldap setup"
namespace :gitlab do namespace :gitlab do
task migrate_ldap_providers: :environment do task migrate_ldap_providers: :environment do
config = Gitlab::LDAP::Config config = Gitlab::Auth::LDAP::Config
raise 'No LDAP server hash defined. See config/gitlab.yml.example for an example' unless config.servers.any? raise 'No LDAP server hash defined. See config/gitlab.yml.example for an example' unless config.servers.any?
provider = config.servers.first['provider_name'] provider = config.servers.first['provider_name']
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::AccessLevels do describe EE::Gitlab::Auth::LDAP::AccessLevels do
describe '#set' do describe '#set' do
let(:access_levels) { described_class.new } let(:access_levels) { described_class.new }
let(:dns) do let(:dns) do
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::Group do describe EE::Gitlab::Auth::LDAP::Group do
include LdapHelpers include LdapHelpers
before do before do
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::Sync::AdminUsers do describe EE::Gitlab::Auth::LDAP::Sync::AdminUsers do
include LdapHelpers include LdapHelpers
let(:adapter) { ldap_adapter } let(:adapter) { ldap_adapter }
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::Sync::ExternalUsers do describe EE::Gitlab::Auth::LDAP::Sync::ExternalUsers do
include LdapHelpers include LdapHelpers
describe '#update_permissions' do describe '#update_permissions' do
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::Sync::Group do describe EE::Gitlab::Auth::LDAP::Sync::Group do
include LdapHelpers include LdapHelpers
let(:adapter) { ldap_adapter } let(:adapter) { ldap_adapter }
...@@ -69,7 +69,7 @@ describe EE::Gitlab::LDAP::Sync::Group do ...@@ -69,7 +69,7 @@ describe EE::Gitlab::LDAP::Sync::Group do
adapter = ldap_adapter('main') adapter = ldap_adapter('main')
proxy = proxy(adapter, 'main') proxy = proxy(adapter, 'main')
allow(EE::Gitlab::LDAP::Sync::Proxy).to receive(:open).and_yield(proxy) allow(EE::Gitlab::Auth::LDAP::Sync::Proxy).to receive(:open).and_yield(proxy)
end end
let(:group) do let(:group) do
...@@ -303,7 +303,7 @@ describe EE::Gitlab::LDAP::Sync::Group do ...@@ -303,7 +303,7 @@ describe EE::Gitlab::LDAP::Sync::Group do
it 'does not update permissions when group base is missing' do it 'does not update permissions when group base is missing' do
stub_ldap_config(group_base: nil) stub_ldap_config(group_base: nil)
expect_any_instance_of(EE::Gitlab::LDAP::Sync::Proxy).not_to receive(:dns_for_group_cn) expect_any_instance_of(EE::Gitlab::Auth::LDAP::Sync::Proxy).not_to receive(:dns_for_group_cn)
sync_group.update_permissions sync_group.update_permissions
end end
...@@ -390,7 +390,7 @@ describe EE::Gitlab::LDAP::Sync::Group do ...@@ -390,7 +390,7 @@ describe EE::Gitlab::LDAP::Sync::Group do
# Safe-check because some permissions are removed when `Group#ldap_synced?` # Safe-check because some permissions are removed when `Group#ldap_synced?`
# is true (e.g. in `GroupPolicy`). # is true (e.g. in `GroupPolicy`).
expect(group).to be_ldap_synced expect(group).to be_ldap_synced
allow(EE::Gitlab::LDAP::UserFilter).to receive(:filter).and_return([user_dn(user.username)]) allow(EE::Gitlab::Auth::LDAP::UserFilter).to receive(:filter).and_return([user_dn(user.username)])
group.start_ldap_sync group.start_ldap_sync
end end
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::Sync::Groups do describe EE::Gitlab::Auth::LDAP::Sync::Groups do
include LdapHelpers include LdapHelpers
let(:adapter) { ldap_adapter } let(:adapter) { ldap_adapter }
...@@ -8,9 +8,9 @@ describe EE::Gitlab::LDAP::Sync::Groups do ...@@ -8,9 +8,9 @@ describe EE::Gitlab::LDAP::Sync::Groups do
describe '#update_permissions' do describe '#update_permissions' do
before do before do
allow(EE::Gitlab::LDAP::Sync::Group).to receive(:execute) allow(EE::Gitlab::Auth::LDAP::Sync::Group).to receive(:execute)
allow(EE::Gitlab::LDAP::Sync::AdminUsers).to receive(:execute) allow(EE::Gitlab::Auth::LDAP::Sync::AdminUsers).to receive(:execute)
allow(EE::Gitlab::LDAP::Sync::ExternalUsers).to receive(:execute) allow(EE::Gitlab::Auth::LDAP::Sync::ExternalUsers).to receive(:execute)
2.times { create(:group_with_ldap_group_link) } 2.times { create(:group_with_ldap_group_link) }
end end
...@@ -24,12 +24,12 @@ describe EE::Gitlab::LDAP::Sync::Groups do ...@@ -24,12 +24,12 @@ describe EE::Gitlab::LDAP::Sync::Groups do
stub_ldap_config(group_base: nil) stub_ldap_config(group_base: nil)
end end
it 'does not call EE::Gitlab::LDAP::Sync::AdminUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::AdminUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::AdminUsers).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::AdminUsers).not_to receive(:execute)
end end
it 'does not call EE::Gitlab::LDAP::Sync::ExternalUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::ExternalUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::ExternalUsers).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::ExternalUsers).not_to receive(:execute)
end end
end end
...@@ -39,16 +39,16 @@ describe EE::Gitlab::LDAP::Sync::Groups do ...@@ -39,16 +39,16 @@ describe EE::Gitlab::LDAP::Sync::Groups do
stub_ldap_config(group_base: 'dc=example,dc=com') stub_ldap_config(group_base: 'dc=example,dc=com')
end end
it 'calls EE::Gitlab::LDAP::Sync::Group#execute' do it 'calls EE::Gitlab::Auth::LDAP::Sync::Group#execute' do
expect(EE::Gitlab::LDAP::Sync::Group).to receive(:execute).twice expect(EE::Gitlab::Auth::LDAP::Sync::Group).to receive(:execute).twice
end end
it 'does not call EE::Gitlab::LDAP::Sync::AdminUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::AdminUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::AdminUsers).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::AdminUsers).not_to receive(:execute)
end end
it 'does not call EE::Gitlab::LDAP::Sync::ExternalUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::ExternalUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::ExternalUsers).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::ExternalUsers).not_to receive(:execute)
end end
end end
...@@ -60,16 +60,16 @@ describe EE::Gitlab::LDAP::Sync::Groups do ...@@ -60,16 +60,16 @@ describe EE::Gitlab::LDAP::Sync::Groups do
) )
end end
it 'calls EE::Gitlab::LDAP::Sync::Group#execute' do it 'calls EE::Gitlab::Auth::LDAP::Sync::Group#execute' do
expect(EE::Gitlab::LDAP::Sync::Group).to receive(:execute).twice expect(EE::Gitlab::Auth::LDAP::Sync::Group).to receive(:execute).twice
end end
it 'does not call EE::Gitlab::LDAP::Sync::AdminUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::AdminUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::AdminUsers).to receive(:execute).once expect(EE::Gitlab::Auth::LDAP::Sync::AdminUsers).to receive(:execute).once
end end
it 'does not call EE::Gitlab::LDAP::Sync::ExternalUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::ExternalUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::ExternalUsers).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::ExternalUsers).not_to receive(:execute)
end end
end end
...@@ -81,16 +81,16 @@ describe EE::Gitlab::LDAP::Sync::Groups do ...@@ -81,16 +81,16 @@ describe EE::Gitlab::LDAP::Sync::Groups do
) )
end end
it 'calls EE::Gitlab::LDAP::Sync::Group#execute' do it 'calls EE::Gitlab::Auth::LDAP::Sync::Group#execute' do
expect(EE::Gitlab::LDAP::Sync::Group).to receive(:execute).twice expect(EE::Gitlab::Auth::LDAP::Sync::Group).to receive(:execute).twice
end end
it 'does not call EE::Gitlab::LDAP::Sync::AdminUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::AdminUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::AdminUsers).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::AdminUsers).not_to receive(:execute)
end end
it 'does not call EE::Gitlab::LDAP::Sync::ExternalUsers#execute' do it 'does not call EE::Gitlab::Auth::LDAP::Sync::ExternalUsers#execute' do
expect(EE::Gitlab::LDAP::Sync::ExternalUsers).to receive(:execute).once expect(EE::Gitlab::Auth::LDAP::Sync::ExternalUsers).to receive(:execute).once
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
require 'net/ldap/dn' require 'net/ldap/dn'
describe EE::Gitlab::LDAP::Sync::Proxy do describe EE::Gitlab::Auth::LDAP::Sync::Proxy do
include LdapHelpers include LdapHelpers
let(:adapter) { ldap_adapter } let(:adapter) { ldap_adapter }
...@@ -56,7 +56,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do ...@@ -56,7 +56,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do
sync_proxy.dns_for_group_cn('ldap_group1') sync_proxy.dns_for_group_cn('ldap_group1')
expect(sync_proxy).not_to receive(:ldap_group_member_dns) expect(sync_proxy).not_to receive(:ldap_group_member_dns)
expect(EE::Gitlab::LDAP::Group).not_to receive(:find_by_cn) expect(EE::Gitlab::Auth::LDAP::Group).not_to receive(:find_by_cn)
sync_proxy.dns_for_group_cn('ldap_group1') sync_proxy.dns_for_group_cn('ldap_group1')
end end
...@@ -123,7 +123,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do ...@@ -123,7 +123,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do
end end
it 'retrieves the user from LDAP' do it 'retrieves the user from LDAP' do
expect(::Gitlab::LDAP::Person).to receive(:find_by_uid) expect(::Gitlab::Auth::LDAP::Person).to receive(:find_by_uid)
sync_proxy.dn_for_uid('jane_doe') sync_proxy.dn_for_uid('jane_doe')
end end
...@@ -133,7 +133,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do ...@@ -133,7 +133,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do
expect(sync_proxy).not_to receive(:member_uid_to_dn) expect(sync_proxy).not_to receive(:member_uid_to_dn)
expect(Identity).not_to receive(:find_by) expect(Identity).not_to receive(:find_by)
expect(::Gitlab::LDAP::Person).not_to receive(:find_by_uid) expect(::Gitlab::Auth::LDAP::Person).not_to receive(:find_by_uid)
sync_proxy.dn_for_uid('jane_doe') sync_proxy.dn_for_uid('jane_doe')
end end
...@@ -177,7 +177,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do ...@@ -177,7 +177,7 @@ describe EE::Gitlab::LDAP::Sync::Proxy do
end end
it 'does not query LDAP' do it 'does not query LDAP' do
expect(::Gitlab::LDAP::Person).not_to receive(:find_by_uid) expect(::Gitlab::Auth::LDAP::Person).not_to receive(:find_by_uid)
end end
it 'retrieves the DN from the identity' do it 'retrieves the DN from the identity' do
......
require 'spec_helper' require 'spec_helper'
describe EE::Gitlab::LDAP::UserFilter do describe EE::Gitlab::Auth::LDAP::UserFilter do
include LdapHelpers include LdapHelpers
let(:auth_hash) do let(:auth_hash) do
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Adapter do describe Gitlab::Auth::LDAP::Adapter do
include LdapHelpers include LdapHelpers
let(:adapter) { ldap_adapter('ldapmain') } let(:adapter) { ldap_adapter('ldapmain') }
it 'includes the EE module' do it 'includes the EE module' do
expect(described_class).to include_module(EE::Gitlab::LDAP::Adapter) expect(described_class).to include_module(EE::Gitlab::Auth::LDAP::Adapter)
end end
describe '#groups' do describe '#groups' do
...@@ -34,7 +34,7 @@ describe Gitlab::LDAP::Adapter do ...@@ -34,7 +34,7 @@ describe Gitlab::LDAP::Adapter do
results = adapter.groups('group1') results = adapter.groups('group1')
expect(results.first).to be_a(EE::Gitlab::LDAP::Group) expect(results.first).to be_a(EE::Gitlab::Auth::LDAP::Group)
expect(results.first.cn).to eq('group1') expect(results.first.cn).to eq('group1')
expect(results.first.member_dns).to match_array(%w(uid=john uid=mary)) expect(results.first.member_dns).to match_array(%w(uid=john uid=mary))
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Person do describe Gitlab::Auth::LDAP::Person do
include LdapHelpers include LdapHelpers
let(:entry) { ldap_user_entry('john.doe') } let(:entry) { ldap_user_entry('john.doe') }
it 'includes the EE module' do it 'includes the EE module' do
expect(described_class).to include(EE::Gitlab::LDAP::Person) expect(described_class).to include(EE::Gitlab::Auth::LDAP::Person)
end end
describe '.ldap_attributes' do describe '.ldap_attributes' do
...@@ -84,7 +84,7 @@ describe Gitlab::LDAP::Person do ...@@ -84,7 +84,7 @@ describe Gitlab::LDAP::Person do
) )
end end
let(:config) { Gitlab::LDAP::Config.new('ldapmain') } let(:config) { Gitlab::Auth::LDAP::Config.new('ldapmain') }
let(:ldap_attributes) { described_class.ldap_attributes(config) } let(:ldap_attributes) { described_class.ldap_attributes(config) }
let(:expected_attributes) { %w(dn cn uid mail memberof) } let(:expected_attributes) { %w(dn cn uid mail memberof) }
...@@ -140,7 +140,7 @@ describe Gitlab::LDAP::Person do ...@@ -140,7 +140,7 @@ describe Gitlab::LDAP::Person do
subject { described_class.new(entry, 'ldapmain') } subject { described_class.new(entry, 'ldapmain') }
before do before do
allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(sync_ssh_keys: ssh_key_attribute_name) allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(sync_ssh_keys: ssh_key_attribute_name)
end end
context 'when the SSH key is literal' do context 'when the SSH key is literal' do
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::User do describe Gitlab::Auth::LDAP::User do
include LdapHelpers include LdapHelpers
let(:ldap_user) { described_class.new(auth_hash) } let(:ldap_user) { described_class.new(auth_hash) }
...@@ -26,7 +26,7 @@ describe Gitlab::LDAP::User do ...@@ -26,7 +26,7 @@ describe Gitlab::LDAP::User do
end end
it 'includes the EE module' do it 'includes the EE module' do
expect(described_class).to include_module(EE::Gitlab::LDAP::User) expect(described_class).to include_module(EE::Gitlab::Auth::LDAP::User)
end end
describe '#initialize' do describe '#initialize' do
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::OAuth::AuthHash do describe Gitlab::Auth::OAuth::AuthHash do
let(:auth_hash) do let(:auth_hash) do
described_class.new( described_class.new(
OmniAuth::AuthHash.new( OmniAuth::AuthHash.new(
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::OAuth::User do describe Gitlab::Auth::OAuth::User do
include LdapHelpers include LdapHelpers
describe 'login through kerberos with linkable LDAP user' do describe 'login through kerberos with linkable LDAP user' do
......
...@@ -58,7 +58,7 @@ describe LdapGroupLink do ...@@ -58,7 +58,7 @@ describe LdapGroupLink do
end end
it 'defaults to the first ldap server if empty' do it 'defaults to the first ldap server if empty' do
expect( klass.new.provider ).to eql Gitlab::LDAP::Config.providers.first expect( klass.new.provider ).to eql Gitlab::Auth::LDAP::Config.providers.first
end end
end end
end end
......
...@@ -14,8 +14,8 @@ describe API::Ldap do ...@@ -14,8 +14,8 @@ describe API::Ldap do
OpenStruct.new(cn: 'students') OpenStruct.new(cn: 'students')
] ]
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
allow(Gitlab::LDAP::Adapter).to receive(:new).and_return(adapter) allow(Gitlab::Auth::LDAP::Adapter).to receive(:new).and_return(adapter)
allow(adapter).to receive_messages(groups: groups) allow(adapter).to receive_messages(groups: groups)
end end
......
module EE module EE
module LdapHelpers module LdapHelpers
def proxy(adapter, provider = 'ldapmain') def proxy(adapter, provider = 'ldapmain')
EE::Gitlab::LDAP::Sync::Proxy.new(provider, adapter) EE::Gitlab::Auth::LDAP::Sync::Proxy.new(provider, adapter)
end end
# Stub an LDAP group search and provide the return entry. Specify `nil` for # Stub an LDAP group search and provide the return entry. Specify `nil` for
# `entry` to simulate when an LDAP group is not found # `entry` to simulate when an LDAP group is not found
# #
# Example: # Example:
# adapter = ::Gitlab::LDAP::Adapter.new('ldapmain', double(:ldap)) # adapter = ::Gitlab::Auth::LDAP::Adapter.new('ldapmain', double(:ldap))
# ldap_group1 = ldap_group_entry('uid=user,ou=users,dc=example,dc=com') # ldap_group1 = ldap_group_entry('uid=user,ou=users,dc=example,dc=com')
# #
# stub_ldap_group_find_by_cn('ldap_group1', ldap_group1, adapter) # stub_ldap_group_find_by_cn('ldap_group1', ldap_group1, adapter)
def stub_ldap_group_find_by_cn(cn, entry, adapter = nil) def stub_ldap_group_find_by_cn(cn, entry, adapter = nil)
if entry.present? if entry.present?
return_value = EE::Gitlab::LDAP::Group.new(entry, adapter) return_value = EE::Gitlab::Auth::LDAP::Group.new(entry, adapter)
end end
allow(EE::Gitlab::LDAP::Group) allow(EE::Gitlab::Auth::LDAP::Group)
.to receive(:find_by_cn) .to receive(:find_by_cn)
.with(cn, kind_of(::Gitlab::LDAP::Adapter)).and_return(return_value) .with(cn, kind_of(::Gitlab::Auth::LDAP::Adapter)).and_return(return_value)
end end
# Create an LDAP group entry with any number of members. By default, creates # Create an LDAP group entry with any number of members. By default, creates
...@@ -94,7 +94,7 @@ module EE ...@@ -94,7 +94,7 @@ module EE
# Stub Active Directory range member retrieval. # Stub Active Directory range member retrieval.
# #
# Example: # Example:
# adapter = ::Gitlab::LDAP::Adapter.new('ldapmain', double(:ldap)) # adapter = ::Gitlab::Auth::LDAP::Adapter.new('ldapmain', double(:ldap))
# group_entry_page1 = ldap_group_entry_with_member_range( # group_entry_page1 = ldap_group_entry_with_member_range(
# [user_dn('user1'), user_dn('user2'), user_dn('user3')], # [user_dn('user1'), user_dn('user2'), user_dn('user3')],
# range_start: '0', # range_start: '0',
...@@ -105,7 +105,7 @@ module EE ...@@ -105,7 +105,7 @@ module EE
# range_start: '3', # range_start: '3',
# range_end: '*' # range_end: '*'
# ) # )
# group = EE::Gitlab::LDAP::Group.new(group_entry_page1, adapter) # group = EE::Gitlab::Auth::LDAP::Group.new(group_entry_page1, adapter)
# #
# stub_ldap_adapter_group_members_in_range(group_entry_page2, adapter, range_start: '3') # stub_ldap_adapter_group_members_in_range(group_entry_page2, adapter, range_start: '3')
def stub_ldap_adapter_group_members_in_range( def stub_ldap_adapter_group_members_in_range(
...@@ -118,7 +118,7 @@ module EE ...@@ -118,7 +118,7 @@ module EE
end end
def stub_ldap_adapter_nested_groups(parent_dn, entries = [], adapter = ldap_adapter) def stub_ldap_adapter_nested_groups(parent_dn, entries = [], adapter = ldap_adapter)
groups = entries.map { |entry| EE::Gitlab::LDAP::Group.new(entry, adapter) } groups = entries.map { |entry| EE::Gitlab::Auth::LDAP::Group.new(entry, adapter) }
allow(adapter).to receive(:nested_groups).with(parent_dn).and_return(groups) allow(adapter).to receive(:nested_groups).with(parent_dn).and_return(groups)
end end
......
...@@ -5,13 +5,13 @@ describe LdapAllGroupsSyncWorker do ...@@ -5,13 +5,13 @@ describe LdapAllGroupsSyncWorker do
before do before do
allow(Sidekiq.logger).to receive(:info) allow(Sidekiq.logger).to receive(:info)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
end end
describe '#perform' do describe '#perform' do
context 'with the default license key' do context 'with the default license key' do
it 'syncs all groups when group_id is nil' do it 'syncs all groups when group_id is nil' do
expect(EE::Gitlab::LDAP::Sync::Groups).to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::Groups).to receive(:execute)
subject.perform subject.perform
end end
...@@ -23,7 +23,7 @@ describe LdapAllGroupsSyncWorker do ...@@ -23,7 +23,7 @@ describe LdapAllGroupsSyncWorker do
end end
it 'does not sync all groups' do it 'does not sync all groups' do
expect(EE::Gitlab::LDAP::Sync::Groups).not_to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::Groups).not_to receive(:execute)
subject.perform subject.perform
end end
......
...@@ -7,14 +7,14 @@ describe LdapGroupSyncWorker do ...@@ -7,14 +7,14 @@ describe LdapGroupSyncWorker do
def expect_fake_proxy(provider) def expect_fake_proxy(provider)
fake = double fake = double
expect(EE::Gitlab::LDAP::Sync::Proxy) expect(EE::Gitlab::Auth::LDAP::Sync::Proxy)
.to receive(:open).with(provider).and_yield(fake) .to receive(:open).with(provider).and_yield(fake)
fake fake
end end
before do before do
allow(Sidekiq.logger).to receive(:info) allow(Sidekiq.logger).to receive(:info)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
end end
describe '#perform' do describe '#perform' do
...@@ -63,16 +63,16 @@ describe LdapGroupSyncWorker do ...@@ -63,16 +63,16 @@ describe LdapGroupSyncWorker do
describe '#sync_group' do describe '#sync_group' do
it 'syncs a single provider when a provider was given' do it 'syncs a single provider when a provider was given' do
proxy = EE::Gitlab::LDAP::Sync::Proxy.new('ldapmain', ldap_adapter) proxy = EE::Gitlab::Auth::LDAP::Sync::Proxy.new('ldapmain', ldap_adapter)
expect(EE::Gitlab::LDAP::Sync::Group).to receive(:execute) expect(EE::Gitlab::Auth::LDAP::Sync::Group).to receive(:execute)
.with(group, proxy) .with(group, proxy)
subject.sync_group(group, proxy: proxy) subject.sync_group(group, proxy: proxy)
end end
it 'syncs all providers when no proxy was given' do it 'syncs all providers when no proxy was given' do
expect(EE::Gitlab::LDAP::Sync::Group).to receive(:execute_all_providers) expect(EE::Gitlab::Auth::LDAP::Sync::Group).to receive(:execute_all_providers)
.with(group) .with(group)
subject.sync_group(group) subject.sync_group(group)
......
...@@ -5,7 +5,7 @@ describe LdapSyncWorker do ...@@ -5,7 +5,7 @@ describe LdapSyncWorker do
before do before do
allow(Sidekiq.logger).to receive(:info) allow(Sidekiq.logger).to receive(:info)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
create(:omniauth_user, provider: 'ldapmain') create(:omniauth_user, provider: 'ldapmain')
end end
...@@ -13,7 +13,7 @@ describe LdapSyncWorker do ...@@ -13,7 +13,7 @@ describe LdapSyncWorker do
describe '#perform' do describe '#perform' do
context 'with the default license key' do context 'with the default license key' do
it 'syncs all LDAP users' do it 'syncs all LDAP users' do
expect(Gitlab::LDAP::Access).to receive(:allowed?) expect(Gitlab::Auth::LDAP::Access).to receive(:allowed?)
subject.perform subject.perform
end end
...@@ -25,7 +25,7 @@ describe LdapSyncWorker do ...@@ -25,7 +25,7 @@ describe LdapSyncWorker do
end end
it 'does not sync LDAP users' do it 'does not sync LDAP users' do
expect(Gitlab::LDAP::Access).not_to receive(:allowed?) expect(Gitlab::Auth::LDAP::Access).not_to receive(:allowed?)
subject.perform subject.perform
end end
......
...@@ -275,7 +275,7 @@ module API ...@@ -275,7 +275,7 @@ module API
desc 'Sync a group with LDAP.' desc 'Sync a group with LDAP.'
post ":id/ldap_sync" do post ":id/ldap_sync" do
not_found! unless Gitlab::LDAP::Config.group_sync_enabled? not_found! unless Gitlab::Auth::LDAP::Config.group_sync_enabled?
group = find_group!(params[:id]) group = find_group!(params[:id])
authorize! :admin_group, group authorize! :admin_group, group
......
...@@ -32,7 +32,7 @@ feature 'Admin updates settings' do ...@@ -32,7 +32,7 @@ feature 'Admin updates settings' do
describe 'LDAP settings' do describe 'LDAP settings' do
context 'with LDAP enabled' do context 'with LDAP enabled' do
scenario 'Change allow group owners to manage ldap' do scenario 'Change allow group owners to manage ldap' do
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
visit admin_application_settings_path visit admin_application_settings_path
find('#application_setting_allow_group_owners_to_manage_ldap').set(false) find('#application_setting_allow_group_owners_to_manage_ldap').set(false)
......
...@@ -13,7 +13,7 @@ feature 'Edit group settings' do ...@@ -13,7 +13,7 @@ feature 'Edit group settings' do
context 'with LDAP enabled' do context 'with LDAP enabled' do
before do before do
allow_any_instance_of(Group).to receive(:ldap_synced?).and_return(true) allow_any_instance_of(Group).to receive(:ldap_synced?).and_return(true)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
end end
scenario 'is able to navigate to LDAP group section' do scenario 'is able to navigate to LDAP group section' do
......
...@@ -19,14 +19,14 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -19,14 +19,14 @@ describe Gitlab::Auth::LDAP::Access do
describe '#find_ldap_user' do describe '#find_ldap_user' do
it 'finds a user by dn first' do it 'finds a user by dn first' do
expect(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user) expect(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user)
access.find_ldap_user access.find_ldap_user
end end
it 'finds a user by email if the email came from LDAP' do it 'finds a user by email if the email came from LDAP' do
expect(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(nil) expect(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil)
expect(Gitlab::LDAP::Person).to receive(:find_by_email) expect(Gitlab::Auth::LDAP::Person).to receive(:find_by_email)
access.find_ldap_user access.find_ldap_user
end end
...@@ -196,7 +196,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -196,7 +196,7 @@ describe Gitlab::Auth::LDAP::Access do
before do before do
allow(access).to( allow(access).to(
receive_messages( receive_messages(
ldap_user: Gitlab::LDAP::Person.new(entry, user.ldap_identity.provider) ldap_user: Gitlab::Auth::LDAP::Person.new(entry, user.ldap_identity.provider)
) )
) )
end end
...@@ -240,18 +240,18 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -240,18 +240,18 @@ describe Gitlab::Auth::LDAP::Access do
end end
before do before do
allow(access).to receive_messages(ldap_user: Gitlab::LDAP::Person.new(entry, user.ldap_identity.provider)) allow(access).to receive_messages(ldap_user: Gitlab::Auth::LDAP::Person.new(entry, user.ldap_identity.provider))
end end
it "adds a Kerberos identity if it is in Active Directory but not in GitLab" do it "adds a Kerberos identity if it is in Active Directory but not in GitLab" do
allow_any_instance_of(EE::Gitlab::LDAP::Person).to receive_messages(kerberos_principal: "mylogin@FOO.COM") allow_any_instance_of(EE::Gitlab::Auth::LDAP::Person).to receive_messages(kerberos_principal: "mylogin@FOO.COM")
expect { access.update_kerberos_identity }.to change(user.identities.where(provider: :kerberos), :count).from(0).to(1) expect { access.update_kerberos_identity }.to change(user.identities.where(provider: :kerberos), :count).from(0).to(1)
expect(user.identities.where(provider: "kerberos").last.extern_uid).to eq("mylogin@FOO.COM") expect(user.identities.where(provider: "kerberos").last.extern_uid).to eq("mylogin@FOO.COM")
end end
it "updates existing Kerberos identity in GitLab if Active Directory has a different one" do it "updates existing Kerberos identity in GitLab if Active Directory has a different one" do
allow_any_instance_of(EE::Gitlab::LDAP::Person).to receive_messages(kerberos_principal: "otherlogin@BAR.COM") allow_any_instance_of(EE::Gitlab::Auth::LDAP::Person).to receive_messages(kerberos_principal: "otherlogin@BAR.COM")
user.identities.build(provider: "kerberos", extern_uid: "mylogin@FOO.COM").save user.identities.build(provider: "kerberos", extern_uid: "mylogin@FOO.COM").save
expect { access.update_kerberos_identity }.not_to change(user.identities.where(provider: "kerberos"), :count) expect { access.update_kerberos_identity }.not_to change(user.identities.where(provider: "kerberos"), :count)
...@@ -259,7 +259,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -259,7 +259,7 @@ describe Gitlab::Auth::LDAP::Access do
end end
it "does not remove Kerberos identities from GitLab if they are none in the LDAP provider" do it "does not remove Kerberos identities from GitLab if they are none in the LDAP provider" do
allow_any_instance_of(EE::Gitlab::LDAP::Person).to receive_messages(kerberos_principal: nil) allow_any_instance_of(EE::Gitlab::Auth::LDAP::Person).to receive_messages(kerberos_principal: nil)
user.identities.build(provider: "kerberos", extern_uid: "otherlogin@BAR.COM").save user.identities.build(provider: "kerberos", extern_uid: "otherlogin@BAR.COM").save
expect { access.update_kerberos_identity }.not_to change(user.identities.where(provider: "kerberos"), :count) expect { access.update_kerberos_identity }.not_to change(user.identities.where(provider: "kerberos"), :count)
...@@ -267,7 +267,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -267,7 +267,7 @@ describe Gitlab::Auth::LDAP::Access do
end end
it "does not modify identities in GitLab if they are no kerberos principal in the LDAP provider" do it "does not modify identities in GitLab if they are no kerberos principal in the LDAP provider" do
allow_any_instance_of(EE::Gitlab::LDAP::Person).to receive_messages(kerberos_principal: nil) allow_any_instance_of(EE::Gitlab::Auth::LDAP::Person).to receive_messages(kerberos_principal: nil)
expect { access.update_kerberos_identity }.not_to change(user.identities, :count) expect { access.update_kerberos_identity }.not_to change(user.identities, :count)
end end
...@@ -281,18 +281,18 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -281,18 +281,18 @@ describe Gitlab::Auth::LDAP::Access do
end end
before do before do
allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(sync_ssh_keys: ssh_key_attribute_name) allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(sync_ssh_keys: ssh_key_attribute_name)
allow(access).to receive_messages(sync_ssh_keys?: true) allow(access).to receive_messages(sync_ssh_keys?: true)
end end
it "adds a SSH key if it is in LDAP but not in gitlab" do it "adds a SSH key if it is in LDAP but not in gitlab" do
allow_any_instance_of(Gitlab::LDAP::Adapter).to receive(:user) { Gitlab::LDAP::Person.new(entry, 'ldapmain') } allow_any_instance_of(Gitlab::Auth::LDAP::Adapter).to receive(:user) { Gitlab::Auth::LDAP::Person.new(entry, 'ldapmain') }
expect { access.update_ssh_keys }.to change(user.keys, :count).from(0).to(1) expect { access.update_ssh_keys }.to change(user.keys, :count).from(0).to(1)
end end
it "adds a SSH key and give it a proper name" do it "adds a SSH key and give it a proper name" do
allow_any_instance_of(Gitlab::LDAP::Adapter).to receive(:user) { Gitlab::LDAP::Person.new(entry, 'ldapmain') } allow_any_instance_of(Gitlab::Auth::LDAP::Adapter).to receive(:user) { Gitlab::Auth::LDAP::Person.new(entry, 'ldapmain') }
access.update_ssh_keys access.update_ssh_keys
expect(user.keys.last.title).to match(/LDAP/) expect(user.keys.last.title).to match(/LDAP/)
...@@ -301,7 +301,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -301,7 +301,7 @@ describe Gitlab::Auth::LDAP::Access do
it "does not add a SSH key if it is invalid" do it "does not add a SSH key if it is invalid" do
entry = Net::LDAP::Entry.from_single_ldif_string("dn: cn=foo, dc=bar, dc=com\n#{ssh_key_attribute_name}: I am not a valid key") entry = Net::LDAP::Entry.from_single_ldif_string("dn: cn=foo, dc=bar, dc=com\n#{ssh_key_attribute_name}: I am not a valid key")
allow_any_instance_of(Gitlab::LDAP::Adapter).to receive(:user) { Gitlab::LDAP::Person.new(entry, 'ldapmain') } allow_any_instance_of(Gitlab::Auth::LDAP::Adapter).to receive(:user) { Gitlab::Auth::LDAP::Person.new(entry, 'ldapmain') }
expect { access.update_ssh_keys }.not_to change(user.keys, :count) expect { access.update_ssh_keys }.not_to change(user.keys, :count)
end end
...@@ -313,14 +313,14 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -313,14 +313,14 @@ describe Gitlab::Auth::LDAP::Access do
it "removes a SSH key if it is no longer in LDAP" do it "removes a SSH key if it is no longer in LDAP" do
entry = Net::LDAP::Entry.from_single_ldif_string("dn: cn=foo, dc=bar, dc=com\n#{ssh_key_attribute_name}:\n") entry = Net::LDAP::Entry.from_single_ldif_string("dn: cn=foo, dc=bar, dc=com\n#{ssh_key_attribute_name}:\n")
allow_any_instance_of(Gitlab::LDAP::Adapter).to receive(:user) { Gitlab::LDAP::Person.new(entry, 'ldapmain') } allow_any_instance_of(Gitlab::Auth::LDAP::Adapter).to receive(:user) { Gitlab::Auth::LDAP::Person.new(entry, 'ldapmain') }
expect { access.update_ssh_keys }.to change(user.keys, :count).from(1).to(0) expect { access.update_ssh_keys }.to change(user.keys, :count).from(1).to(0)
end end
it "removes a SSH key if the ldap attribute was removed" do it "removes a SSH key if the ldap attribute was removed" do
entry = Net::LDAP::Entry.from_single_ldif_string("dn: cn=foo, dc=bar, dc=com") entry = Net::LDAP::Entry.from_single_ldif_string("dn: cn=foo, dc=bar, dc=com")
allow_any_instance_of(Gitlab::LDAP::Adapter).to receive(:user) { Gitlab::LDAP::Person.new(entry, 'ldapmain') } allow_any_instance_of(Gitlab::Auth::LDAP::Adapter).to receive(:user) { Gitlab::Auth::LDAP::Person.new(entry, 'ldapmain') }
expect { access.update_ssh_keys }.to change(user.keys, :count).from(1).to(0) expect { access.update_ssh_keys }.to change(user.keys, :count).from(1).to(0)
end end
...@@ -331,7 +331,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -331,7 +331,7 @@ describe Gitlab::Auth::LDAP::Access do
let(:entry) { Net::LDAP::Entry.new } let(:entry) { Net::LDAP::Entry.new }
before do before do
allow(access).to receive_messages(ldap_user: Gitlab::LDAP::Person.new(entry, user.ldap_identity.provider)) allow(access).to receive_messages(ldap_user: Gitlab::Auth::LDAP::Person.new(entry, user.ldap_identity.provider))
end end
it "does not update email if email attribute is not set" do it "does not update email if email attribute is not set" do
...@@ -361,7 +361,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -361,7 +361,7 @@ describe Gitlab::Auth::LDAP::Access do
let(:person_with_memberof) do let(:person_with_memberof) do
entry['memberof'] = ['CN=Group1,CN=Users,DC=The dc,DC=com', entry['memberof'] = ['CN=Group1,CN=Users,DC=The dc,DC=com',
'CN=Group2,CN=Builtin,DC=The dc,DC=com'] 'CN=Group2,CN=Builtin,DC=The dc,DC=com']
Gitlab::LDAP::Person.new(entry, provider) Gitlab::Auth::LDAP::Person.new(entry, provider)
end end
it 'triggers a sync for all groups found in `memberof`' do it 'triggers a sync for all groups found in `memberof`' do
...@@ -379,7 +379,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -379,7 +379,7 @@ describe Gitlab::Auth::LDAP::Access do
it "doesn't continue when there is no `memberOf` param" do it "doesn't continue when there is no `memberOf` param" do
allow(access).to receive(:ldap_user) allow(access).to receive(:ldap_user)
.and_return(Gitlab::LDAP::Person.new(entry, provider)) .and_return(Gitlab::Auth::LDAP::Person.new(entry, provider))
expect(LdapGroupLink).not_to receive(:where) expect(LdapGroupLink).not_to receive(:where)
expect(LdapGroupSyncWorker).not_to receive(:perform_async) expect(LdapGroupSyncWorker).not_to receive(:perform_async)
...@@ -404,7 +404,7 @@ describe Gitlab::Auth::LDAP::Access do ...@@ -404,7 +404,7 @@ describe Gitlab::Auth::LDAP::Access do
it 'updates the external UID if it changed in the entry' do it 'updates the external UID if it changed in the entry' do
entry = ldap_user_entry('another uid') entry = ldap_user_entry('another uid')
provider = user.ldap_identity.provider provider = user.ldap_identity.provider
person = Gitlab::LDAP::Person.new(entry, provider) person = Gitlab::Auth::LDAP::Person.new(entry, provider)
allow(access).to receive(:ldap_user).and_return(person) allow(access).to receive(:ldap_user).and_return(person)
......
...@@ -23,7 +23,7 @@ describe Gitlab::Auth::LDAP::Config do ...@@ -23,7 +23,7 @@ describe Gitlab::Auth::LDAP::Config do
end end
it 'raises an error if a unknown provider is used' do it 'raises an error if a unknown provider is used' do
expect { described_class.new 'unknown' }.to raise_error(Gitlab::LDAP::Config::InvalidProvider) expect { described_class.new 'unknown' }.to raise_error(Gitlab::Auth::LDAP::Config::InvalidProvider)
end end
end end
......
...@@ -180,15 +180,7 @@ describe Gitlab::Auth::LDAP::User do ...@@ -180,15 +180,7 @@ describe Gitlab::Auth::LDAP::User do
describe 'blocking' do describe 'blocking' do
def configure_block(value) def configure_block(value)
<<<<<<< HEAD:spec/lib/gitlab/ldap/user_spec.rb
stub_ldap_config(block_auto_created_users: value) stub_ldap_config(block_auto_created_users: value)
||||||| parent of 50a70efd118... Moved o_auth/saml/ldap modules under gitlab/auth
allow_any_instance_of(Gitlab::LDAP::Config)
.to receive(:block_auto_created_users).and_return(value)
=======
allow_any_instance_of(Gitlab::Auth::LDAP::Config)
.to receive(:block_auto_created_users).and_return(value)
>>>>>>> 50a70efd118... Moved o_auth/saml/ldap modules under gitlab/auth:spec/lib/gitlab/auth/ldap/user_spec.rb
end end
context 'signup' do context 'signup' do
......
...@@ -25,23 +25,23 @@ describe Gitlab::Auth::Saml::User do ...@@ -25,23 +25,23 @@ describe Gitlab::Auth::Saml::User do
end end
def stub_ldap_config(messages) def stub_ldap_config(messages)
allow(Gitlab::LDAP::Config).to receive_messages(messages) allow(Gitlab::Auth::LDAP::Config).to receive_messages(messages)
end end
def stub_basic_saml_config def stub_basic_saml_config
allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', args: {} } }) allow(Gitlab::Auth::Saml::Config).to receive_messages({ options: { name: 'saml', args: {} } })
end end
def stub_saml_group_config(groups) def stub_saml_group_config(groups)
allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } }) allow(Gitlab::Auth::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } })
end end
def stub_saml_required_group_config(groups) def stub_saml_required_group_config(groups)
allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', required_groups: groups, args: {} } }) allow(Gitlab::Auth::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', required_groups: groups, args: {} } })
end end
def stub_saml_admin_group_config(groups) def stub_saml_admin_group_config(groups)
allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', admin_groups: groups, args: {} } }) allow(Gitlab::Auth::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', admin_groups: groups, args: {} } })
end end
before do before do
...@@ -216,7 +216,7 @@ describe Gitlab::Auth::Saml::User do ...@@ -216,7 +216,7 @@ describe Gitlab::Auth::Saml::User do
it 'does not allow non-members' do it 'does not allow non-members' do
stub_saml_required_group_config(%w(ArchitectureAstronauts)) stub_saml_required_group_config(%w(ArchitectureAstronauts))
expect { saml_user.save }.to raise_error Gitlab::OAuth::SignupDisabledError expect { saml_user.save }.to raise_error Gitlab::Auth::OAuth::User::SignupDisabledError
end end
it 'blocks non-members' do it 'blocks non-members' do
......
...@@ -938,7 +938,7 @@ describe API::Groups do ...@@ -938,7 +938,7 @@ describe API::Groups do
describe 'POST /groups/:id/ldap_sync' do describe 'POST /groups/:id/ldap_sync' do
before do before do
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true) allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
end end
context 'when the ldap_group_sync feature is available' do context 'when the ldap_group_sync feature is available' do
......
...@@ -7,7 +7,7 @@ module LdapHelpers ...@@ -7,7 +7,7 @@ module LdapHelpers
def fake_ldap_sync_proxy(provider) def fake_ldap_sync_proxy(provider)
fake_proxy = double(:proxy, adapter: ldap_adapter) fake_proxy = double(:proxy, adapter: ldap_adapter)
allow(::EE::Gitlab::LDAP::Sync::Proxy).to receive(:open).with(provider).and_yield(fake_proxy) allow(::EE::Gitlab::Auth::LDAP::Sync::Proxy).to receive(:open).with(provider).and_yield(fake_proxy)
fake_proxy fake_proxy
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment