A Kerberos user may have multiple identities from the same provider
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
Original Issue
Zendesk: https://gitlab.zendesk.com/agent/tickets/86075Generally we assume that a user has a single identity within a given authentication provider. GitLab will try to update the extern_uid from the external provider - particularly to facilitate automatic updating of an LDAP user's DN. In this customer's case with Kerberos, users may have multiple valid Kerberos identities. GitLab handles this correctly on the first sign in, by creating each unique Kerberos identity. However, on subsequent sign ins GitLab may find only the first identity with the mismatched extern_uid and try to update it. Since another identity with that extern_uid already exists, a validation error occurs and sign in fails.
Debugging with the customer, we tracked the error down to https://gitlab.com/gitlab-org/gitlab-ee/blob/bf6149a5d770b8290ad9885734830017cfba3a24/lib/gitlab/o_auth/user.rb#L72. We look up the identity only based on provider.
Possible solutions
-
Make 2 separate database lookups with a fall-through to retain the behavior where LDAP identities get updated. Downside here is that there may need to be 2 database lookups.
identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider && identity.extern_uid == auth_hash.uid } identity ||= gl_user.identities.find { |identity| identity.provider == auth_hash.provider } -
Grab all identities with that provider and do the computations in Ruby.
identities = gl_user.identities.where(provider: auth_hash.provider) identity = if identities.size > 1 identities.select { |identity| identity.extern_uid == auth_hash.uid }.first else identities.first end
@jacobvosmaer-gitlab @mkozono Can you think of any ways this would break LDAP or other auth? I'm leaning toward the second solution as is should be more performant (less DB calls).
Summary
Zendesk: Ticket #86075
Description
Generally we assume that a user has a single identity within a given authentication provider. GitLab will try to update the extern_uid from the external provider - particularly to facilitate automatic updating of an LDAP user's DN.
In this customer's case with Kerberos, users may have multiple valid Kerberos identities. GitLab handles this correctly on the first sign in, by creating each unique Kerberos identity.
However, on subsequent sign ins GitLab may find only the first identity with the mismatched extern_uid and try to update it. Since another identity with that extern_uid already exists, a validation error occurs and sign in fails.
Debugging with the customer, we tracked the error down to here. We look up the identity only based on provider.
Debugging with the customer, we traced the error to this line, where we look up the identity based solely on the provider.
Current Behaviour
On subsequent sign-ins, users encounter a validation error due to mismatched extern_uid when multiple valid identities exist for the same provider.
Expected Behaviour
GitLab should correctly handle multiple valid identities for the same provider without encountering validation errors during sign-in.
Possible solutions
-
Make 2 separate database lookups with a fall-through to retain the behavior where LDAP identities get updated. Downside here is that there may need to be 2 database lookups.
identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider && identity.extern_uid == auth_hash.uid } identity ||= gl_user.identities.find { |identity| identity.provider == auth_hash.provider } -
Grab all identities with that provider and do the computations in Ruby.
identities = gl_user.identities.where(provider: auth_hash.provider) identity = if identities.size > 1 identities.select { |identity| identity.extern_uid == auth_hash.uid }.first else identities.first end
@jacobvosmaer-gitlab @mkozono Can you think of any ways this would break LDAP or other auth? I'm leaning toward the second solution as is should be more performant (less DB calls).