2022-01-17 15:24:07 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# Handle authentication actions via OmniAuth (used by SSO providers)
|
2016-03-23 18:39:41 +01:00
|
|
|
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
2022-01-17 15:24:07 +01:00
|
|
|
require 'sso_logger'
|
|
|
|
logger = SsoLogger.new
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2023-03-29 18:01:16 +02:00
|
|
|
active_provider = Rails.configuration.auth_provider
|
2016-03-23 18:39:41 +01:00
|
|
|
define_method active_provider.strategy_name do
|
2022-01-17 15:24:07 +01:00
|
|
|
logger.info "[Users::OmniauthCallbacksController##{active_provider.strategy_name}] initiated"
|
2016-03-23 18:39:41 +01:00
|
|
|
if request.env['omniauth.params'].blank?
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'the user has not provided any authentication token'
|
2016-03-23 18:39:41 +01:00
|
|
|
@user = User.from_omniauth(request.env['omniauth.auth'])
|
|
|
|
|
|
|
|
# Here we create the new user or update the existing one with values retrieved from the SSO.
|
|
|
|
|
|
|
|
if @user.id.nil? # => new user (ie. not updating existing)
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'trying to create a new user'
|
2016-03-23 18:39:41 +01:00
|
|
|
# If the username is mapped, we just check its uniqueness as it would break the postgresql
|
2018-12-12 17:24:31 +01:00
|
|
|
# unique constraint otherwise. If the name is not unique, another unique is generated
|
2023-03-29 18:01:16 +02:00
|
|
|
if active_provider.db.sso_fields.include?('user.username')
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'the username was already in use, generating a new one'
|
2016-03-23 18:39:41 +01:00
|
|
|
@user.username = generate_unique_username(@user.username)
|
|
|
|
end
|
|
|
|
# If the email is mapped, we check its uniqueness. If the email is already in use, we mark it as duplicate with an
|
2016-09-12 17:29:44 +02:00
|
|
|
# unique random string, because:
|
2016-03-23 18:39:41 +01:00
|
|
|
# - if it is the same user, his email will be filled from the SSO when he merge his accounts
|
|
|
|
# - if it is not the same user, this will prevent the raise of PG::UniqueViolation
|
2023-03-29 18:01:16 +02:00
|
|
|
if active_provider.db.sso_fields.include?('user.email') && email_exists?(@user.email)
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'the email was already in use, marking it as duplicate'
|
2016-03-23 18:39:41 +01:00
|
|
|
old_mail = @user.email
|
|
|
|
@user.email = "<#{old_mail}>#{Devise.friendly_token}-duplicate"
|
|
|
|
flash[:alert] = t('omniauth.email_already_linked_to_another_account_please_input_your_authentication_code', OLD_MAIL: old_mail)
|
|
|
|
end
|
2016-09-12 17:29:44 +02:00
|
|
|
else # => update of an existing user
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug "an existing user was found (id=#{@user.id})"
|
2016-03-23 18:39:41 +01:00
|
|
|
if username_exists?(@user.username, @user.id)
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'the username was already in use, alerting user'
|
2016-03-23 18:39:41 +01:00
|
|
|
flash[:alert] = t('omniauth.your_username_is_already_linked_to_another_account_unable_to_update_it', USERNAME: @user.username)
|
|
|
|
@user.username = User.find(@user.id).username
|
|
|
|
end
|
|
|
|
|
|
|
|
if email_exists?(@user.email, @user.id)
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'the email was already in use, alerting user'
|
2016-03-23 18:39:41 +01:00
|
|
|
flash[:alert] = t('omniauth.your_email_address_is_already_linked_to_another_account_unable_to_update_it', EMAIL: @user.email)
|
|
|
|
@user.email = User.find(@user.id).email
|
|
|
|
end
|
|
|
|
end
|
2023-03-29 18:01:16 +02:00
|
|
|
# For users imported from the SSO, we consider the SSO as a source of trust so the email is automatically validated
|
|
|
|
@user.confirmed_at = Time.current if active_provider.db.sso_fields.include?('user.email') && !email_exists?(@user.email)
|
2016-03-23 18:39:41 +01:00
|
|
|
|
2023-04-03 11:10:57 +02:00
|
|
|
# We BYPASS THE VALIDATION because, in case of a new user, we want to save him anyway,
|
|
|
|
# we'll ask him later to complete his profile (on first login).
|
2016-03-23 18:39:41 +01:00
|
|
|
# In case of an existing user, we trust the SSO validation as we want the SSO to have authority on users management and policy.
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'saving the user'
|
2023-03-29 18:01:16 +02:00
|
|
|
logger.error "unable to save the user, an error occurred : #{@user.errors.full_messages.join(', ')}" unless @user.save(validate: false)
|
2021-11-09 09:43:30 +01:00
|
|
|
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'signing-in the user and redirecting'
|
2018-12-12 17:24:31 +01:00
|
|
|
sign_in_and_redirect @user, event: :authentication # this will throw if @user is not activated
|
2016-03-23 18:39:41 +01:00
|
|
|
else
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'the user has provided an authentication token'
|
2016-03-23 18:39:41 +01:00
|
|
|
@user = User.find_by(auth_token: request.env['omniauth.params']['auth_token'])
|
|
|
|
|
|
|
|
# Here the user already exists in the database and request to be linked with the SSO
|
|
|
|
# so let's update its sso attributes and log him on
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug "found user id=#{@user.id}"
|
2016-03-23 18:39:41 +01:00
|
|
|
|
|
|
|
begin
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'linking with the omniauth provider'
|
2016-03-23 18:39:41 +01:00
|
|
|
@user.link_with_omniauth_provider(request.env['omniauth.auth'])
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.debug 'signing-in the user and redirecting'
|
2018-12-12 17:24:31 +01:00
|
|
|
sign_in_and_redirect @user, event: :authentication
|
2016-03-23 18:39:41 +01:00
|
|
|
rescue DuplicateIndexError
|
2021-11-10 13:03:12 +01:00
|
|
|
logger.error 'user already linked'
|
2016-03-23 18:39:41 +01:00
|
|
|
redirect_to root_url, alert: t('omniauth.this_account_is_already_linked_to_an_user_of_the_platform', NAME: active_provider.name)
|
2021-11-10 13:03:12 +01:00
|
|
|
rescue StandardError => e
|
2022-04-20 14:12:22 +02:00
|
|
|
logger.error "an expected error occurred: #{e}"
|
2021-11-10 13:03:12 +01:00
|
|
|
raise e
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2018-12-12 17:24:31 +01:00
|
|
|
|
2016-03-23 18:39:41 +01:00
|
|
|
def username_exists?(username, exclude_id = nil)
|
|
|
|
if exclude_id.nil?
|
2022-01-17 12:42:27 +01:00
|
|
|
User.where('lower(username) = ?', username&.downcase).size.positive?
|
2016-03-23 18:39:41 +01:00
|
|
|
else
|
2022-01-17 12:42:27 +01:00
|
|
|
User.where('lower(username) = ?', username&.downcase).where.not(id: exclude_id).size.positive?
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def email_exists?(email, exclude_id = nil)
|
|
|
|
if exclude_id.nil?
|
2022-01-17 12:42:27 +01:00
|
|
|
User.where('lower(email) = ?', email&.downcase).size.positive?
|
2016-03-23 18:39:41 +01:00
|
|
|
else
|
2022-01-17 12:42:27 +01:00
|
|
|
User.where('lower(email) = ?', email&.downcase).where.not(id: exclude_id).size.positive?
|
2016-03-23 18:39:41 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def generate_unique_username(username)
|
|
|
|
generated = username
|
|
|
|
i = 1000
|
|
|
|
while username_exists?(generated)
|
|
|
|
generated = username + rand(1..i).to_s
|
|
|
|
i += 10
|
|
|
|
end
|
|
|
|
generated
|
|
|
|
end
|
2018-12-12 17:24:31 +01:00
|
|
|
end
|