Refactor lib files for multiple LDAP groups

parent 6c0994a3
...@@ -3,11 +3,13 @@ module Gitlab ...@@ -3,11 +3,13 @@ module Gitlab
def find(login, password) def find(login, password)
user = User.find_by(email: login) || User.find_by(username: login) 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? if user.nil? || user.ldap_user?
# Second chance - try LDAP authentication # 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 else
user if user.valid_password?(password) user if user.valid_password?(password)
end end
......
# LDAP authorization model
#
# * Check if we are allowed access (not blocked)
#
module Gitlab module Gitlab
module LDAP module LDAP
class Access class Access
attr_reader :adapter attr_reader :adapter, :provider, :user
def self.open(&block) def self.open(user, &block)
Gitlab::LDAP::Adapter.open do |adapter| Gitlab::LDAP::Adapter.open(user.provider) do |adapter|
block.call(self.new(adapter)) block.call(self.new(user, adapter))
end end
end end
def self.allowed?(user) def self.allowed?(user)
self.open do |access| self.open(user) do |access|
if access.allowed?(user) if access.allowed?
# GitLab EE LDAP code goes here
user.last_credential_check_at = Time.now user.last_credential_check_at = Time.now
user.save user.save
true true
...@@ -22,21 +25,26 @@ module Gitlab ...@@ -22,21 +25,26 @@ module Gitlab
end end
end end
def initialize(adapter=nil) def initialize(user, adapter=nil)
@adapter = adapter @adapter = adapter
@user = user
@provider = user.provider
end end
def allowed?(user) def allowed?
if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter) if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
if Gitlab.config.ldap.active_directory return true unless ldap_config.active_directory
!Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter) !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter)
end
else else
false false
end end
rescue rescue
false false
end end
def adapter
@adapter ||= Gitlab::LDAP::Adapter.new(provider)
end
end end
end end
end end
module Gitlab module Gitlab
module LDAP module LDAP
class Adapter class Adapter
attr_reader :ldap attr_reader :provider, :ldap
def self.open(&block) def self.open(provider, &block)
Net::LDAP.open(adapter_options) do |ldap| Net::LDAP.open(config(provider).adapter_options) do |ldap|
block.call(self.new(ldap)) block.call(self.new(provider, ldap))
end end
end end
def self.config def self.config(provider)
Gitlab.config.ldap Gitlab::LDAP::Config.new(provider)
end end
def self.adapter_options def initialize(provider, ldap=nil)
encryption = @provider = provider
case config['method'].to_s @ldap = ldap || Net::LDAP.new(config.adapter_options)
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
end end
def config
def initialize(ldap=nil) Gitlab::LDAP::Config.new(provider)
@ldap = ldap || Net::LDAP.new(self.class.adapter_options)
end end
def users(field, value) def users(field, value)
...@@ -57,13 +30,13 @@ module Gitlab ...@@ -57,13 +30,13 @@ module Gitlab
} }
else else
options = { options = {
base: config['base'], base: config.base,
filter: Net::LDAP::Filter.eq(field, value) filter: Net::LDAP::Filter.eq(field, value)
} }
end end
if config['user_filter'].present? if config.user_filter.present?
user_filter = Net::LDAP::Filter.construct(config['user_filter']) user_filter = Net::LDAP::Filter.construct(config.user_filter)
options[:filter] = if options[:filter] options[:filter] = if options[:filter]
Net::LDAP::Filter.join(options[:filter], user_filter) Net::LDAP::Filter.join(options[:filter], user_filter)
...@@ -77,7 +50,7 @@ module Gitlab ...@@ -77,7 +50,7 @@ module Gitlab
end end
entries.map do |entry| entries.map do |entry|
Gitlab::LDAP::Person.new(entry) Gitlab::LDAP::Person.new(entry, provider)
end end
end end
...@@ -105,12 +78,6 @@ module Gitlab ...@@ -105,12 +78,6 @@ module Gitlab
results results
end end
end end
private
def config
@config ||= self.class.config
end
end end
end end
end end
# 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
# 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
...@@ -6,24 +6,24 @@ module Gitlab ...@@ -6,24 +6,24 @@ module Gitlab
# Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/ # 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") AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2")
def self.find_by_uid(uid, adapter=nil) attr_accessor :entry, :provider
adapter ||= Gitlab::LDAP::Adapter.new
adapter.user(config.uid, uid) def self.find_by_uid(uid, adapter)
adapter.user(Gitlab.config.ldap.uid, uid)
end end
def self.find_by_dn(dn, adapter=nil) def self.find_by_dn(dn, adapter)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.user('dn', dn) adapter.user('dn', dn)
end end
def self.disabled_via_active_directory?(dn, adapter=nil) def self.disabled_via_active_directory?(dn, adapter)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.dn_matches_filter?(dn, AD_USER_DISABLED) adapter.dn_matches_filter?(dn, AD_USER_DISABLED)
end end
def initialize(entry) def initialize(entry, provider)
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry @entry = entry
@provider = provider
end end
def name def name
...@@ -38,22 +38,30 @@ module Gitlab ...@@ -38,22 +38,30 @@ module Gitlab
uid uid
end end
def email
entry.try(:mail)
end
def dn def dn
entry.dn entry.dn
end 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 private
def entry def entry
@entry @entry
end end
def adapter
@adapter ||= Gitlab::LDAP::Adapter.new
end
def config def config
@config ||= Gitlab.config.ldap @config ||= Gitlab::LDAP::Config.new(provider)
end end
end end
end end
......
...@@ -10,45 +10,11 @@ module Gitlab ...@@ -10,45 +10,11 @@ module Gitlab
module LDAP module LDAP
class User < Gitlab::OAuth::User class User < Gitlab::OAuth::User
class << self class << self
def authenticate(login, password) def find_by_uid_and_provider(uid, provider)
# 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)
# LDAP distinguished name is case-insensitive # LDAP distinguished name is case-insensitive
model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last ::User.
end where(provider: [provider, :ldap]).
where('lower(extern_uid) = ?', uid.downcase).last
def provider
'ldap'
end end
end end
...@@ -65,7 +31,7 @@ module Gitlab ...@@ -65,7 +31,7 @@ module Gitlab
def find_by_uid_and_provider def find_by_uid_and_provider
# LDAP distinguished name is case-insensitive # LDAP distinguished name is case-insensitive
model. model.
where(provider: auth_hash.provider). where(provider: [auth_hash.provider, :ldap]).
where('lower(extern_uid) = ?', auth_hash.uid.downcase).last where('lower(extern_uid) = ?', auth_hash.uid.downcase).last
end end
...@@ -88,6 +54,10 @@ module Gitlab ...@@ -88,6 +54,10 @@ module Gitlab
def needs_blocking? def needs_blocking?
false false
end end
def allowed?
Gitlab::LDAP::Access.allowed?(gl_user)
end
end end
end end
end end
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Access do describe Gitlab::LDAP::Access do
let(:access) { Gitlab::LDAP::Access.new } let(:access) { Gitlab::LDAP::Access.new user }
let(:user) { create(:user) } let(:user) { create(:user, :ldap) }
describe :allowed? do describe :allowed? do
subject { access.allowed?(user) } subject { access.allowed? }
context 'when the user cannot be found' do context 'when the user cannot be found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: nil) } before { Gitlab::LDAP::Person.stub(find_by_dn: nil) }
...@@ -28,20 +28,14 @@ describe Gitlab::LDAP::Access do ...@@ -28,20 +28,14 @@ describe Gitlab::LDAP::Access do
it { should be_true } it { should be_true }
end end
context 'and has no disabled flag in active diretory' do context 'without ActiveDirectory enabled' do
before { before do
Gitlab::LDAP::Person.stub(disabled_via_active_directory?: false) Gitlab::LDAP::Config.stub(enabled?: true)
Gitlab.config.ldap['enabled'] = true Gitlab::LDAP::Config.any_instance.stub(active_directory: false)
Gitlab.config.ldap['active_directory'] = false end
}
after {
Gitlab.config.ldap['enabled'] = false
Gitlab.config.ldap['active_directory'] = true
}
it { should be_false } it { should be_true }
end end
end end
end end
end end
\ No newline at end of file
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