scinote-web/app/controllers/users/omniauth_callbacks_controller.rb

346 lines
14 KiB
Ruby

# frozen_string_literal: true
module Users
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include UsersGenerator
include ApplicationHelper
skip_before_action :verify_authenticity_token
before_action :sign_up_with_provider_enabled?,
only: :linkedin
before_action :check_sso_status, only: %i(customazureactivedirectory okta openid_connect saml)
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
# You should also create an action method in this controller like this:
# def twitter
# end
def customazureactivedirectory
auth = request.env['omniauth.auth']
provider_id = auth.dig(:extra, :raw_info, :aud)
settings = ApplicationSettings.instance
provider_conf = settings.values['azure_ad_apps'].find { |v| v['enable_sign_in'] && v['app_id'] == provider_id }
raise StandardError, 'No matching Azure AD provider config found' if provider_conf.blank?
auth.provider = provider_conf['provider_name']
return redirect_to connected_accounts_path if current_user
email = auth.info.email
email ||= auth.dig(:extra, :raw_info, :id_token_claims, :emails)&.first
auth.uid ||= auth.dig(:extra, :raw_info, :sub)
user = User.from_omniauth(auth)
# User found in database so just signing in
return sign_in_and_redirect(user, event: :authentication) if user.present?
if email.blank?
# No email in the token so can not link or create user
missing_attribute = 'Email'
return redirect_to after_omniauth_failure_path_for(resource_name)
end
user = User.find_by(email: email.downcase)
if user.blank?
# Create new user and identity
user = create_user_from_auth(email, auth)
if user.errors.present?
redirect_to after_omniauth_failure_path_for(resource_name)
else
sign_in_and_redirect(user, event: :authentication)
end
elsif provider_conf['auto_link_on_sign_in']
# Link to existing local account
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at) if user.confirmed_at.blank?
sign_in_and_redirect(user, event: :authentication)
else
# Cannot do anything with it, so just return an error
error_message = I18n.t('devise.azure.errors.no_local_user_map')
redirect_to after_omniauth_failure_path_for(resource_name)
end
rescue StandardError => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
error_message = I18n.t('devise.azure.errors.failed_to_save') if e.is_a?(ActiveRecord::RecordInvalid)
error_message ||= I18n.t('devise.azure.errors.generic')
redirect_to after_omniauth_failure_path_for(resource_name)
ensure
if user&.errors.present? || missing_attribute.present?
missing_attribute ||= user.errors.first.attribute.capitalize
set_flash_message(:alert, :missing_attribute, attribute: missing_attribute)
elsif error_message
set_flash_message(:alert, :failure, kind: I18n.t('devise.azure.provider_name'), reason: error_message)
else
set_flash_message(:notice, :success, kind: I18n.t('devise.azure.provider_name'))
end
end
def linkedin
auth_hash = request.env['omniauth.auth']
@user = User.from_omniauth(auth_hash)
if @user && @user.current_team_id?
# User already exists and has been signed up with LinkedIn; just sign in
set_flash_message(:notice,
:success,
kind: I18n.t('devise.linkedin.provider_name'))
sign_in_and_redirect(@user, event: :authentication)
elsif @user
# User already exists and has started sign up with LinkedIn;
# but doesn't have team (needs to complete sign up - agrees to TOS)
set_flash_message(:alert,
:failure,
kind: I18n.t('devise.linkedin.provider_name'),
reason: I18n.t('devise.linkedin.complete_sign_up'))
sign_in(@user, event: :authentication)
redirect_to users_sign_up_provider_path
elsif User.find_by_email(auth_hash['info']['email'])
# email is already taken, so sign up with Linked in is not allowed
set_flash_message(:alert,
:failure,
kind: I18n.t('devise.linkedin.provider_name'),
reason: I18n.t('devise.linkedin.email_already_taken',
email: auth_hash['info']['email']))
redirect_to after_omniauth_failure_path_for(resource_name)
else
# Create new user and identity; and redirect to complete sign up form
full_name = "#{auth_hash['info']['first_name']} #{auth_hash['info']['last_name']}"
@user = User.new(
full_name: full_name,
initials: generate_initials(full_name),
email: auth_hash['info']['email'],
password: generate_user_password
)
if auth_hash['info']['picture_url']
avatar = URI.open(auth_hash['info']['picture_url'])
@user.avatar.attach(io: avatar, filename: 'linkedin_avatar.jpg')
end
user_identity = UserIdentity.new(user: @user,
provider: auth_hash['provider'],
uid: auth_hash['uid'])
unless @user.save && user_identity.save
set_flash_message(:alert,
:failure,
kind: I18n.t('devise.linkedin.provider_name'),
reason: I18n.t('devise.linkedin.failed_to_save'))
redirect_to after_omniauth_failure_path_for(resource_name) and return
end
# Confirm user
@user.update!(confirmed_at: @user.created_at)
sign_in(@user, event: :authentication)
redirect_to users_sign_up_provider_path
end
end
def okta
auth = request.env['omniauth.auth']
user = User.from_omniauth(auth)
# User found in database so just signing in
return sign_in_and_redirect(user, event: :authentication) if user.present?
user = User.find_by(email: auth.info.email.downcase)
if user.blank?
user = create_user_from_auth(auth.info.email.downcase, auth)
if user.errors.present?
redirect_to after_omniauth_failure_path_for(resource_name)
else
sign_in_and_redirect(user, event: :authentication)
end
else
# Link to existing local account
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at) if user.confirmed_at.blank?
sign_in_and_redirect(user, event: :authentication)
end
rescue StandardError => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
error_message = I18n.t('devise.okta.errors.failed_to_save') if e.is_a?(ActiveRecord::RecordInvalid)
error_message ||= I18n.t('devise.okta.errors.generic')
redirect_to after_omniauth_failure_path_for(resource_name)
ensure
if user&.errors.present? || missing_attribute.present?
missing_attribute ||= user.errors.first.attribute.capitalize
set_flash_message(:alert, :missing_attribute, attribute: missing_attribute)
elsif error_message
set_flash_message(:alert, :failure, kind: I18n.t('devise.okta.provider_name'), reason: error_message)
else
set_flash_message(:notice, :success, kind: I18n.t('devise.okta.provider_name'))
end
end
def openid_connect
auth = request.env['omniauth.auth']
settings = ApplicationSettings.instance
provider_conf = settings.values['openid_connect']
raise StandardError, 'No matching OpenID Connect AD provider config found' if provider_conf.blank?
return redirect_to connected_accounts_path if current_user
email = auth.info.email
email ||= auth.dig(:extra, :raw_info, :id_token_claims, :emails)&.first
user = User.from_omniauth(auth)
# User found in database so just signing in
return sign_in_and_redirect(user) if user.present?
if email.blank?
# No email in the token so can not link or create user
missing_attribute = 'Email'
return redirect_to after_omniauth_failure_path_for(resource_name)
end
user = User.find_by(email: email.downcase)
if user.blank?
# Create new user and identity
user = create_user_from_auth(email, auth)
if user.errors.present?
redirect_to after_omniauth_failure_path_for(resource_name)
else
sign_in_and_redirect(user)
end
elsif provider_conf['auto_link_on_sign_in']
# Link to existing local account
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at) if user.confirmed_at.blank?
sign_in_and_redirect(user)
else
# Cannot do anything with it, so just return an error
error_message = I18n.t('devise.openid_connect.errors.no_local_user_map')
redirect_to after_omniauth_failure_path_for(resource_name)
end
rescue StandardError => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
error_message = I18n.t('devise.openid_connect.errors.failed_to_save') if e.is_a?(ActiveRecord::RecordInvalid)
error_message ||= I18n.t('devise.openid_connect.errors.generic')
redirect_to after_omniauth_failure_path_for(resource_name)
ensure
if user&.errors.present? || missing_attribute.present?
missing_attribute ||= user.errors.first.attribute.capitalize
set_flash_message(:alert, :missing_attribute, attribute: missing_attribute)
elsif error_message
set_flash_message(:alert, :failure, kind: I18n.t('devise.openid_connect.provider_name'), reason: error_message)
else
set_flash_message(:notice, :success, kind: I18n.t('devise.openid_connect.provider_name'))
end
end
def saml
auth = request.env['omniauth.auth']
settings = ApplicationSettings.instance
provider_conf = settings.values['saml']
raise StandardError, 'No matching SAML provider config found' if provider_conf.blank?
return redirect_to connected_accounts_path if current_user
email = auth.info.email
user = User.from_omniauth(auth)
# User found in database so just signing in
return sign_in_and_redirect(user) if user.present?
if email.blank?
# No email in the token so can not link or create user
missing_attribute = 'Email'
return redirect_to after_omniauth_failure_path_for(resource_name)
end
user = User.find_by(email: email.downcase)
if user.blank?
user = create_user_from_auth(email, auth)
if user.errors.present?
redirect_to after_omniauth_failure_path_for(resource_name)
else
sign_in_and_redirect(user)
end
elsif provider_conf['auto_link_on_sign_in']
# Link to existing local account
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at) if user.confirmed_at.blank?
sign_in_and_redirect(user)
else
# Cannot do anything with it, so just return an error
error_message = I18n.t('devise.saml.errors.no_local_user_map')
redirect_to after_omniauth_failure_path_for(resource_name)
end
rescue StandardError => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
error_message = I18n.t('devise.saml.errors.failed_to_save') if e.is_a?(ActiveRecord::RecordInvalid)
error_message ||= I18n.t('devise.saml.errors.generic')
redirect_to after_omniauth_failure_path_for(resource_name)
ensure
if user&.errors.present? || missing_attribute.present?
missing_attribute ||= user.errors.first.attribute.to_s.capitalize
set_flash_message(:alert, :missing_attribute, attribute: missing_attribute)
elsif error_message
set_flash_message(:alert, :failure, kind: I18n.t('devise.saml.provider_name'), reason: error_message)
else
set_flash_message(:notice, :success, kind: I18n.t('devise.saml.provider_name'))
end
end
# More info at:
# https://github.com/plataformatec/devise#omniauth
# GET|POST /resource/auth/twitter
# def passthru
# super
# end
# GET|POST /users/auth/twitter/callback
# def failure
# super
# end
# protected
# The path used when OmniAuth fails
# def after_omniauth_failure_path_for(scope)
# super(scope)
# end
private
def sign_up_with_provider_enabled?
render_403 unless Rails.configuration.x.enable_user_registration
render_403 unless Rails.configuration.x.linkedin_signin_enabled
end
def check_sso_status
render_403 unless sso_enabled?
end
def generate_initials(full_name)
initials = full_name.titleize.scan(/[A-Z]+/).join
initials = initials.strip.blank? ? 'PLCH' : initials[0..3]
initials
end
def create_user_from_auth(email, auth)
full_name = "#{auth.info.first_name} #{auth.info.last_name}"
user = User.new(full_name: full_name,
initials: generate_initials(full_name),
email: email,
password: generate_user_password)
User.transaction do
user.save!
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at)
rescue StandardError => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
raise ActiveRecord::Rollback
end
user
end
end
end