Commit e3bd17a7 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

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
parents 4bebdc09 b4f7b387
...@@ -24,7 +24,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -24,7 +24,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
gl_user.remember_me = true if @user.persisted? gl_user.remember_me = true if @user.persisted?
# Do additional LDAP checks for the user filter and EE features # Do additional LDAP checks for the user filter and EE features
if Gitlab::LDAP::Access.allowed?(gl_user) if @user.allowed?
sign_in_and_redirect(gl_user) sign_in_and_redirect(gl_user)
else else
flash[:alert] = "Access denied for your LDAP account." flash[:alert] = "Access denied for your LDAP account."
......
...@@ -18,6 +18,10 @@ class SessionsController < Devise::SessionsController ...@@ -18,6 +18,10 @@ class SessionsController < Devise::SessionsController
store_location_for(:redirect, redirect_path) store_location_for(:redirect, redirect_path)
end end
if Gitlab.config.ldap.enabled
@ldap_servers = Gitlab::LDAP::Config.servers
end
super super
end end
......
module OauthHelper module OauthHelper
def ldap_enabled? def ldap_enabled?
Devise.omniauth_providers.include?(:ldap) Gitlab.config.ldap.enabled
end end
def default_providers def default_providers
......
...@@ -178,8 +178,7 @@ class User < ActiveRecord::Base ...@@ -178,8 +178,7 @@ class User < ActiveRecord::Base
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
scope :ldap, -> { where(provider: 'ldap') } scope :ldap, -> { where('provider LIKE ?', 'ldap%') }
scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
# #
...@@ -407,7 +406,7 @@ class User < ActiveRecord::Base ...@@ -407,7 +406,7 @@ class User < ActiveRecord::Base
end end
def ldap_user? def ldap_user?
extern_uid && provider == 'ldap' extern_uid && provider.start_with?('ldap')
end end
def accessible_deploy_keys def accessible_deploy_keys
......
= form_tag(user_omniauth_callback_path(:ldap), id: 'new_ldap_user' ) do = form_tag(user_omniauth_callback_path(provider), id: 'new_ldap_user' ) do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"} = text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"} = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
%br/ %br/
......
...@@ -4,20 +4,22 @@ ...@@ -4,20 +4,22 @@
.login-body .login-body
- if ldap_enabled? && gitlab_config.signin_enabled - if ldap_enabled? && gitlab_config.signin_enabled
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li.active - @ldap_servers.each_with_index do |server, i|
= link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab' %li{class: (:active if i==0)}
= link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
%li %li
= link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
.tab-content .tab-content
%div#tab-ldap.tab-pane.active - @ldap_servers.each_with_index do |server,i|
= render partial: 'devise/sessions/new_ldap' %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i==0)}
= render 'devise/sessions/new_ldap', provider: server['provider_name']
%div#tab-signin.tab-pane %div#tab-signin.tab-pane
= render partial: 'devise/sessions/new_base' = render 'devise/sessions/new_base'
- elsif ldap_enabled? - elsif ldap_enabled?
= render partial: 'devise/sessions/new_ldap' = render 'devise/sessions/new_ldap', ldap_servers: @ldap_servers
- elsif gitlab_config.signin_enabled - elsif gitlab_config.signin_enabled
= render partial: 'devise/sessions/new_base' = render 'devise/sessions/new_base'
- else - else
%div %div
No authentication methods configured. No authentication methods configured.
...@@ -36,7 +38,6 @@ ...@@ -36,7 +38,6 @@
%span.light Did not receive confirmation email? %span.light Did not receive confirmation email?
= link_to "Send again", new_confirmation_path(resource_name) = link_to "Send again", new_confirmation_path(resource_name)
- if extra_config.has_key?('sign_in_text') - if extra_config.has_key?('sign_in_text')
%hr %hr
= markdown(extra_config.sign_in_text) = markdown(extra_config.sign_in_text)
...@@ -135,43 +135,61 @@ production: &base ...@@ -135,43 +135,61 @@ production: &base
# bundle exec rake gitlab:ldap:check RAILS_ENV=production # bundle exec rake gitlab:ldap:check RAILS_ENV=production
ldap: ldap:
enabled: false enabled: false
host: '_your_ldap_server' servers:
port: 636 main: # 'main' is the GitLab 'provider ID' of this LDAP server
uid: 'sAMAccountName' ## label
method: 'ssl' # "tls" or "ssl" or "plain" #
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' # A human-friendly name for your LDAP server. It is OK to change the label later,
password: '_the_password_of_the_bind_user' # for instance if you find out it is too large to fit on the web page.
#
# This setting specifies if LDAP server is Active Directory LDAP server. # Example: 'Paris' or 'Acme, Ltd.'
# For non AD servers it skips the AD specific queries. label: 'LDAP'
# If your LDAP server is not AD, set this to false.
active_directory: true host: '_your_ldap_server'
port: 636
# If allow_username_or_email_login is enabled, GitLab will ignore everything uid: 'sAMAccountName'
# after the first '@' in the LDAP username submitted by the user on login. method: 'ssl' # "tls" or "ssl" or "plain"
# bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
# Example: password: '_the_password_of_the_bind_user'
# - the user enters 'jane.doe@example.com' and 'p@ssw0rd' as LDAP credentials;
# - GitLab queries the LDAP server with 'jane.doe' and 'p@ssw0rd'. # This setting specifies if LDAP server is Active Directory LDAP server.
# # For non AD servers it skips the AD specific queries.
# If you are using "uid: 'userPrincipalName'" on ActiveDirectory you need to # If your LDAP server is not AD, set this to false.
# disable this setting, because the userPrincipalName contains an '@'. active_directory: true
allow_username_or_email_login: false
# If allow_username_or_email_login is enabled, GitLab will ignore everything
# Base where we can search for users # after the first '@' in the LDAP username submitted by the user on login.
# #
# Ex. ou=People,dc=gitlab,dc=example # Example:
# # - the user enters 'jane.doe@example.com' and 'p@ssw0rd' as LDAP credentials;
base: '' # - GitLab queries the LDAP server with 'jane.doe' and 'p@ssw0rd'.
#
# Filter LDAP users # If you are using "uid: 'userPrincipalName'" on ActiveDirectory you need to
# # disable this setting, because the userPrincipalName contains an '@'.
# Format: RFC 4515 http://tools.ietf.org/search/rfc4515 allow_username_or_email_login: false
# Ex. (employeeType=developer)
# # Base where we can search for users
# Note: GitLab does not support omniauth-ldap's custom filter syntax. #
# # Ex. ou=People,dc=gitlab,dc=example
user_filter: '' #
base: ''
# Filter LDAP users
#
# Format: RFC 4515 http://tools.ietf.org/search/rfc4515
# Ex. (employeeType=developer)
#
# Note: GitLab does not support omniauth-ldap's custom filter syntax.
#
user_filter: ''
# GitLab EE only: add more LDAP servers
# Choose an ID made of a-z and 0-9 . This ID will be stored in the database
# so that GitLab can remember which LDAP server a user belongs to.
# uswest2:
# label:
# host:
# ....
## OmniAuth settings ## OmniAuth settings
...@@ -300,6 +318,20 @@ test: ...@@ -300,6 +318,20 @@ test:
project_url: "http://redmine/projects/:issues_tracker_id" project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new" new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
ldap:
enabled: false
servers:
main:
label: ldap
host: 127.0.0.1
port: 3890
uid: 'uid'
method: 'plain' # "tls" or "ssl" or "plain"
base: 'dc=example,dc=com'
user_filter: ''
group_base: 'ou=groups,dc=example,dc=com'
admin_group: ''
sync_ssh_keys: false
staging: staging:
<<: *base <<: *base
...@@ -56,9 +56,25 @@ end ...@@ -56,9 +56,25 @@ end
# Default settings # Default settings
Settings['ldap'] ||= Settingslogic.new({}) Settings['ldap'] ||= Settingslogic.new({})
Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil?
Settings.ldap['allow_username_or_email_login'] = false if Settings.ldap['allow_username_or_email_login'].nil?
Settings.ldap['active_directory'] = true if Settings.ldap['active_directory'].nil?
# backwards compatibility, we only have one host
if Settings.ldap['enabled'] || Rails.env.test?
if Settings.ldap['host'].present?
server = Settings.ldap.except('sync_time')
server['label'] = 'LDAP'
server['provider_name'] = 'ldap'
Settings.ldap['servers'] = {
'ldap' => server
}
end
Settings.ldap['servers'].each do |key, server|
server['allow_username_or_email_login'] = false if server['allow_username_or_email_login'].nil?
server['active_directory'] = true if server['active_directory'].nil?
server['provider_name'] ||= "ldap#{key}".downcase
server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name'])
end
end
Settings['omniauth'] ||= Settingslogic.new({}) Settings['omniauth'] ||= Settingslogic.new({})
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
......
module OmniAuth::Strategies
server = Gitlab.config.ldap.servers.values.first
const_set(server['provider_class'], Class.new(LDAP))
end
OmniauthCallbacksController.class_eval do
server = Gitlab.config.ldap.servers.values.first
alias_method server['provider_name'], :ldap
end
\ No newline at end of file
...@@ -205,21 +205,23 @@ Devise.setup do |config| ...@@ -205,21 +205,23 @@ Devise.setup do |config|
# end # end
if Gitlab.config.ldap.enabled if Gitlab.config.ldap.enabled
if Gitlab.config.ldap.allow_username_or_email_login Gitlab.config.ldap.servers.values.each do |server|
email_stripping_proc = ->(name) {name.gsub(/@.*$/,'')} if server['allow_username_or_email_login']
else email_stripping_proc = ->(name) {name.gsub(/@.*$/,'')}
email_stripping_proc = ->(name) {name} else
email_stripping_proc = ->(name) {name}
end
config.omniauth server['provider_name'],
host: server['host'],
base: server['base'],
uid: server['uid'],
port: server['port'],
method: server['method'],
bind_dn: server['bind_dn'],
password: server['password'],
name_proc: email_stripping_proc
end end
config.omniauth :ldap,
host: Gitlab.config.ldap['host'],
base: Gitlab.config.ldap['base'],
uid: Gitlab.config.ldap['uid'],
port: Gitlab.config.ldap['port'],
method: Gitlab.config.ldap['method'],
bind_dn: Gitlab.config.ldap['bind_dn'],
password: Gitlab.config.ldap['password'],
name_proc: email_stripping_proc
end end
Gitlab.config.omniauth.providers.each do |provider| Gitlab.config.omniauth.providers.each do |provider|
......
...@@ -3,22 +3,16 @@ module Gitlab ...@@ -3,22 +3,16 @@ 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
end end
def log
Gitlab::AppLogger
end
def ldap_conf
@ldap_conf ||= Gitlab.config.ldap
end
end end
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,30 @@ module Gitlab ...@@ -22,21 +25,30 @@ 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
def ldap_config
Gitlab::LDAP::Config.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
# 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
# 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
...@@ -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(adapter.config.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,6 +38,10 @@ module Gitlab ...@@ -38,6 +38,10 @@ module Gitlab
uid uid
end end
def email
entry.try(:mail)
end
def dn def dn
entry.dn entry.dn
end end
...@@ -48,12 +52,8 @@ module Gitlab ...@@ -48,12 +52,8 @@ module Gitlab
@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
...@@ -63,10 +29,8 @@ module Gitlab ...@@ -63,10 +29,8 @@ module Gitlab
end end
def find_by_uid_and_provider def find_by_uid_and_provider
# LDAP distinguished name is case-insensitive self.class.find_by_uid_and_provider(
model. auth_hash.uid.downcase, auth_hash.provider)
where(provider: auth_hash.provider).
where('lower(extern_uid) = ?', auth_hash.uid.downcase).last
end end
def find_by_email def find_by_email
...@@ -88,6 +52,10 @@ module Gitlab ...@@ -88,6 +52,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
...@@ -70,10 +70,6 @@ module Gitlab ...@@ -70,10 +70,6 @@ module Gitlab
Gitlab::AppLogger Gitlab::AppLogger
end end
def raise_error(message)
raise OmniAuth::Error, "(OAuth) " + message
end
def needs_blocking? def needs_blocking?
Gitlab.config.omniauth['block_auto_created_users'] Gitlab.config.omniauth['block_auto_created_users']
end end
......
...@@ -24,6 +24,11 @@ FactoryGirl.define do ...@@ -24,6 +24,11 @@ FactoryGirl.define do
admin true admin true
end end
trait :ldap do
provider 'ldapmain'
extern_uid 'my-ldap-id'
end
factory :admin, traits: [:admin] factory :admin, traits: [:admin]
end end
......
...@@ -28,17 +28,16 @@ describe Gitlab::Auth do ...@@ -28,17 +28,16 @@ describe Gitlab::Auth do
end end
context "with ldap enabled" do context "with ldap enabled" do
before { Gitlab.config.ldap['enabled'] = true } before { Gitlab::LDAP::Config.stub(enabled?: true) }
after { Gitlab.config.ldap['enabled'] = false }
it "tries to autheticate with db before ldap" do it "tries to autheticate with db before ldap" do
expect(Gitlab::LDAP::User).not_to receive(:authenticate) expect(Gitlab::LDAP::Authentication).not_to receive(:login)
gl_auth.find(username, password) gl_auth.find(username, password)
end end
it "uses ldap as fallback to for authentication" do it "uses ldap as fallback to for authentication" do
expect(Gitlab::LDAP::User).to receive(:authenticate) expect(Gitlab::LDAP::Authentication).to receive(:login)
gl_auth.find('ldap_user', 'password') gl_auth.find('ldap_user', 'password')
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
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Adapter do describe Gitlab::LDAP::Adapter do
let(:adapter) { Gitlab::LDAP::Adapter.new } let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' }
describe :dn_matches_filter? do describe :dn_matches_filter? do
let(:ldap) { double(:ldap) } let(:ldap) { double(:ldap) }
......
require 'spec_helper'
describe Gitlab::LDAP::Authentication do
let(:klass) { Gitlab::LDAP::Authentication }
let(:user) { create(:user, :ldap, extern_uid: dn) }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:login) { 'john' }
let(:password) { 'password' }
describe :login do
let(:adapter) { double :adapter }
before do
Gitlab::LDAP::Config.stub(enabled?: true)
end
it "finds the user if authentication is successful" do
user
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
expect(klass.login(login, password)).to be_true
end
it "is false if the user does not exist" do
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
expect(klass.login(login, password)).to be_false
end
it "is false if authentication fails" do
user
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, bind_as: nil))
expect(klass.login(login, password)).to be_false
end
it "fails if ldap is disabled" do
Gitlab::LDAP::Config.stub(enabled?: false)
expect(klass.login(login, password)).to be_false
end
it "fails if no login is supplied" do
expect(klass.login('', password)).to be_false
end
it "fails if no password is supplied" do
expect(klass.login(login, '')).to be_false
end
end
end
\ No newline at end of file
require 'spec_helper'
describe Gitlab::LDAP::Config do
let(:config) { Gitlab::LDAP::Config.new provider }
let(:provider) { 'ldapmain' }
describe :initalize do
it 'requires a provider' do
expect{ Gitlab::LDAP::Config.new }.to raise_error ArgumentError
end
it "works" do
expect(config).to be_a described_class
end
it "raises an error if a unknow provider is used" do
expect{ Gitlab::LDAP::Config.new 'unknown' }.to raise_error
end
end
end
\ No newline at end of file
...@@ -10,12 +10,12 @@ describe Gitlab::LDAP::User do ...@@ -10,12 +10,12 @@ describe Gitlab::LDAP::User do
} }
end end
let(:auth_hash) do let(:auth_hash) do
double(uid: 'my-uid', provider: 'ldap', info: double(info)) double(uid: 'my-uid', provider: 'ldapmain', info: double(info))
end end
describe :find_or_create do describe :find_or_create do
it "finds the user if already existing" do it "finds the user if already existing" do
existing_user = create(:user, extern_uid: 'my-uid', provider: 'ldap') existing_user = create(:user, extern_uid: 'my-uid', provider: 'ldapmain')
expect{ gl_user.save }.to_not change{ User.count } expect{ gl_user.save }.to_not change{ User.count }
end end
...@@ -26,27 +26,11 @@ describe Gitlab::LDAP::User do ...@@ -26,27 +26,11 @@ describe Gitlab::LDAP::User do
existing_user.reload existing_user.reload
expect(existing_user.extern_uid).to eql 'my-uid' expect(existing_user.extern_uid).to eql 'my-uid'
expect(existing_user.provider).to eql 'ldap' expect(existing_user.provider).to eql 'ldapmain'
end end
it "creates a new user if not found" do it "creates a new user if not found" do
expect{ gl_user.save }.to change{ User.count }.by(1) expect{ gl_user.save }.to change{ User.count }.by(1)
end end
end end
describe "authenticate" do
let(:login) { 'john' }
let(:password) { 'my-secret' }
before {
Gitlab.config.ldap['enabled'] = true
Gitlab.config.ldap['user_filter'] = 'employeeType=developer'
}
after { Gitlab.config.ldap['enabled'] = false }
it "send an authentication request to ldap" do
expect( Gitlab::LDAP::User.adapter ).to receive(:bind_as)
Gitlab::LDAP::User.authenticate(login, password)
end
end
end end
...@@ -346,6 +346,25 @@ describe User do ...@@ -346,6 +346,25 @@ describe User do
end end
end end
describe :ldap_user? do
let(:user) { build(:user, :ldap) }
it "is true if provider name starts with ldap" do
user.provider = 'ldapmain'
expect( user.ldap_user? ).to be_true
end
it "is false for other providers" do
user.provider = 'other-provider'
expect( user.ldap_user? ).to be_false
end
it "is false if no extern_uid is provided" do
user.extern_uid = nil
expect( user.ldap_user? ).to be_false
end
end
describe '#full_website_url' do describe '#full_website_url' do
let(:user) { create(:user) } let(:user) { create(:user) }
......
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