From 01b791237cf6a1b7deaee3da3df6541e0b5107d1 Mon Sep 17 00:00:00 2001 From: Jan-Willem van der Meer Date: Mon, 13 Oct 2014 17:24:05 +0200 Subject: Refactor lib files for multiple LDAP groups --- lib/gitlab/ldap/access.rb | 32 +++++++---- lib/gitlab/ldap/adapter.rb | 63 +++++---------------- lib/gitlab/ldap/authentication.rb | 68 ++++++++++++++++++++++ lib/gitlab/ldap/config.rb | 115 ++++++++++++++++++++++++++++++++++++++ lib/gitlab/ldap/person.rb | 34 ++++++----- lib/gitlab/ldap/user.rb | 48 +++------------- 6 files changed, 248 insertions(+), 112 deletions(-) create mode 100644 lib/gitlab/ldap/authentication.rb create mode 100644 lib/gitlab/ldap/config.rb (limited to 'lib/gitlab/ldap') diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index d2235d2e3bc..111c750226f 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,26 @@ 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 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..0eca9b26133 --- /dev/null +++ b/lib/gitlab/ldap/authentication.rb @@ -0,0 +1,68 @@ +# 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 + + 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) + Net::LDAP::Filter.eq(config.uid, login).tap do |filter| + # Apply LDAP user filter if present + if config.user_filter.present? + Net::LDAP::Filter.join( + filter, + Net::LDAP::Filter.construct(config.user_filter) + ) + end + end + 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..697b66dcdaa --- /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 + end + + def self.providers + servers.map &: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.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..a35fd22073e 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(Gitlab.config.ldap.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,22 +38,30 @@ module Gitlab uid end + def email + entry.try(:mail) + end + def dn entry.dn end + def ssh_keys + if config.sync_ssh_keys? && entry.respond_to?(config.sync_ssh_keys) + entry[config.sync_ssh_keys.to_sym] + else + [] + end + end + private def entry @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..3069027a421 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 @@ -65,7 +31,7 @@ module Gitlab def find_by_uid_and_provider # LDAP distinguished name is case-insensitive model. - where(provider: auth_hash.provider). + where(provider: [auth_hash.provider, :ldap]). where('lower(extern_uid) = ?', auth_hash.uid.downcase).last end @@ -88,6 +54,10 @@ module Gitlab def needs_blocking? false end + + def allowed? + Gitlab::LDAP::Access.allowed?(gl_user) + end end end end -- cgit v1.2.1