Commit dfcea8ed authored by Alex Lossent's avatar Alex Lossent

Add option to automatically link omniauth and LDAP identities

Until now, a user needed to first sign in with his LDAP identity and then manually
link his/her account with an omniauth identity from their profile.
Only when this is done can the user authenticate with the omniauth provider and at
the same time benefit from the LDAP integration (HTTPS authentication with LDAP
username/password and in EE: LDAP groups, SSH keys etc.).
This feature automates the process by looking up a corresponding LDAP person when a
user connects with omniauth for the first time and then automatically linking the LDAP
and omniauth identities (of course, like the existing allow_single_sign_on setting,
this is meant to be used with trusted omniauth providers).
The result is identical to a manual account link.

Add config initializers for other omniauth settings.
parent 41d4aaa4
...@@ -41,6 +41,7 @@ v 7.12.0 (unreleased) ...@@ -41,6 +41,7 @@ v 7.12.0 (unreleased)
- Add an option to automatically sign-in with an Omniauth provider - Add an option to automatically sign-in with an Omniauth provider
- Better performance for web editor (switched from satellites to rugged) - Better performance for web editor (switched from satellites to rugged)
- GitLab CI service sends .gitlab-ci.yaml in each push call - GitLab CI service sends .gitlab-ci.yaml in each push call
- Add option to automatically link omniauth and LDAP identities
v 7.11.4 v 7.11.4
- Fix missing bullets when creating lists - Fix missing bullets when creating lists
......
...@@ -192,6 +192,9 @@ production: &base ...@@ -192,6 +192,9 @@ production: &base
allow_single_sign_on: false allow_single_sign_on: false
# Locks down those users until they have been cleared by the admin (default: true). # Locks down those users until they have been cleared by the admin (default: true).
block_auto_created_users: true block_auto_created_users: true
# Look up new users in LDAP servers. If a match is found (same uid), automatically
# link the omniauth identity with the LDAP account. (default: false)
auto_link_ldap_user: false
## Auth providers ## Auth providers
# Uncomment the following lines and fill in the data of the auth provider you want to use # Uncomment the following lines and fill in the data of the auth provider you want to use
......
...@@ -88,6 +88,9 @@ end ...@@ -88,6 +88,9 @@ 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?
Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil? Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil?
Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil?
Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block_auto_created_users'].nil?
Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
Settings.omniauth['providers'] ||= [] Settings.omniauth['providers'] ||= []
......
...@@ -46,6 +46,10 @@ module Gitlab ...@@ -46,6 +46,10 @@ module Gitlab
def gl_user def gl_user
@user ||= find_by_uid_and_provider @user ||= find_by_uid_and_provider
if auto_link_ldap_user?
@user ||= find_or_create_ldap_user
end
if signup_enabled? if signup_enabled?
@user ||= build_new_user @user ||= build_new_user
end end
...@@ -55,8 +59,52 @@ module Gitlab ...@@ -55,8 +59,52 @@ module Gitlab
protected protected
def find_or_create_ldap_user
return unless ldap_person
#If a corresponding person exists with same uid in a LDAP server,
#set up a Gitlab user with dual LDAP and Omniauth identities.
if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn.downcase, ldap_person.provider)
#case when a LDAP user already exists in Gitlab. Add the Omniauth identity to existing account.
user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
else
#no account in Gitlab yet: create it and add the LDAP identity
user = build_new_user
user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn)
end
user
end
def auto_link_ldap_user?
Gitlab.config.omniauth.auto_link_ldap_user
end
def creating_linked_ldap_user?
auto_link_ldap_user? && ldap_person
end
def ldap_person
return @ldap_person if defined?(@ldap_person)
#looks for a corresponding person with same uid in any of the configured LDAP providers
Gitlab::LDAP::Config.providers.each do |provider|
adapter = Gitlab::LDAP::Adapter.new(provider)
@ldap_person = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter)
break if @ldap_person #exit on first person found
end
@ldap_person #may be nil if we could not find a match
end
def ldap_config
ldap_person && Gitlab::LDAP::Config.new(ldap_person.provider)
end
def needs_blocking? def needs_blocking?
new? && block_after_signup? return false unless new?
if creating_linked_ldap_user?
ldap_config.block_auto_created_users
else
block_after_signup?
end
end end
def signup_enabled? def signup_enabled?
...@@ -84,10 +132,16 @@ module Gitlab ...@@ -84,10 +132,16 @@ module Gitlab
end end
def user_attributes def user_attributes
{ # Give preference to LDAP for sensitive information when creating a linked account
username, email = if creating_linked_ldap_user?
[ ldap_person.username, ldap_person.email.first ]
else
[ auth_hash.username, auth_hash.email ]
end
return {
name: auth_hash.name, name: auth_hash.name,
username: ::Namespace.clean_path(auth_hash.username), username: ::Namespace.clean_path(username),
email: auth_hash.email, email: email,
password: auth_hash.password, password: auth_hash.password,
password_confirmation: auth_hash.password, password_confirmation: auth_hash.password,
password_automatically_set: true password_automatically_set: true
......
...@@ -13,6 +13,7 @@ describe Gitlab::OAuth::User do ...@@ -13,6 +13,7 @@ describe Gitlab::OAuth::User do
email: 'john@mail.com' email: 'john@mail.com'
} }
end end
let(:ldap_user) { Gitlab::LDAP::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
describe :persisted? do describe :persisted? do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
...@@ -32,6 +33,7 @@ describe Gitlab::OAuth::User do ...@@ -32,6 +33,7 @@ describe Gitlab::OAuth::User do
let(:provider) { 'twitter' } let(:provider) { 'twitter' }
describe 'signup' do describe 'signup' do
shared_examples "to verify compliance with allow_single_sign_on" do
context "with allow_single_sign_on enabled" do context "with allow_single_sign_on enabled" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: true } before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
...@@ -46,17 +48,79 @@ describe Gitlab::OAuth::User do ...@@ -46,17 +48,79 @@ describe Gitlab::OAuth::User do
end end
context "with allow_single_sign_on disabled (Default)" do context "with allow_single_sign_on disabled (Default)" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: false }
it "throws an error" do it "throws an error" do
expect{ oauth_user.save }.to raise_error StandardError expect{ oauth_user.save }.to raise_error StandardError
end end
end end
end end
context "with auto_link_ldap_user disabled (default)" do
before { Gitlab.config.omniauth.stub auto_link_ldap_user: false }
include_examples "to verify compliance with allow_single_sign_on"
end
context "with auto_link_ldap_user enabled" do
before { Gitlab.config.omniauth.stub auto_link_ldap_user: true }
context "and a corresponding LDAP person" do
before do
ldap_user.stub(:uid) { uid }
ldap_user.stub(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
end
context "and no account for the LDAP user" do
it "creates a user with dual LDAP and omniauth identities" do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
expect(gl_user.email).to eql 'johndoe@example.com'
expect(gl_user.identities.length).to eql 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array(
[ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
{ provider: 'twitter', extern_uid: uid }
])
end
end
context "and LDAP user has an account already" do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
it "adds the omniauth identity to the LDAP account" do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
expect(gl_user.email).to eql 'john@example.com'
expect(gl_user.identities.length).to eql 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array(
[ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
{ provider: 'twitter', extern_uid: uid }
])
end
end
end
context "and no corresponding LDAP person" do
before { allow(oauth_user).to receive(:ldap_person).and_return(nil) }
include_examples "to verify compliance with allow_single_sign_on"
end
end
end
describe 'blocking' do describe 'blocking' do
let(:provider) { 'twitter' } let(:provider) { 'twitter' }
before { Gitlab.config.omniauth.stub allow_single_sign_on: true } before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
context 'signup' do context 'signup with omniauth only' do
context 'dont block on create' do context 'dont block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: false } before { Gitlab.config.omniauth.stub block_auto_created_users: false }
...@@ -78,6 +142,64 @@ describe Gitlab::OAuth::User do ...@@ -78,6 +142,64 @@ describe Gitlab::OAuth::User do
end end
end end
context 'signup with linked omniauth and LDAP account' do
before do
Gitlab.config.omniauth.stub auto_link_ldap_user: true
ldap_user.stub(:uid) { uid }
ldap_user.stub(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
end
context "and no account for the LDAP user" do
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).to be_blocked
end
end
end
context 'and LDAP user has an account already' do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
end
end
context 'sign-in' do context 'sign-in' do
before do before do
oauth_user.save oauth_user.save
...@@ -103,6 +225,26 @@ describe Gitlab::OAuth::User do ...@@ -103,6 +225,26 @@ describe Gitlab::OAuth::User do
expect(gl_user).not_to be_blocked expect(gl_user).not_to be_blocked
end end
end end
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
end end
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