Commit 65c3b156 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'bvl-ldap-find-user-by-email' into 'master'

Find a user by email from LDAP

Closes gitlab-ce#22924 and #2522

See merge request !2003
parents e5669cb2 14f0aa51
---
title: Lookup users by email in LDAP if lookup by DN fails during sync
merge_request: 2003
author:
...@@ -248,23 +248,10 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server ...@@ -248,23 +248,10 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
### User DN has changed ### User DN has changed
When an LDAP user is created in GitLab, their LDAP DN is stored for later reference. When an LDAP user is created in GitLab, their LDAP DN is stored for later reference.
If a user's DN changes, it can cause problems for LDAP sync. Administrators can
manually update a user's stored DN in this case. If GitLab cannot find a user by their DN, it will attempt to fallback
to finding the user by their email. If the lookup is successful, GitLab will
> **Note:** If GitLab cannot find a user by their DN, it will attempt to fallback update the stored DN to the new value.
to finding the user by their email. If the lookup is successful, GitLab will
update the stored DN to the new value.
1. Sign in to GitLab as an administrator user.
1. Navigate to **Admin area -> Users**.
1. Search for the user
1. Open the user, by clicking on their name. Do not click 'Edit'.
1. Navigate to the **Identities** tab.
1. Click 'Edit' next to the LDAP identity.
1. Change the 'Identifier' to match the user's new LDAP DN.
1. Save the identity.
Now the user should sync correctly.
### User is not being added to a group ### User is not being added to a group
......
...@@ -2,6 +2,19 @@ module EE ...@@ -2,6 +2,19 @@ module EE
module Gitlab module Gitlab
module LDAP module LDAP
module Person 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
end
end
def ssh_keys def ssh_keys
if config.sync_ssh_keys? && entry.respond_to?(config.sync_ssh_keys) if config.sync_ssh_keys? && entry.respond_to?(config.sync_ssh_keys)
entry[config.sync_ssh_keys.to_sym] entry[config.sync_ssh_keys.to_sym]
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
module Gitlab module Gitlab
module LDAP module LDAP
class Access class Access
attr_reader :adapter, :provider, :user, :ldap_user attr_reader :adapter, :provider, :user, :ldap_user, :ldap_identity
def self.open(user, &block) def self.open(user, &block)
Gitlab::LDAP::Adapter.open(user.ldap_identity.provider) do |adapter| Gitlab::LDAP::Adapter.open(user.ldap_identity.provider) do |adapter|
...@@ -32,7 +32,8 @@ module Gitlab ...@@ -32,7 +32,8 @@ module Gitlab
def initialize(user, adapter = nil) def initialize(user, adapter = nil)
@adapter = adapter @adapter = adapter
@user = user @user = user
@provider = user.ldap_identity.provider @provider = adapter&.provider || user.ldap_identity.provider
@ldap_identity = user.identities.find_by(provider: @provider)
end end
def allowed? def allowed?
...@@ -43,7 +44,7 @@ module Gitlab ...@@ -43,7 +44,7 @@ module Gitlab
end end
# Block user in GitLab if he/she was blocked in AD # Block user in GitLab if he/she was blocked in AD
if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter) if Gitlab::LDAP::Person.disabled_via_active_directory?(ldap_identity.extern_uid, adapter)
block_user(user, 'is disabled in Active Directory') block_user(user, 'is disabled in Active Directory')
false false
else else
...@@ -65,15 +66,24 @@ module Gitlab ...@@ -65,15 +66,24 @@ module Gitlab
Gitlab::LDAP::Config.new(provider) Gitlab::LDAP::Config.new(provider)
end end
def find_ldap_user
found_user = Gitlab::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter)
return found_user if found_user
if user.ldap_email?
Gitlab::LDAP::Person.find_by_email(user.email, adapter)
end
end
def ldap_user def ldap_user
@ldap_user ||= Gitlab::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter) @ldap_user ||= find_ldap_user
end end
def block_user(user, reason) def block_user(user, reason)
user.ldap_block user.ldap_block
Gitlab::AppLogger.info( Gitlab::AppLogger.info(
"LDAP account \"#{user.ldap_identity.extern_uid}\" #{reason}, " \ "LDAP account \"#{ldap_identity.extern_uid}\" #{reason}, " \
"blocking Gitlab user \"#{user.name}\" (#{user.email})" "blocking Gitlab user \"#{user.name}\" (#{user.email})"
) )
end end
...@@ -82,7 +92,7 @@ module Gitlab ...@@ -82,7 +92,7 @@ module Gitlab
user.activate user.activate
Gitlab::AppLogger.info( Gitlab::AppLogger.info(
"LDAP account \"#{user.ldap_identity.extern_uid}\" #{reason}, " \ "LDAP account \"#{ldap_identity.extern_uid}\" #{reason}, " \
"unblocking Gitlab user \"#{user.name}\" (#{user.email})" "unblocking Gitlab user \"#{user.name}\" (#{user.email})"
) )
end end
...@@ -90,6 +100,7 @@ module Gitlab ...@@ -90,6 +100,7 @@ module Gitlab
def update_user def update_user
update_email update_email
update_memberships update_memberships
update_identity
update_ssh_keys if sync_ssh_keys? update_ssh_keys if sync_ssh_keys?
update_kerberos_identity if import_kerberos_identities? update_kerberos_identity if import_kerberos_identities?
end end
...@@ -156,6 +167,15 @@ module Gitlab ...@@ -156,6 +167,15 @@ module Gitlab
user.update(email: ldap_email) user.update(email: ldap_email)
end end
def update_identity
return if ldap_user.dn.empty? || ldap_user.dn == ldap_identity.extern_uid
unless ldap_identity.update(extern_uid: ldap_user.dn)
Rails.logger.error "Could not update DN for #{user.name} (#{user.id})\n"\
"error messages: #{user.ldap_identity.errors.messages}"
end
end
delegate :sync_ssh_keys?, to: :ldap_config delegate :sync_ssh_keys?, to: :ldap_config
def import_kerberos_identities? def import_kerberos_identities?
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Person do describe Gitlab::LDAP::Person do
include LdapHelpers
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::LDAP::Person)
end end
describe '.find_by_email' do
it 'tries finding for each configured email attribute' do
adapter = ldap_adapter
expect(adapter).to receive(:user).with('mail', 'jane@gitlab.com')
expect(adapter).to receive(:user).with('email', 'jane@gitlab.com')
expect(adapter).to receive(:user).with('userPrincipalName', 'jane@gitlab.com')
described_class.find_by_email('jane@gitlab.com', adapter)
end
end
describe '#kerberos_principal' do describe '#kerberos_principal' do
let(:entry) do let(:entry) do
ldif = "dn: cn=foo, dc=bar, dc=com\n" ldif = "dn: cn=foo, dc=bar, dc=com\n"
......
...@@ -5,6 +5,23 @@ describe Gitlab::LDAP::Access, lib: true do ...@@ -5,6 +5,23 @@ describe Gitlab::LDAP::Access, lib: true do
let(:access) { Gitlab::LDAP::Access.new user } let(:access) { Gitlab::LDAP::Access.new user }
let(:user) { create(:omniauth_user) } let(:user) { create(:omniauth_user) }
describe '#find_ldap_user' do
it 'finds a user by dn first' do
expect(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user)
expect(user).not_to receive(:ldap_email?)
access.find_ldap_user
end
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(user).to receive(:ldap_email?).and_return(true)
expect(Gitlab::LDAP::Person).to receive(:find_by_email)
access.find_ldap_user
end
end
describe '#allowed?' do describe '#allowed?' do
subject { access.allowed? } subject { access.allowed? }
...@@ -193,6 +210,12 @@ describe Gitlab::LDAP::Access, lib: true do ...@@ -193,6 +210,12 @@ describe Gitlab::LDAP::Access, lib: true do
subject subject
end end
it 'updates the ldap identity' do
expect(access).to receive(:update_identity)
subject
end
end end
describe '#update_kerberos_identity' do describe '#update_kerberos_identity' do
...@@ -358,4 +381,19 @@ describe Gitlab::LDAP::Access, lib: true do ...@@ -358,4 +381,19 @@ describe Gitlab::LDAP::Access, lib: true do
access.update_memberships access.update_memberships
end end
end end
describe '#update_identity' do
it 'updates the external UID if it changed in the entry' do
entry = ldap_user_entry('another uid')
provider = user.ldap_identity.provider
person = Gitlab::LDAP::Person.new(entry, provider)
allow(access).to receive(:ldap_user).and_return(person)
access.update_identity
expect(user.ldap_identity.reload.extern_uid)
.to eq('uid=another uid,ou=users,dc=example,dc=com')
end
end
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