summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--app/models/user.rb2
-rw-r--r--db/migrate/20150817163600_deduplicate_user_identities.rb14
-rw-r--r--lib/gitlab/ldap/user.rb11
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb22
5 files changed, 46 insertions, 4 deletions
diff --git a/CHANGELOG b/CHANGELOG
index b827b7c69eb..bbf44ca14c9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,7 @@ v 8.0.0 (unreleased)
- Improve search page usability
- Bring more UI consistency in way how projects, snippets and groups lists are rendered
- Make all profiles public
+ - Fixed login failure when extern_uid changes (Joel Koglin)
v 7.14.1
- Improve abuse reports management from admin area
diff --git a/app/models/user.rb b/app/models/user.rb
index b4e779370a0..48e0e6ed48b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -104,7 +104,7 @@ class User < ActiveRecord::Base
# Profile
has_many :keys, dependent: :destroy
has_many :emails, dependent: :destroy
- has_many :identities, dependent: :destroy
+ has_many :identities, dependent: :destroy, autosave: true
# Groups
has_many :members, dependent: :destroy
diff --git a/db/migrate/20150817163600_deduplicate_user_identities.rb b/db/migrate/20150817163600_deduplicate_user_identities.rb
new file mode 100644
index 00000000000..fab669c2905
--- /dev/null
+++ b/db/migrate/20150817163600_deduplicate_user_identities.rb
@@ -0,0 +1,14 @@
+class DeduplicateUserIdentities < ActiveRecord::Migration
+ def change
+ execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;'
+ execute 'CREATE TEMPORARY TABLE tt_migration_DeduplicateUserIdentities AS SELECT id,provider,user_id FROM identities;'
+ execute 'DELETE FROM identities WHERE id NOT IN ( SELECT MIN(id) FROM tt_migration_DeduplicateUserIdentities GROUP BY user_id, provider);'
+ execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;'
+ end
+
+ def down
+ # This is an irreversible migration;
+ # If someone is trying to rollback for other reasons, we should not throw an Exception.
+ # raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index f7f3ba9ad7d..04a22237478 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -44,9 +44,14 @@ module Gitlab
gl_user.skip_reconfirmation!
gl_user.email = auth_hash.email
- # Build new identity only if we dont have have same one
- gl_user.identities.find_or_initialize_by(provider: auth_hash.provider,
- extern_uid: auth_hash.uid)
+ # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
+ identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider }
+ identity ||= gl_user.identities.build(provider: auth_hash.provider)
+
+ # For a new user set extern_uid to the LDAP DN
+ # For an existing user with matching email but changed DN, update the DN.
+ # For an existing user with no change in DN, this line changes nothing.
+ identity.extern_uid = auth_hash.uid
gl_user
end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 7cfca96f4e0..84d9fb54b61 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -47,6 +47,28 @@ describe Gitlab::LDAP::User do
expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
end
+ it 'connects to existing ldap user if the extern_uid changes' do
+ existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain')
+ expect{ ldap_user.save }.not_to change{ User.count }
+
+ existing_user.reload
+ expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
+ expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
+ expect(existing_user.id).to eql ldap_user.gl_user.id
+ end
+
+ it 'maintains an identity per provider' do
+ existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter')
+ expect(existing_user.identities.count).to eql(1)
+
+ ldap_user.save
+ expect(ldap_user.gl_user.identities.count).to eql(2)
+
+ # Expect that find_by provider only returns a single instance of an identity and not an Enumerable
+ expect(ldap_user.gl_user.identities.find_by(provider: 'twitter')).to be_instance_of Identity
+ expect(ldap_user.gl_user.identities.find_by(provider: auth_hash.provider)).to be_instance_of Identity
+ end
+
it "creates a new user if not found" do
expect{ ldap_user.save }.to change{ User.count }.by(1)
end