mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
(feat) optional sso debug logs according to SSO_DEBUG env var
This commit is contained in:
parent
c2f4e0f3b7
commit
67850a6f9f
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
- Support for JSONPath syntax in OAuth2 SSO fields mapping
|
- Support for JSONPath syntax in OAuth2 SSO fields mapping
|
||||||
- Basic support for OAuth2 scopes through an environment variable
|
- Basic support for OAuth2 scopes through an environment variable
|
||||||
- Add debug logs for the SSO authentication process
|
- Ability to enable debug logs for the SSO authentication process using `SSO_DEBUG=true`
|
||||||
- Remove case sensitivity for the SSO account mapping process
|
- Remove case sensitivity for the SSO account mapping process
|
||||||
- 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
|
||||||
|
@ -19,7 +19,7 @@ class API::MembersController < API::ApiController
|
|||||||
|
|
||||||
def last_subscribed
|
def last_subscribed
|
||||||
@query = User.active.with_role(:member)
|
@query = User.active.with_role(:member)
|
||||||
.includes(profile: [:user_avatar])
|
.includes(:statistic_profile, profile: [:user_avatar])
|
||||||
.where('is_allow_contact = true AND confirmed_at IS NOT NULL')
|
.where('is_allow_contact = true AND confirmed_at IS NOT NULL')
|
||||||
.order('created_at desc')
|
.order('created_at desc')
|
||||||
.limit(params[:last])
|
.limit(params[:last])
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Handle authentication actions via OmniAuth (used by SSO providers)
|
||||||
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
|
require 'sso_logger'
|
||||||
|
logger = SsoLogger.new
|
||||||
|
|
||||||
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"
|
logger.info "[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'
|
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'])
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Suspense } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is a wrapper that display a loader while the children components have their rendering suspended
|
* This component is a wrapper that display a loader while the children components have their rendering suspended.
|
||||||
*/
|
*/
|
||||||
export const Loader: React.FC = ({ children }) => {
|
export const Loader: React.FC = ({ children }) => {
|
||||||
const loading = (
|
const loading = (
|
||||||
|
@ -55,7 +55,7 @@ interface AbstractPaymentModalProps {
|
|||||||
/**
|
/**
|
||||||
* This component is an abstract modal that must be extended by each payment gateway to include its payment form.
|
* This component is an abstract modal that must be extended by each payment gateway to include its payment form.
|
||||||
*
|
*
|
||||||
* This component must not be called directly but must be extended for each implemented payment gateway
|
* This component must not be called directly but must be extended for each implemented payment gateway.
|
||||||
* @see https://reactjs.org/docs/composition-vs-inheritance.html
|
* @see https://reactjs.org/docs/composition-vs-inheritance.html
|
||||||
*/
|
*/
|
||||||
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, onError, cart, updateCart, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName, title, preventCgv, preventScheduleInfo, modalSize }) => {
|
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, onError, cart, updateCart, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName, title, preventCgv, preventScheduleInfo, modalSize }) => {
|
||||||
|
@ -9,7 +9,7 @@ interface AvatarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the user-profile's picture or a placeholder
|
* This component renders the user-profile's picture or a placeholder.
|
||||||
*/
|
*/
|
||||||
export const Avatar: React.FC<AvatarProps> = ({ user, className }) => {
|
export const Avatar: React.FC<AvatarProps> = ({ user, className }) => {
|
||||||
/**
|
/**
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
# User is a physical or moral person with its authentication parameters
|
# User is a physical or moral person with its authentication parameters
|
||||||
# It is linked to the Profile model with hold information about this person (like address, name, etc.)
|
# It is linked to the Profile model with hold information about this person (like address, name, etc.)
|
||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
|
require 'sso_logger'
|
||||||
|
|
||||||
include NotifyWith::NotificationReceiver
|
include NotifyWith::NotificationReceiver
|
||||||
include NotifyWith::NotificationAttachedObject
|
include NotifyWith::NotificationAttachedObject
|
||||||
# Include default devise modules. Others available are:
|
# Include default devise modules. Others available are:
|
||||||
@ -201,6 +203,7 @@ class User < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.from_omniauth(auth)
|
def self.from_omniauth(auth)
|
||||||
|
logger = SsoLogger.new
|
||||||
logger.debug "[User::from_omniauth] initiated with parameter #{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
|
||||||
@ -309,6 +312,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 = SsoLogger.new
|
||||||
logger.debug "[User::merge_from_sso] initiated with parameter #{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
|
||||||
|
@ -138,7 +138,18 @@ Please, ensure you know what you're doing, as this can lead to serious security
|
|||||||
A comma separated list of settings that cannot be changed from the UI.
|
A comma separated list of settings that cannot be changed from the UI.
|
||||||
Please refer to https://github.com/sleede/fab-manager/blob/master/app/models/setting.rb for a list of possible values.
|
Please refer to https://github.com/sleede/fab-manager/blob/master/app/models/setting.rb for a list of possible values.
|
||||||
Only the system administrator can change them, with the command: `ENV=value rails fablab:setup:env_to_db`
|
Only the system administrator can change them, with the command: `ENV=value rails fablab:setup:env_to_db`
|
||||||
|
<a name="OAUTH2_SCOPE"></a>
|
||||||
|
|
||||||
|
OAUTH2_SCOPE
|
||||||
|
|
||||||
|
A comma separated list of scopes that will be requested when authenticating with OAuth2.
|
||||||
|
<a name="SSO_DEBUG"></a>
|
||||||
|
|
||||||
|
SSO_DEBUG
|
||||||
|
|
||||||
|
If set to `true`, the SSO authentication process will print more debug logs.
|
||||||
|
Use in accordance with LOG_LEVEL=debug.
|
||||||
|
Please do not enable this in production, as it can expose sensitive information.
|
||||||
<a name="internationalization-settings"></a>
|
<a name="internationalization-settings"></a>
|
||||||
## Internationalization setting.
|
## Internationalization setting.
|
||||||
<a name="APP_LOCALE"></a>
|
<a name="APP_LOCALE"></a>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require 'omniauth-oauth2'
|
require 'omniauth-oauth2'
|
||||||
require 'jsonpath'
|
require 'jsonpath'
|
||||||
|
require 'sso_logger'
|
||||||
|
|
||||||
module OmniAuth::Strategies
|
module OmniAuth::Strategies
|
||||||
# Authentication strategy provided trough oAuth 2.0
|
# Authentication strategy provided trough oAuth 2.0
|
||||||
@ -52,58 +53,57 @@ 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
|
||||||
|
logger = SsoLogger.new
|
||||||
|
|
||||||
@raw_info ||= {}
|
@raw_info ||= {}
|
||||||
puts "[raw_info] @raw_infos = #{@raw_info&.to_json}"
|
logger.debug "[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}"
|
logger.debug "mapping = #{mapping&.to_json}"
|
||||||
unless @raw_info.key?(mapping.api_endpoint.to_sym)
|
next if @raw_info.key?(mapping.api_endpoint.to_sym)
|
||||||
puts "api_endpoint = #{mapping.api_endpoint.to_sym}"
|
|
||||||
puts "access_token = #{access_token&.to_json}"
|
logger.debug "api_endpoint = #{mapping.api_endpoint.to_sym}"
|
||||||
puts "token get = #{access_token.get(mapping.api_endpoint)}"
|
logger.debug "access_token = #{access_token&.to_json}"
|
||||||
puts "parsed = #{access_token.get(mapping.api_endpoint).parsed}"
|
logger.debug "token get = #{access_token.get(mapping.api_endpoint)}"
|
||||||
|
logger.debug "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
|
||||||
end
|
|
||||||
@raw_info
|
@raw_info
|
||||||
end
|
end
|
||||||
|
|
||||||
def parsed_info
|
def parsed_info
|
||||||
|
logger = SsoLogger.new
|
||||||
|
|
||||||
@parsed_info ||= {}
|
@parsed_info ||= {}
|
||||||
puts "[parsed_info] @parsed_info = #{@parsed_info.to_json}"
|
logger.debug "[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|
|
||||||
|
|
||||||
|
raw_data = ::JsonPath.new(mapping.api_field).on(raw_info[mapping.api_endpoint.to_sym]).first
|
||||||
|
logger.debug "@parsed_info[#{local_sym(mapping)}] mapped from #{raw_data}"
|
||||||
if mapping.transformation
|
if mapping.transformation
|
||||||
case mapping.transformation['type']
|
case mapping.transformation['type']
|
||||||
## INTEGER
|
## INTEGER
|
||||||
when 'integer'
|
when 'integer'
|
||||||
@parsed_info[local_sym(mapping)] = map_integer(mapping.transformation,
|
@parsed_info[local_sym(mapping)] = map_integer(mapping.transformation, raw_data)
|
||||||
mapping.api_endpoint.to_sym,
|
|
||||||
mapping.api_field)
|
|
||||||
|
|
||||||
## BOOLEAN
|
## BOOLEAN
|
||||||
when 'boolean'
|
when 'boolean'
|
||||||
@parsed_info[local_sym(mapping)] = map_boolean(mapping.transformation,
|
@parsed_info[local_sym(mapping)] = map_boolean(mapping.transformation, raw_data)
|
||||||
mapping.api_endpoint.to_sym,
|
|
||||||
mapping.api_field)
|
|
||||||
|
|
||||||
## DATE
|
## DATE
|
||||||
when 'date'
|
when 'date'
|
||||||
@params[local_sym(mapping)] = map_date(mapping.transformation,
|
@params[local_sym(mapping)] = map_date(mapping.transformation, raw_data)
|
||||||
mapping.api_endpoint.to_sym,
|
|
||||||
mapping.api_field)
|
|
||||||
|
|
||||||
## OTHER TRANSFORMATIONS (not supported)
|
## OTHER TRANSFORMATIONS (not supported)
|
||||||
else
|
else
|
||||||
@parsed_info[local_sym(mapping)] = raw_info[mapping.api_endpoint.to_sym][mapping.api_field]
|
@parsed_info[local_sym(mapping)] = raw_data
|
||||||
end
|
end
|
||||||
|
|
||||||
## NO TRANSFORMATION
|
## NO TRANSFORMATION
|
||||||
else
|
else
|
||||||
puts "@parsed_info[#{local_sym(mapping)}] found in #{raw_info[mapping.api_endpoint.to_sym]}"
|
@parsed_info[local_sym(mapping)] = raw_data
|
||||||
@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
|
||||||
@ -116,38 +116,38 @@ module OmniAuth::Strategies
|
|||||||
(mapping.local_model + '.' + mapping.local_field).to_sym
|
(mapping.local_model + '.' + mapping.local_field).to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
def map_integer(transformation, api_endpoint, api_field)
|
def map_integer(transformation, raw_data)
|
||||||
value = nil
|
value = nil
|
||||||
transformation['mapping'].each do |m|
|
transformation['mapping'].each do |m|
|
||||||
if m['from'] == raw_info[api_endpoint][api_field]
|
if m['from'] == raw_data
|
||||||
value = m['to']
|
value = m['to']
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# if no transformation had set any value, return the raw value
|
# if no transformation had set any value, return the raw value
|
||||||
value || raw_info[api_endpoint.to_sym][api_field]
|
value || raw_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def map_boolean(transformation, api_endpoint, api_field)
|
def map_boolean(transformation, raw_data)
|
||||||
return false if raw_info[api_endpoint][api_field] == transformation['false_value']
|
return false if raw_data == transformation['false_value']
|
||||||
|
|
||||||
true if raw_info[api_endpoint][api_field] == transformation['true_value']
|
true if raw_data == transformation['true_value']
|
||||||
end
|
end
|
||||||
|
|
||||||
def map_date(transformation, api_endpoint, api_field)
|
def map_date(transformation, raw_data)
|
||||||
case transformation['format']
|
case transformation['format']
|
||||||
when 'iso8601'
|
when 'iso8601'
|
||||||
DateTime.iso8601(raw_info[api_endpoint][api_field])
|
DateTime.iso8601(raw_data)
|
||||||
when 'rfc2822'
|
when 'rfc2822'
|
||||||
DateTime.rfc2822(raw_info[api_endpoint][api_field])
|
DateTime.rfc2822(raw_data)
|
||||||
when 'rfc3339'
|
when 'rfc3339'
|
||||||
DateTime.rfc3339(raw_info[api_endpoint][api_field])
|
DateTime.rfc3339(raw_data)
|
||||||
when 'timestamp-s'
|
when 'timestamp-s'
|
||||||
DateTime.strptime(raw_info[api_endpoint][api_field], '%s')
|
DateTime.strptime(raw_data, '%s')
|
||||||
when 'timestamp-ms'
|
when 'timestamp-ms'
|
||||||
DateTime.strptime(raw_info[api_endpoint][api_field], '%Q')
|
DateTime.strptime(raw_data, '%Q')
|
||||||
else
|
else
|
||||||
DateTime.parse(raw_info[api_endpoint][api_field])
|
DateTime.parse(raw_data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
27
lib/sso_logger.rb
Normal file
27
lib/sso_logger.rb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# This class provides logging functionalities for SSO authentication
|
||||||
|
class SsoLogger
|
||||||
|
def initialize()
|
||||||
|
@logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
|
||||||
|
@log_status = ENV.fetch('SSO_DEBUG') { false }
|
||||||
|
end
|
||||||
|
|
||||||
|
def debug(message)
|
||||||
|
return unless @log_status
|
||||||
|
|
||||||
|
@logger.tagged('SSO') { @logger.debug(message) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def info(message)
|
||||||
|
@logger.tagged('SSO') { @logger.info(message) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def warn(message)
|
||||||
|
@logger.tagged('SSO') { @logger.warn(message) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(message)
|
||||||
|
@logger.tagged('SSO') { @logger.error(message) }
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user