mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-12-01 12:24:28 +01:00
Merge branch 'oauth2' into dev
This commit is contained in:
commit
c031f099a5
@ -1,5 +1,11 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
|
|
||||||
|
- Support for JSONPath syntax in OAuth2 SSO fields mapping
|
||||||
|
- Support for OAuth2 scopes
|
||||||
|
- Add debug logs for the SSO authentication process
|
||||||
|
- Fix a bug: SSO configuration interface has a misnamed field (Common URL)
|
||||||
|
- Fix a bug: unable to bind Profile.birthday and Profile.gender from an SSO
|
||||||
|
|
||||||
- Ability to cancel a payement schedule from the interface
|
- Ability to cancel a payement schedule from the interface
|
||||||
- Ability to create slots in the past
|
- Ability to create slots in the past
|
||||||
- Ability to disable public account creation
|
- Ability to disable public account creation
|
||||||
|
6
Gemfile
6
Gemfile
@ -14,6 +14,7 @@ gem 'webpacker', '~> 5.x'
|
|||||||
gem 'jbuilder', '~> 2.5'
|
gem 'jbuilder', '~> 2.5'
|
||||||
gem 'jbuilder_cache_multi'
|
gem 'jbuilder_cache_multi'
|
||||||
gem 'json', '>= 2.3.0'
|
gem 'json', '>= 2.3.0'
|
||||||
|
gem 'jsonpath'
|
||||||
|
|
||||||
gem 'forgery'
|
gem 'forgery'
|
||||||
gem 'responders', '~> 2.0'
|
gem 'responders', '~> 2.0'
|
||||||
@ -65,7 +66,10 @@ gem 'pg_search'
|
|||||||
# authentication
|
# authentication
|
||||||
gem 'devise', '>= 4.6.0'
|
gem 'devise', '>= 4.6.0'
|
||||||
|
|
||||||
gem 'omniauth', '~> 1.9.0'
|
|
||||||
|
git 'https://github.com/sleede/omniauth.git', branch: 'v1.9' do
|
||||||
|
gem 'omniauth', '~> 1.9.0'
|
||||||
|
end
|
||||||
gem 'omniauth-oauth2'
|
gem 'omniauth-oauth2'
|
||||||
gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
||||||
|
|
||||||
|
17
Gemfile.lock
17
Gemfile.lock
@ -1,3 +1,12 @@
|
|||||||
|
GIT
|
||||||
|
remote: https://github.com/sleede/omniauth.git
|
||||||
|
revision: 832d5ac48fcc3b93eba8bf1444145d0c876314cf
|
||||||
|
branch: v1.9
|
||||||
|
specs:
|
||||||
|
omniauth (1.9.1)
|
||||||
|
hashie (>= 3.4.6)
|
||||||
|
rack (>= 1.6.2, < 3)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
@ -175,6 +184,8 @@ GEM
|
|||||||
jbuilder_cache_multi (0.1.0)
|
jbuilder_cache_multi (0.1.0)
|
||||||
jbuilder (>= 1.5.0, < 3)
|
jbuilder (>= 1.5.0, < 3)
|
||||||
json (2.3.1)
|
json (2.3.1)
|
||||||
|
jsonpath (1.1.0)
|
||||||
|
multi_json
|
||||||
jwt (2.2.1)
|
jwt (2.2.1)
|
||||||
kaminari (1.2.1)
|
kaminari (1.2.1)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
@ -234,9 +245,6 @@ GEM
|
|||||||
multi_xml (~> 0.5)
|
multi_xml (~> 0.5)
|
||||||
rack (>= 1.2, < 3)
|
rack (>= 1.2, < 3)
|
||||||
oj (3.10.5)
|
oj (3.10.5)
|
||||||
omniauth (1.9.1)
|
|
||||||
hashie (>= 3.4.6)
|
|
||||||
rack (>= 1.6.2, < 3)
|
|
||||||
omniauth-oauth2 (1.6.0)
|
omniauth-oauth2 (1.6.0)
|
||||||
oauth2 (~> 1.1)
|
oauth2 (~> 1.1)
|
||||||
omniauth (~> 1.9)
|
omniauth (~> 1.9)
|
||||||
@ -456,6 +464,7 @@ DEPENDENCIES
|
|||||||
jbuilder (~> 2.5)
|
jbuilder (~> 2.5)
|
||||||
jbuilder_cache_multi
|
jbuilder_cache_multi
|
||||||
json (>= 2.3.0)
|
json (>= 2.3.0)
|
||||||
|
jsonpath
|
||||||
kaminari
|
kaminari
|
||||||
listen (~> 3.0.5)
|
listen (~> 3.0.5)
|
||||||
message_format
|
message_format
|
||||||
@ -463,7 +472,7 @@ DEPENDENCIES
|
|||||||
minitest-reporters
|
minitest-reporters
|
||||||
notify_with
|
notify_with
|
||||||
oj
|
oj
|
||||||
omniauth (~> 1.9.0)
|
omniauth (~> 1.9.0)!
|
||||||
omniauth-oauth2
|
omniauth-oauth2
|
||||||
omniauth-rails_csrf_protection (~> 0.1)
|
omniauth-rails_csrf_protection (~> 0.1)
|
||||||
openlab_ruby
|
openlab_ruby
|
||||||
|
@ -2,15 +2,19 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||||||
|
|
||||||
active_provider = AuthProvider.active
|
active_provider = AuthProvider.active
|
||||||
define_method active_provider.strategy_name do
|
define_method active_provider.strategy_name do
|
||||||
|
logger.debug "[Users::OmniauthCallbacksController##{active_provider.strategy_name}] initiated"
|
||||||
if request.env['omniauth.params'].blank?
|
if request.env['omniauth.params'].blank?
|
||||||
|
logger.debug 'the user has not provided any authentication token'
|
||||||
@user = User.from_omniauth(request.env['omniauth.auth'])
|
@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.
|
# 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)
|
if @user.id.nil? # => new user (ie. not updating existing)
|
||||||
|
logger.debug 'trying to create a new user'
|
||||||
# If the username is mapped, we just check its uniqueness as it would break the postgresql
|
# If the username is mapped, we just check its uniqueness as it would break the postgresql
|
||||||
# unique constraint otherwise. If the name is not unique, another unique is generated
|
# unique constraint otherwise. If the name is not unique, another unique is generated
|
||||||
if active_provider.sso_fields.include?('user.username')
|
if active_provider.sso_fields.include?('user.username')
|
||||||
|
logger.debug 'the username was already in use, generating a new one'
|
||||||
@user.username = generate_unique_username(@user.username)
|
@user.username = generate_unique_username(@user.username)
|
||||||
end
|
end
|
||||||
# If the email is mapped, we check its uniqueness. If the email is already in use, we mark it as duplicate with an
|
# If the email is mapped, we check its uniqueness. If the email is already in use, we mark it as duplicate with an
|
||||||
@ -18,17 +22,21 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||||||
# - if it is the same user, his email will be filled from the SSO when he merge his accounts
|
# - 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
|
# - if it is not the same user, this will prevent the raise of PG::UniqueViolation
|
||||||
if active_provider.sso_fields.include?('user.email') && email_exists?(@user.email)
|
if active_provider.sso_fields.include?('user.email') && email_exists?(@user.email)
|
||||||
|
logger.debug 'the email was already in use, marking it as duplicate'
|
||||||
old_mail = @user.email
|
old_mail = @user.email
|
||||||
@user.email = "<#{old_mail}>#{Devise.friendly_token}-duplicate"
|
@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)
|
flash[:alert] = t('omniauth.email_already_linked_to_another_account_please_input_your_authentication_code', OLD_MAIL: old_mail)
|
||||||
end
|
end
|
||||||
else # => update of an existing user
|
else # => update of an existing user
|
||||||
|
logger.debug "an existing user was found (id=#{@user.id})"
|
||||||
if username_exists?(@user.username, @user.id)
|
if username_exists?(@user.username, @user.id)
|
||||||
|
logger.debug 'the username was already in use, alerting user'
|
||||||
flash[:alert] = t('omniauth.your_username_is_already_linked_to_another_account_unable_to_update_it', USERNAME: @user.username)
|
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
|
@user.username = User.find(@user.id).username
|
||||||
end
|
end
|
||||||
|
|
||||||
if email_exists?(@user.email, @user.id)
|
if email_exists?(@user.email, @user.id)
|
||||||
|
logger.debug 'the email was already in use, alerting user'
|
||||||
flash[:alert] = t('omniauth.your_email_address_is_already_linked_to_another_account_unable_to_update_it', EMAIL: @user.email)
|
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
|
@user.email = User.find(@user.id).email
|
||||||
end
|
end
|
||||||
@ -36,19 +44,32 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||||||
|
|
||||||
# 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).
|
# 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).
|
||||||
# In case of an existing user, we trust the SSO validation as we want the SSO to have authority on users management and policy.
|
# In case of an existing user, we trust the SSO validation as we want the SSO to have authority on users management and policy.
|
||||||
@user.save(validate: false)
|
logger.debug 'saving the user'
|
||||||
|
unless @user.save(validate: false)
|
||||||
|
logger.error "unable to save the user, an error occurred : #{@user.errors.full_messages.join(', ')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
logger.debug 'signing-in the user and redirecting'
|
||||||
sign_in_and_redirect @user, event: :authentication # this will throw if @user is not activated
|
sign_in_and_redirect @user, event: :authentication # this will throw if @user is not activated
|
||||||
else
|
else
|
||||||
|
logger.debug 'the user has provided an authentication token'
|
||||||
@user = User.find_by(auth_token: request.env['omniauth.params']['auth_token'])
|
@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
|
# 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
|
# so let's update its sso attributes and log him on
|
||||||
|
logger.debug "found user id=#{@user.id}"
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
logger.debug 'linking with the omniauth provider'
|
||||||
@user.link_with_omniauth_provider(request.env['omniauth.auth'])
|
@user.link_with_omniauth_provider(request.env['omniauth.auth'])
|
||||||
|
logger.debug 'signing-in the user and redirecting'
|
||||||
sign_in_and_redirect @user, event: :authentication
|
sign_in_and_redirect @user, event: :authentication
|
||||||
rescue DuplicateIndexError
|
rescue DuplicateIndexError
|
||||||
|
logger.error 'user already linked'
|
||||||
redirect_to root_url, alert: t('omniauth.this_account_is_already_linked_to_an_user_of_the_platform', NAME: active_provider.name)
|
redirect_to root_url, alert: t('omniauth.this_account_is_already_linked_to_an_user_of_the_platform', NAME: active_provider.name)
|
||||||
|
rescue StandardError => e
|
||||||
|
logger.unknown "an expected error occurred: #{e}"
|
||||||
|
raise e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -58,17 +79,17 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||||||
|
|
||||||
def username_exists?(username, exclude_id = nil)
|
def username_exists?(username, exclude_id = nil)
|
||||||
if exclude_id.nil?
|
if exclude_id.nil?
|
||||||
User.where(username: username).size.positive?
|
User.where('lower(username) = ?', username&.downcase).size.positive?
|
||||||
else
|
else
|
||||||
User.where(username: username).where.not(id: exclude_id).size.positive?
|
User.where('lower(username) = ?', username&.downcase).where.not(id: exclude_id).size.positive?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def email_exists?(email, exclude_id = nil)
|
def email_exists?(email, exclude_id = nil)
|
||||||
if exclude_id.nil?
|
if exclude_id.nil?
|
||||||
User.where(email: email).size.positive?
|
User.where('lower(email) = ?', email&.downcase).size.positive?
|
||||||
else
|
else
|
||||||
User.where(email: email).where.not(id: exclude_id).size.positive?
|
User.where('lower(email) = ?', email&.downcase).where.not(id: exclude_id).size.positive?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -64,10 +64,11 @@
|
|||||||
</td>
|
</td>
|
||||||
<td ng-class="{'has-error': mappingForm['auth_mapping[api_field]'].$dirty && mappingForm['auth_mapping[api_field]'].$invalid}">
|
<td ng-class="{'has-error': mappingForm['auth_mapping[api_field]'].$dirty && mappingForm['auth_mapping[api_field]'].$invalid}">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="form-control"
|
class="form-control help-cursor"
|
||||||
placeholder="field_name"
|
placeholder="field_name"
|
||||||
ng-model="newMapping.api_field"
|
ng-model="newMapping.api_field"
|
||||||
name="auth_mapping[api_field]"
|
name="auth_mapping[api_field]"
|
||||||
|
title="{{ 'app.shared.oauth2.api_field_help' | translate }}"
|
||||||
required/>
|
required/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -8,13 +8,13 @@ class OAuth2Provider < ApplicationRecord
|
|||||||
accepts_nested_attributes_for :o_auth2_mappings, allow_destroy: true
|
accepts_nested_attributes_for :o_auth2_mappings, allow_destroy: true
|
||||||
|
|
||||||
def domain
|
def domain
|
||||||
URI(base_url).scheme+'://'+URI(base_url).host
|
URI(base_url).scheme + '://' + URI(base_url).host
|
||||||
end
|
end
|
||||||
|
|
||||||
def protected_fields
|
def protected_fields
|
||||||
fields = []
|
fields = []
|
||||||
o_auth2_mappings.each do |mapping|
|
o_auth2_mappings.each do |mapping|
|
||||||
fields.push(mapping.local_model+'.'+mapping.local_field)
|
fields.push(mapping.local_model + '.' + mapping.local_field)
|
||||||
end
|
end
|
||||||
fields
|
fields
|
||||||
end
|
end
|
||||||
|
@ -27,7 +27,8 @@ class Profile < ApplicationRecord
|
|||||||
# we protect some fields as they are designed to be managed by the system and must not be updated externally
|
# we protect some fields as they are designed to be managed by the system and must not be updated externally
|
||||||
blacklist = %w[id user_id created_at updated_at]
|
blacklist = %w[id user_id created_at updated_at]
|
||||||
# model-relationships must be added manually
|
# model-relationships must be added manually
|
||||||
additional = [%w[avatar string], %w[address string], %w[organization_name string], %w[organization_address string]]
|
additional = [%w[avatar string], %w[address string], %w[organization_name string], %w[organization_address string],
|
||||||
|
%w[gender boolean], %w[birthday date]]
|
||||||
Profile.columns_hash
|
Profile.columns_hash
|
||||||
.map { |k, v| [k, v.type.to_s] }
|
.map { |k, v| [k, v.type.to_s] }
|
||||||
.delete_if { |col| blacklist.include?(col[0]) }
|
.delete_if { |col| blacklist.include?(col[0]) }
|
||||||
|
@ -201,16 +201,20 @@ class User < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.from_omniauth(auth)
|
def self.from_omniauth(auth)
|
||||||
|
logger.debug "[User::from_omniauth] initiated with parameter #{auth}"
|
||||||
active_provider = AuthProvider.active
|
active_provider = AuthProvider.active
|
||||||
raise SecurityError, 'The identity provider does not match the activated one' if active_provider.strategy_name != auth.provider
|
raise SecurityError, 'The identity provider does not match the activated one' if active_provider.strategy_name != auth.provider
|
||||||
|
|
||||||
where(provider: auth.provider, uid: auth.uid).first_or_create.tap do |user|
|
where(provider: auth.provider, uid: auth.uid).first_or_create.tap do |user|
|
||||||
# execute this regardless of whether record exists or not (-> User#tap)
|
# execute this regardless of whether record exists or not (-> User#tap)
|
||||||
# this will init or update the user thanks to the information retrieved from the SSO
|
# this will init or update the user thanks to the information retrieved from the SSO
|
||||||
|
logger.debug user.id.nil? ? 'no user found, creating a new one' : "found user id=#{user.id}"
|
||||||
user.profile ||= Profile.new
|
user.profile ||= Profile.new
|
||||||
auth.info.mapping.each do |key, value|
|
auth.info.mapping.each do |key, value|
|
||||||
|
logger.debug "mapping info #{key} with value=#{value}"
|
||||||
user.set_data_from_sso_mapping(key, value)
|
user.set_data_from_sso_mapping(key, value)
|
||||||
end
|
end
|
||||||
|
logger.debug "generating a new password"
|
||||||
user.password = Devise.friendly_token[0, 20]
|
user.password = Devise.friendly_token[0, 20]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -238,6 +242,10 @@ class User < ApplicationRecord
|
|||||||
invoicing_profile.organization.name
|
invoicing_profile.organization.name
|
||||||
when 'profile.organization_address'
|
when 'profile.organization_address'
|
||||||
invoicing_profile.organization.address.address
|
invoicing_profile.organization.address.address
|
||||||
|
when 'profile.gender'
|
||||||
|
statistic_profile.gender
|
||||||
|
when 'profile.birthday'
|
||||||
|
statistic_profile.birthday
|
||||||
else
|
else
|
||||||
profile[parsed[2].to_sym]
|
profile[parsed[2].to_sym]
|
||||||
end
|
end
|
||||||
@ -256,15 +264,24 @@ class User < ApplicationRecord
|
|||||||
profile.user_avatar ||= UserAvatar.new
|
profile.user_avatar ||= UserAvatar.new
|
||||||
profile.user_avatar.remote_attachment_url = data
|
profile.user_avatar.remote_attachment_url = data
|
||||||
when 'profile.address'
|
when 'profile.address'
|
||||||
|
invoicing_profile ||= InvoicingProfile.new
|
||||||
invoicing_profile.address ||= Address.new
|
invoicing_profile.address ||= Address.new
|
||||||
invoicing_profile.address.address = data
|
invoicing_profile.address.address = data
|
||||||
when 'profile.organization_name'
|
when 'profile.organization_name'
|
||||||
|
invoicing_profile ||= InvoicingProfile.new
|
||||||
invoicing_profile.organization ||= Organization.new
|
invoicing_profile.organization ||= Organization.new
|
||||||
invoicing_profile.organization.name = data
|
invoicing_profile.organization.name = data
|
||||||
when 'profile.organization_address'
|
when 'profile.organization_address'
|
||||||
|
invoicing_profile ||= InvoicingProfile.new
|
||||||
invoicing_profile.organization ||= Organization.new
|
invoicing_profile.organization ||= Organization.new
|
||||||
invoicing_profile.organization.address ||= Address.new
|
invoicing_profile.organization.address ||= Address.new
|
||||||
invoicing_profile.organization.address.address = data
|
invoicing_profile.organization.address.address = data
|
||||||
|
when 'profile.gender'
|
||||||
|
statistic_profile ||= StatisticProfile.new
|
||||||
|
statistic_profile.gender = data
|
||||||
|
when 'profile.birthday'
|
||||||
|
statistic_profile ||= StatisticProfile.new
|
||||||
|
statistic_profile.birthday = data
|
||||||
else
|
else
|
||||||
profile[sso_mapping[8..-1].to_sym] = data unless data.nil?
|
profile[sso_mapping[8..-1].to_sym] = data unless data.nil?
|
||||||
end
|
end
|
||||||
@ -292,6 +309,7 @@ class User < ApplicationRecord
|
|||||||
## Merge the provided User's SSO details into the current user and drop the provided user to ensure the unity
|
## Merge the provided User's SSO details into the current user and drop the provided user to ensure the unity
|
||||||
## @param sso_user {User} the provided user will be DELETED after the merge was successful
|
## @param sso_user {User} the provided user will be DELETED after the merge was successful
|
||||||
def merge_from_sso(sso_user)
|
def merge_from_sso(sso_user)
|
||||||
|
logger.debug "[User::merge_from_sso] initiated with parameter #{sso_user}"
|
||||||
# update the attributes to link the account to the sso account
|
# update the attributes to link the account to the sso account
|
||||||
self.provider = sso_user.provider
|
self.provider = sso_user.provider
|
||||||
self.uid = sso_user.uid
|
self.uid = sso_user.uid
|
||||||
@ -303,24 +321,34 @@ class User < ApplicationRecord
|
|||||||
# check that the email duplication was resolved
|
# check that the email duplication was resolved
|
||||||
if sso_user.email.end_with? '-duplicate'
|
if sso_user.email.end_with? '-duplicate'
|
||||||
email_addr = sso_user.email.match(/^<([^>]+)>.{20}-duplicate$/)[1]
|
email_addr = sso_user.email.match(/^<([^>]+)>.{20}-duplicate$/)[1]
|
||||||
|
logger.error 'duplicate email was not resolved'
|
||||||
raise(DuplicateIndexError, email_addr) unless email_addr == email
|
raise(DuplicateIndexError, email_addr) unless email_addr == email
|
||||||
end
|
end
|
||||||
|
|
||||||
# update the user's profile to set the data managed by the SSO
|
# update the user's profile to set the data managed by the SSO
|
||||||
auth_provider = AuthProvider.from_strategy_name(sso_user.provider)
|
auth_provider = AuthProvider.from_strategy_name(sso_user.provider)
|
||||||
|
logger.debug "found auth_provider=#{auth_provider.name}"
|
||||||
auth_provider.sso_fields.each do |field|
|
auth_provider.sso_fields.each do |field|
|
||||||
value = sso_user.get_data_from_sso_mapping(field)
|
value = sso_user.get_data_from_sso_mapping(field)
|
||||||
|
logger.debug "mapping sso field #{field} with value=#{value}"
|
||||||
# we do not merge the email field if its end with the special value '-duplicate' as this means
|
# we do not merge the email field if its end with the special value '-duplicate' as this means
|
||||||
# that the user is currently merging with the account that have the same email than the sso
|
# that the user is currently merging with the account that have the same email than the sso
|
||||||
set_data_from_sso_mapping(field, value) unless field == 'user.email' && value.end_with?('-duplicate')
|
set_data_from_sso_mapping(field, value) unless field == 'user.email' && value.end_with?('-duplicate')
|
||||||
end
|
end
|
||||||
|
|
||||||
# run the account transfer in an SQL transaction to ensure data integrity
|
# run the account transfer in an SQL transaction to ensure data integrity
|
||||||
User.transaction do
|
begin
|
||||||
# remove the temporary account
|
User.transaction do
|
||||||
sso_user.destroy
|
# remove the temporary account
|
||||||
# finally, save the new details
|
logger.debug 'removing the temporary user'
|
||||||
save!
|
sso_user.destroy
|
||||||
|
# finally, save the new details
|
||||||
|
logger.debug 'saving the updated user'
|
||||||
|
save!
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
|
logger.error "error while merging user #{sso_user.id} into #{id}: #{e.message}"
|
||||||
|
raise e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
json.user User.mapping
|
json.user User.mapping
|
||||||
|
|
||||||
json.profile Profile.mapping
|
json.profile Profile.mapping
|
@ -252,7 +252,7 @@ en:
|
|||||||
mappings: "Mappings"
|
mappings: "Mappings"
|
||||||
#edition/creation form of an OAuth2 authentication provider
|
#edition/creation form of an OAuth2 authentication provider
|
||||||
oauth2:
|
oauth2:
|
||||||
common_url: "Common URL"
|
common_url: "Server root URL"
|
||||||
common_url_is_required: "Common URL is required."
|
common_url_is_required: "Common URL is required."
|
||||||
provided_url_is_not_a_valid_url: "Provided URL is not a valid URL."
|
provided_url_is_not_a_valid_url: "Provided URL is not a valid URL."
|
||||||
authorization_endpoint: "Authorization endpoint"
|
authorization_endpoint: "Authorization endpoint"
|
||||||
@ -274,6 +274,7 @@ en:
|
|||||||
api_endpoint_url: "API endpoint URL"
|
api_endpoint_url: "API endpoint URL"
|
||||||
api_type: "API type"
|
api_type: "API type"
|
||||||
api_fields: "API fields"
|
api_fields: "API fields"
|
||||||
|
api_field_help: "JsonPath syntax is supported.\n If many fields are selected, the first one will be used.\n Example: $.data[*].name"
|
||||||
#machine/training slot modification modal
|
#machine/training slot modification modal
|
||||||
confirm_modify_slot_modal:
|
confirm_modify_slot_modal:
|
||||||
change_the_slot: "Change the slot"
|
change_the_slot: "Change the slot"
|
||||||
|
@ -26,9 +26,9 @@ For this guide, we will use [GitHub](https://developer.github.com/v3/oauth/) as
|
|||||||
3. The slug of this name is used in the callback URL provided to the SSO server (eg. /users/auth/oauth2-**github**/callback)
|
3. The slug of this name is used in the callback URL provided to the SSO server (eg. /users/auth/oauth2-**github**/callback)
|
||||||
|
|
||||||
- Fulfill the form with the following parameters:
|
- Fulfill the form with the following parameters:
|
||||||
- **Common URL**: `https://github.com/login/oauth/` This is the common part in the URLs of the two following parameters.
|
- **Server root URL**: `https://github.com` This is the domain name of the where the SSO server is located.
|
||||||
- **Authorization endpoint**: `authorize` This URL can be found [here](https://developer.github.com/v3/oauth/).
|
- **Authorization endpoint**: `/login/oauth/authorize` This URL can be found [here](https://developer.github.com/v3/oauth/).
|
||||||
- **Token Acquisition Endpoint**: `access_token` This URL can be found [here](https://developer.github.com/v3/oauth/).
|
- **Token Acquisition Endpoint**: `/login/oauth/access_token` This URL can be found [here](https://developer.github.com/v3/oauth/).
|
||||||
- **Profile edition URL**: `https://github.com/settings/profile` This is the URL where you are directed when you click on `Edit profile` in your GitHub dashboard.
|
- **Profile edition URL**: `https://github.com/settings/profile` This is the URL where you are directed when you click on `Edit profile` in your GitHub dashboard.
|
||||||
- **Client identifier**: Your Client ID, collected just before.
|
- **Client identifier**: Your Client ID, collected just before.
|
||||||
- **Client secret**: Your Client Secret, collected just before.
|
- **Client secret**: Your Client Secret, collected just before.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'omniauth-oauth2'
|
require 'omniauth-oauth2'
|
||||||
|
require 'jsonpath'
|
||||||
|
|
||||||
module OmniAuth::Strategies
|
module OmniAuth::Strategies
|
||||||
# Authentication strategy provided trough oAuth 2.0
|
# Authentication strategy provided trough oAuth 2.0
|
||||||
@ -24,6 +25,12 @@ module OmniAuth::Strategies
|
|||||||
authorize_url: active_provider.providable.authorization_endpoint,
|
authorize_url: active_provider.providable.authorization_endpoint,
|
||||||
token_url: active_provider.providable.token_endpoint
|
token_url: active_provider.providable.token_endpoint
|
||||||
|
|
||||||
|
def authorize_params
|
||||||
|
super.tap do |params|
|
||||||
|
params[:scope] = ENV['OAUTH2_SCOPE']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def callback_url
|
def callback_url
|
||||||
url = Rails.application.config.action_controller.default_url_options
|
url = Rails.application.config.action_controller.default_url_options
|
||||||
"#{url[:protocol]}://#{url[:host]}#{script_name}#{callback_path}"
|
"#{url[:protocol]}://#{url[:host]}#{script_name}#{callback_path}"
|
||||||
@ -46,9 +53,15 @@ module OmniAuth::Strategies
|
|||||||
# retrieve data from various url, querying each only once
|
# retrieve data from various url, querying each only once
|
||||||
def raw_info
|
def raw_info
|
||||||
@raw_info ||= {}
|
@raw_info ||= {}
|
||||||
|
puts "[raw_info] @raw_infos = #{@raw_info&.to_json}"
|
||||||
unless @raw_info.size.positive?
|
unless @raw_info.size.positive?
|
||||||
OmniAuth::Strategies::SsoOauth2Provider.active_provider.providable.o_auth2_mappings.each do |mapping|
|
OmniAuth::Strategies::SsoOauth2Provider.active_provider.providable.o_auth2_mappings.each do |mapping|
|
||||||
|
puts "mapping = #{mapping&.to_json}"
|
||||||
unless @raw_info.key?(mapping.api_endpoint.to_sym)
|
unless @raw_info.key?(mapping.api_endpoint.to_sym)
|
||||||
|
puts "api_endpoint = #{mapping.api_endpoint.to_sym}"
|
||||||
|
puts "access_token = #{access_token&.to_json}"
|
||||||
|
puts "token get = #{access_token.get(mapping.api_endpoint)}"
|
||||||
|
puts "parsed = #{access_token.get(mapping.api_endpoint).parsed}"
|
||||||
@raw_info[mapping.api_endpoint.to_sym] = access_token.get(mapping.api_endpoint).parsed
|
@raw_info[mapping.api_endpoint.to_sym] = access_token.get(mapping.api_endpoint).parsed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -58,6 +71,7 @@ module OmniAuth::Strategies
|
|||||||
|
|
||||||
def parsed_info
|
def parsed_info
|
||||||
@parsed_info ||= {}
|
@parsed_info ||= {}
|
||||||
|
puts "[parsed_info] @parsed_info = #{@parsed_info.to_json}"
|
||||||
unless @parsed_info.size.positive?
|
unless @parsed_info.size.positive?
|
||||||
OmniAuth::Strategies::SsoOauth2Provider.active_provider.providable.o_auth2_mappings.each do |mapping|
|
OmniAuth::Strategies::SsoOauth2Provider.active_provider.providable.o_auth2_mappings.each do |mapping|
|
||||||
|
|
||||||
@ -88,7 +102,8 @@ module OmniAuth::Strategies
|
|||||||
|
|
||||||
## NO TRANSFORMATION
|
## NO TRANSFORMATION
|
||||||
else
|
else
|
||||||
@parsed_info[local_sym(mapping)] = raw_info[mapping.api_endpoint.to_sym][mapping.api_field]
|
puts "@parsed_info[#{local_sym(mapping)}] found in #{raw_info[mapping.api_endpoint.to_sym]}"
|
||||||
|
@parsed_info[local_sym(mapping)] = ::JsonPath.new(mapping.api_field).on(raw_info[mapping.api_endpoint.to_sym]).first
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user