mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-21 02:26:12 +10:00
Adjust the SAML control flow to allow LDAP identities to be added to an existing SAML user. It correctly lets an existing SAML user to add their LDAP identity automatically at login. A customer had issues with the `auto_link_ldap_user` feature. The flow was not working if there was an account with a SAML identity, but no LDAP identity. GitLab would pick up the correct LDAP person, but due to the order of the flow, that LDAP person was never associated with the user. Fixes #17346 /cc @dblessing @balameb @stanhu See merge request !4498
190 lines
5.3 KiB
Ruby
190 lines
5.3 KiB
Ruby
# OAuth extension for User model
|
|
#
|
|
# * Find GitLab user based on omniauth uid and provider
|
|
# * Create new user from omniauth data
|
|
#
|
|
module Gitlab
|
|
module OAuth
|
|
class SignupDisabledError < StandardError; end
|
|
|
|
class User
|
|
attr_accessor :auth_hash, :gl_user
|
|
|
|
def initialize(auth_hash)
|
|
self.auth_hash = auth_hash
|
|
end
|
|
|
|
def persisted?
|
|
gl_user.try(:persisted?)
|
|
end
|
|
|
|
def new?
|
|
!persisted?
|
|
end
|
|
|
|
def valid?
|
|
gl_user.try(:valid?)
|
|
end
|
|
|
|
def save(provider = 'OAuth')
|
|
unauthorized_to_create unless gl_user
|
|
|
|
if needs_blocking?
|
|
gl_user.save!
|
|
gl_user.block
|
|
else
|
|
gl_user.save!
|
|
end
|
|
|
|
log.info "(#{provider}) saving user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}"
|
|
gl_user
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
log.info "(#{provider}) Error saving user: #{gl_user.errors.full_messages}"
|
|
return self, e.record.errors
|
|
end
|
|
|
|
def gl_user
|
|
@user ||= find_by_uid_and_provider
|
|
|
|
if auto_link_ldap_user?
|
|
@user ||= find_or_create_ldap_user
|
|
end
|
|
|
|
if signup_enabled?
|
|
@user ||= build_new_user
|
|
end
|
|
|
|
if external_provider? && @user
|
|
@user.external = true
|
|
elsif @user
|
|
@user.external = false
|
|
end
|
|
|
|
@user
|
|
end
|
|
|
|
protected
|
|
|
|
def find_or_create_ldap_user
|
|
return unless ldap_person
|
|
|
|
# If a corresponding person exists with same uid in a LDAP server,
|
|
# check if the user already has a GitLab account.
|
|
user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider)
|
|
if user
|
|
# Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account.
|
|
log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity."
|
|
user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
|
|
else
|
|
log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account."
|
|
user = find_by_uid_and_provider
|
|
if user.nil?
|
|
log.info "No user found using #{auth_hash.provider} provider. Creating a new one."
|
|
user = build_new_user
|
|
end
|
|
log.info "Correct account has been found. Adding LDAP identity to user: #{user.username}."
|
|
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)
|
|
|
|
# Look 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
|
|
end
|
|
@ldap_person
|
|
end
|
|
|
|
def ldap_config
|
|
Gitlab::LDAP::Config.new(ldap_person.provider) if ldap_person
|
|
end
|
|
|
|
def needs_blocking?
|
|
new? && block_after_signup?
|
|
end
|
|
|
|
def signup_enabled?
|
|
providers = Gitlab.config.omniauth.allow_single_sign_on
|
|
if providers.is_a?(Array)
|
|
providers.include?(auth_hash.provider)
|
|
else
|
|
providers
|
|
end
|
|
end
|
|
|
|
def external_provider?
|
|
Gitlab.config.omniauth.external_providers.include?(auth_hash.provider)
|
|
end
|
|
|
|
def block_after_signup?
|
|
if creating_linked_ldap_user?
|
|
ldap_config.block_auto_created_users
|
|
else
|
|
Gitlab.config.omniauth.block_auto_created_users
|
|
end
|
|
end
|
|
|
|
def auth_hash=(auth_hash)
|
|
@auth_hash = AuthHash.new(auth_hash)
|
|
end
|
|
|
|
def find_by_uid_and_provider
|
|
identity = Identity.find_by(provider: auth_hash.provider, extern_uid: auth_hash.uid)
|
|
identity && identity.user
|
|
end
|
|
|
|
def build_new_user
|
|
user = ::User.new(user_attributes)
|
|
user.skip_confirmation!
|
|
user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider)
|
|
user
|
|
end
|
|
|
|
def user_attributes
|
|
# Give preference to LDAP for sensitive information when creating a linked account
|
|
if creating_linked_ldap_user?
|
|
username = ldap_person.username.presence
|
|
email = ldap_person.email.first.presence
|
|
end
|
|
|
|
username ||= auth_hash.username
|
|
email ||= auth_hash.email
|
|
|
|
name = auth_hash.name
|
|
name = ::Namespace.clean_path(username) if name.strip.empty?
|
|
|
|
{
|
|
name: name,
|
|
username: ::Namespace.clean_path(username),
|
|
email: email,
|
|
password: auth_hash.password,
|
|
password_confirmation: auth_hash.password,
|
|
password_automatically_set: true
|
|
}
|
|
end
|
|
|
|
def log
|
|
Gitlab::AppLogger
|
|
end
|
|
|
|
def unauthorized_to_create
|
|
raise SignupDisabledError
|
|
end
|
|
end
|
|
end
|
|
end
|