diff options
author | Dmitriy Zaporozhets <dzaporozhets@gitlab.com> | 2014-10-14 15:01:37 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dzaporozhets@gitlab.com> | 2014-10-14 15:01:37 +0000 |
commit | e3bd17a7ba5238c147a79d0770e8503fd913610c (patch) | |
tree | 4833babe1357f34f9f856e45b139f8b86bceaa3a /lib | |
parent | 4bebdc09463e29d26eac0117e0e3b45a9448c600 (diff) | |
parent | b4f7b387d0dfaef1766a82040249abb933632930 (diff) | |
download | gitlab-ce-e3bd17a7ba5238c147a79d0770e8503fd913610c.tar.gz |
Merge branch 'feature-multiple-ldap-servers' into 'master'
Feature multiple ldap servers
Update the code so Gitlab-EE can support multiple LDAP servers
See merge request !1172
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/auth.rb | 14 | ||||
-rw-r--r-- | lib/gitlab/ldap/access.rb | 36 | ||||
-rw-r--r-- | lib/gitlab/ldap/adapter.rb | 63 | ||||
-rw-r--r-- | lib/gitlab/ldap/authentication.rb | 71 | ||||
-rw-r--r-- | lib/gitlab/ldap/config.rb | 115 | ||||
-rw-r--r-- | lib/gitlab/ldap/person.rb | 26 | ||||
-rw-r--r-- | lib/gitlab/ldap/user.rb | 52 | ||||
-rw-r--r-- | lib/gitlab/oauth/user.rb | 4 |
8 files changed, 252 insertions, 129 deletions
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 955abc1bedd..ae33c529b93 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -3,22 +3,16 @@ module Gitlab def find(login, password) user = User.find_by(email: login) || User.find_by(username: login) + # If no user is found, or it's an LDAP server, try LDAP. + # LDAP users are only authenticated via LDAP if user.nil? || user.ldap_user? # Second chance - try LDAP authentication - return nil unless ldap_conf.enabled + return nil unless Gitlab::LDAP::Config.enabled? - Gitlab::LDAP::User.authenticate(login, password) + Gitlab::LDAP::Authentication.login(login, password) else user if user.valid_password?(password) end end - - def log - Gitlab::AppLogger - end - - def ldap_conf - @ldap_conf ||= Gitlab.config.ldap - end end end diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index d2235d2e3bc..eb2c4e48ff2 100644 --- a/lib/gitlab/ldap/access.rb +++ b/lib/gitlab/ldap/access.rb @@ -1,18 +1,21 @@ +# LDAP authorization model +# +# * Check if we are allowed access (not blocked) +# module Gitlab module LDAP class Access - attr_reader :adapter + attr_reader :adapter, :provider, :user - def self.open(&block) - Gitlab::LDAP::Adapter.open do |adapter| - block.call(self.new(adapter)) + def self.open(user, &block) + Gitlab::LDAP::Adapter.open(user.provider) do |adapter| + block.call(self.new(user, adapter)) end end def self.allowed?(user) - self.open do |access| - if access.allowed?(user) - # GitLab EE LDAP code goes here + self.open(user) do |access| + if access.allowed? user.last_credential_check_at = Time.now user.save true @@ -22,21 +25,30 @@ module Gitlab end end - def initialize(adapter=nil) + def initialize(user, adapter=nil) @adapter = adapter + @user = user + @provider = user.provider end - def allowed?(user) + def allowed? if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter) - if Gitlab.config.ldap.active_directory - !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter) - end + return true unless ldap_config.active_directory + !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter) else false end rescue false end + + def adapter + @adapter ||= Gitlab::LDAP::Adapter.new(provider) + end + + def ldap_config + Gitlab::LDAP::Config.new(provider) + end end end end diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index 68ac1b22909..c4d0a20d89a 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -1,52 +1,25 @@ module Gitlab module LDAP class Adapter - attr_reader :ldap + attr_reader :provider, :ldap - def self.open(&block) - Net::LDAP.open(adapter_options) do |ldap| - block.call(self.new(ldap)) + def self.open(provider, &block) + Net::LDAP.open(config(provider).adapter_options) do |ldap| + block.call(self.new(provider, ldap)) end end - def self.config - Gitlab.config.ldap + def self.config(provider) + Gitlab::LDAP::Config.new(provider) end - def self.adapter_options - encryption = - case config['method'].to_s - when 'ssl' - :simple_tls - when 'tls' - :start_tls - else - nil - end - - options = { - host: config['host'], - port: config['port'], - encryption: encryption - } - - auth_options = { - auth: { - method: :simple, - username: config['bind_dn'], - password: config['password'] - } - } - - if config['password'] || config['bind_dn'] - options.merge!(auth_options) - end - options + def initialize(provider, ldap=nil) + @provider = provider + @ldap = ldap || Net::LDAP.new(config.adapter_options) end - - def initialize(ldap=nil) - @ldap = ldap || Net::LDAP.new(self.class.adapter_options) + def config + Gitlab::LDAP::Config.new(provider) end def users(field, value) @@ -57,13 +30,13 @@ module Gitlab } else options = { - base: config['base'], + base: config.base, filter: Net::LDAP::Filter.eq(field, value) } end - if config['user_filter'].present? - user_filter = Net::LDAP::Filter.construct(config['user_filter']) + if config.user_filter.present? + user_filter = Net::LDAP::Filter.construct(config.user_filter) options[:filter] = if options[:filter] Net::LDAP::Filter.join(options[:filter], user_filter) @@ -77,7 +50,7 @@ module Gitlab end entries.map do |entry| - Gitlab::LDAP::Person.new(entry) + Gitlab::LDAP::Person.new(entry, provider) end end @@ -105,12 +78,6 @@ module Gitlab results end end - - private - - def config - @config ||= self.class.config - end end end end diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb new file mode 100644 index 00000000000..a5944f96983 --- /dev/null +++ b/lib/gitlab/ldap/authentication.rb @@ -0,0 +1,71 @@ +# This calls helps to authenticate to LDAP by providing username and password +# +# Since multiple LDAP servers are supported, it will loop through all of them +# until a valid bind is found +# + +module Gitlab + module LDAP + class Authentication + def self.login(login, password) + return unless Gitlab::LDAP::Config.enabled? + return unless login.present? && password.present? + + auth = nil + # loop through providers until valid bind + providers.find do |provider| + auth = new(provider) + auth.login(login, password) # true will exit the loop + end + + # If (login, password) was invalid for all providers, the value of auth is now the last + # Gitlab::LDAP::Authentication instance we tried. + auth.user + end + + def self.providers + Gitlab::LDAP::Config.providers + end + + attr_accessor :provider, :ldap_user + + def initialize(provider) + @provider = provider + end + + def login(login, password) + @ldap_user = adapter.bind_as( + filter: user_filter(login), + size: 1, + password: password + ) + end + + def adapter + OmniAuth::LDAP::Adaptor.new(config.options) + end + + def config + Gitlab::LDAP::Config.new(provider) + end + + def user_filter(login) + filter = Net::LDAP::Filter.eq(config.uid, login) + + # Apply LDAP user filter if present + if config.user_filter.present? + filter = Net::LDAP::Filter.join( + filter, + Net::LDAP::Filter.construct(config.user_filter) + ) + end + filter + end + + def user + return nil unless ldap_user + Gitlab::LDAP::User.find_by_uid_and_provider(ldap_user.dn, provider) + end + end + end +end
\ No newline at end of file diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb new file mode 100644 index 00000000000..d41bfba9b0f --- /dev/null +++ b/lib/gitlab/ldap/config.rb @@ -0,0 +1,115 @@ +# Load a specific server configuration +module Gitlab + module LDAP + class Config + attr_accessor :provider, :options + + def self.enabled? + Gitlab.config.ldap.enabled + end + + def self.servers + Gitlab.config.ldap.servers.values + end + + def self.providers + servers.map {|server| server['provider_name'] } + end + + def initialize(provider) + @provider = provider + invalid_provider unless valid_provider? + @options = config_for(provider) + end + + def enabled? + base_config.enabled + end + + def adapter_options + { + host: options['host'], + port: options['port'], + encryption: encryption + }.tap do |options| + options.merge!(auth_options) if has_auth? + end + end + + def base + options['base'] + end + + def uid + options['uid'] + end + + def sync_ssh_keys? + sync_ssh_keys.present? + end + + # The LDAP attribute in which the ssh keys are stored + def sync_ssh_keys + options['sync_ssh_keys'] + end + + def user_filter + options['user_filter'] + end + + def group_base + options['group_base'] + end + + def admin_group + options['admin_group'] + end + + def active_directory + options['active_directory'] + end + + protected + def base_config + Gitlab.config.ldap + end + + def config_for(provider) + base_config.servers.values.find { |server| server['provider_name'] == provider } + end + + def encryption + case options['method'].to_s + when 'ssl' + :simple_tls + when 'tls' + :start_tls + else + nil + end + end + + def valid_provider? + self.class.providers.include?(provider) + end + + def invalid_provider + raise "Unknown provider (#{provider}). Available providers: #{self.class.providers}" + end + + def auth_options + { + auth: { + method: :simple, + username: options['bind_dn'], + password: options['password'] + } + } + end + + def has_auth? + options['password'] || options['bind_dn'] + end + end + end +end diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index 87c3d711db4..3e0b3e6cbf8 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -6,24 +6,24 @@ module Gitlab # Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/ AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2") - def self.find_by_uid(uid, adapter=nil) - adapter ||= Gitlab::LDAP::Adapter.new - adapter.user(config.uid, uid) + attr_accessor :entry, :provider + + def self.find_by_uid(uid, adapter) + adapter.user(adapter.config.uid, uid) end - def self.find_by_dn(dn, adapter=nil) - adapter ||= Gitlab::LDAP::Adapter.new + def self.find_by_dn(dn, adapter) adapter.user('dn', dn) end - def self.disabled_via_active_directory?(dn, adapter=nil) - adapter ||= Gitlab::LDAP::Adapter.new + def self.disabled_via_active_directory?(dn, adapter) adapter.dn_matches_filter?(dn, AD_USER_DISABLED) end - def initialize(entry) + def initialize(entry, provider) Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } @entry = entry + @provider = provider end def name @@ -38,6 +38,10 @@ module Gitlab uid end + def email + entry.try(:mail) + end + def dn entry.dn end @@ -48,12 +52,8 @@ module Gitlab @entry end - def adapter - @adapter ||= Gitlab::LDAP::Adapter.new - end - def config - @config ||= Gitlab.config.ldap + @config ||= Gitlab::LDAP::Config.new(provider) end end end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index 006ef170726..3176e9790a7 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -10,45 +10,11 @@ module Gitlab module LDAP class User < Gitlab::OAuth::User class << self - def authenticate(login, password) - # Check user against LDAP backend if user is not authenticated - # Only check with valid login and password to prevent anonymous bind results - return nil unless ldap_conf.enabled && login.present? && password.present? - - ldap_user = adapter.bind_as( - filter: user_filter(login), - size: 1, - password: password - ) - - find_by_uid(ldap_user.dn) if ldap_user - end - - def adapter - @adapter ||= OmniAuth::LDAP::Adaptor.new(ldap_conf) - end - - def user_filter(login) - filter = Net::LDAP::Filter.eq(adapter.uid, login) - # Apply LDAP user filter if present - if ldap_conf['user_filter'].present? - user_filter = Net::LDAP::Filter.construct(ldap_conf['user_filter']) - filter = Net::LDAP::Filter.join(filter, user_filter) - end - filter - end - - def ldap_conf - Gitlab.config.ldap - end - - def find_by_uid(uid) + def find_by_uid_and_provider(uid, provider) # LDAP distinguished name is case-insensitive - model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last - end - - def provider - 'ldap' + ::User. + where(provider: [provider, :ldap]). + where('lower(extern_uid) = ?', uid.downcase).last end end @@ -63,10 +29,8 @@ module Gitlab end def find_by_uid_and_provider - # LDAP distinguished name is case-insensitive - model. - where(provider: auth_hash.provider). - where('lower(extern_uid) = ?', auth_hash.uid.downcase).last + self.class.find_by_uid_and_provider( + auth_hash.uid.downcase, auth_hash.provider) end def find_by_email @@ -88,6 +52,10 @@ module Gitlab def needs_blocking? false end + + def allowed? + Gitlab::LDAP::Access.allowed?(gl_user) + end end end end diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb index 699258baee4..133445d3d05 100644 --- a/lib/gitlab/oauth/user.rb +++ b/lib/gitlab/oauth/user.rb @@ -70,10 +70,6 @@ module Gitlab Gitlab::AppLogger end - def raise_error(message) - raise OmniAuth::Error, "(OAuth) " + message - end - def needs_blocking? Gitlab.config.omniauth['block_auto_created_users'] end |