mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-07 01:54:16 +01:00
(bug) unable to create openid connect provider from the interface
This commit is contained in:
parent
822e104c9f
commit
b3848596bf
@ -12,6 +12,7 @@ class API::AuthProvidersController < API::ApiController
|
|||||||
def create
|
def create
|
||||||
authorize AuthProvider
|
authorize AuthProvider
|
||||||
@provider = AuthProvider.new(provider_params)
|
@provider = AuthProvider.new(provider_params)
|
||||||
|
AuthProviderService.auto_configure(@provider)
|
||||||
if @provider.save
|
if @provider.save
|
||||||
render :show, status: :created, location: @provider
|
render :show, status: :created, location: @provider
|
||||||
else
|
else
|
||||||
@ -97,7 +98,7 @@ class API::AuthProvidersController < API::ApiController
|
|||||||
params.require(:auth_provider)
|
params.require(:auth_provider)
|
||||||
.permit(:name, :providable_type,
|
.permit(:name, :providable_type,
|
||||||
providable_attributes: %i[id issuer discovery client_auth_method scope prompt send_scope_to_token_endpoint
|
providable_attributes: %i[id issuer discovery client_auth_method scope prompt send_scope_to_token_endpoint
|
||||||
client__identifier client__secretclient__authorization_endpoint client__token_endpoint
|
client__identifier client__secret client__authorization_endpoint client__token_endpoint
|
||||||
client__userinfo_endpoint client__jwks_uri client__end_session_endpoint profile_url],
|
client__userinfo_endpoint client__jwks_uri client__end_session_endpoint profile_url],
|
||||||
auth_provider_mappings_attributes: [:id, :local_model, :local_field, :api_field, :api_endpoint, :api_data_type,
|
auth_provider_mappings_attributes: [:id, :local_model, :local_field, :api_field, :api_endpoint, :api_data_type,
|
||||||
:_destroy, transformation: [:type, :format, :true_value, :false_value,
|
:_destroy, transformation: [:type, :format, :true_value, :false_value,
|
||||||
|
@ -103,15 +103,16 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
|
|||||||
valueDefault={'basic'}
|
valueDefault={'basic'}
|
||||||
control={control} />
|
control={control} />
|
||||||
{!scopesAvailable && <FormInput id="providable_attributes.scope"
|
{!scopesAvailable && <FormInput id="providable_attributes.scope"
|
||||||
register={register}
|
register={register}
|
||||||
label={t('app.admin.authentication.openid_connect_form.scope')}
|
label={t('app.admin.authentication.openid_connect_form.scope')}
|
||||||
placeholder="openid,profile,email"
|
placeholder="openid,profile,email"
|
||||||
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />} />}
|
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />} />}
|
||||||
{scopesAvailable && <FormMultiSelect id="providable_attributes.scope"
|
{scopesAvailable && <FormMultiSelect id="providable_attributes.scope"
|
||||||
label={t('app.admin.authentication.openid_connect_form.scope')}
|
expectedResult="string"
|
||||||
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />}
|
label={t('app.admin.authentication.openid_connect_form.scope')}
|
||||||
options={scopesAvailable.map((scope) => ({ value: scope, label: scope }))}
|
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />}
|
||||||
control={control} />}
|
options={scopesAvailable.map((scope) => ({ value: scope, label: scope }))}
|
||||||
|
control={control} />}
|
||||||
<FormSelect id="providable_attributes.prompt"
|
<FormSelect id="providable_attributes.prompt"
|
||||||
label={t('app.admin.authentication.openid_connect_form.prompt')}
|
label={t('app.admin.authentication.openid_connect_form.prompt')}
|
||||||
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.prompt_help_html" />}
|
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.prompt_help_html" />}
|
||||||
|
@ -16,6 +16,7 @@ interface FormSelectProps<TFieldValues, TContext extends object, TOptionValue> e
|
|||||||
className?: string,
|
className?: string,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
|
expectedResult?: 'array' | 'string'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +29,7 @@ type selectOption<TOptionValue> = { value: TOptionValue, label: string };
|
|||||||
* This component is a wrapper around react-select to use with react-hook-form.
|
* This component is a wrapper around react-select to use with react-hook-form.
|
||||||
* It is a multi-select component.
|
* It is a multi-select component.
|
||||||
*/
|
*/
|
||||||
export const FormMultiSelect = <TFieldValues extends FieldValues, TContext extends object, TOptionValue>({ id, label, tooltip, className, control, placeholder, options, valuesDefault, error, rules, disabled, onChange }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
|
export const FormMultiSelect = <TFieldValues extends FieldValues, TContext extends object, TOptionValue>({ id, label, tooltip, className, control, placeholder, options, valuesDefault, error, rules, disabled, onChange, expectedResult }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
|
||||||
const classNames = [
|
const classNames = [
|
||||||
'form-multi-select form-item',
|
'form-multi-select form-item',
|
||||||
`${className || ''}`,
|
`${className || ''}`,
|
||||||
@ -40,11 +41,30 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
|
|||||||
/**
|
/**
|
||||||
* The following callback will trigger the onChange callback, if it was passed to this component,
|
* The following callback will trigger the onChange callback, if it was passed to this component,
|
||||||
* when the selected option changes.
|
* when the selected option changes.
|
||||||
|
* It will also update the react-hook-form's value, according to the provided 'result' property (string or array).
|
||||||
*/
|
*/
|
||||||
const onChangeCb = (newValues: Array<TOptionValue>): void => {
|
const onChangeCb = (newValues: Array<TOptionValue>, rhfOnChange): void => {
|
||||||
if (typeof onChange === 'function') {
|
if (typeof onChange === 'function') {
|
||||||
onChange(newValues);
|
onChange(newValues);
|
||||||
}
|
}
|
||||||
|
if (expectedResult === 'string') {
|
||||||
|
rhfOnChange(newValues.join(','));
|
||||||
|
} else {
|
||||||
|
rhfOnChange(newValues);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will return the currently selected options, according to the provided react-hook-form's value.
|
||||||
|
*/
|
||||||
|
const getCurrentValues = (value: Array<TOptionValue>|string): Array<selectOption<TOptionValue>> => {
|
||||||
|
let values: Array<TOptionValue> = [];
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
values = value.split(',') as Array<unknown> as Array<TOptionValue>;
|
||||||
|
} else {
|
||||||
|
values = value;
|
||||||
|
}
|
||||||
|
return options.filter(c => values?.includes(c.value));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -64,11 +84,10 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
|
|||||||
<Select ref={ref}
|
<Select ref={ref}
|
||||||
classNamePrefix="rs"
|
classNamePrefix="rs"
|
||||||
className="rs"
|
className="rs"
|
||||||
value={options.filter(c => value?.includes(c.value))}
|
value={getCurrentValues(value)}
|
||||||
onChange={val => {
|
onChange={val => {
|
||||||
const values = val?.map(c => c.value);
|
const values = val?.map(c => c.value);
|
||||||
onChangeCb(values);
|
onChangeCb(values, onChange);
|
||||||
onChange(values);
|
|
||||||
}}
|
}}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
options={options}
|
options={options}
|
||||||
@ -79,3 +98,7 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
|
|||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FormMultiSelect.defaultProps = {
|
||||||
|
expectedResult: 'array'
|
||||||
|
};
|
||||||
|
@ -17,12 +17,6 @@ class OpenIdConnectProvider < ApplicationRecord
|
|||||||
validates :prompt, inclusion: { in: %w[none login consent select_account], allow_nil: true }
|
validates :prompt, inclusion: { in: %w[none login consent select_account], allow_nil: true }
|
||||||
validates :client_auth_method, inclusion: { in: %w[basic jwks] }
|
validates :client_auth_method, inclusion: { in: %w[basic jwks] }
|
||||||
|
|
||||||
before_validation :set_post_logout_redirect_uri
|
|
||||||
before_validation :set_client_scheme_host_port
|
|
||||||
before_validation :set_redirect_uri
|
|
||||||
before_validation :set_display
|
|
||||||
before_validation :set_response_type
|
|
||||||
|
|
||||||
def config
|
def config
|
||||||
OpenIdConnectProvider.columns.map(&:name).filter { |n| !n.start_with?('client__') && n != 'profile_url' }.map do |n|
|
OpenIdConnectProvider.columns.map(&:name).filter { |n| !n.start_with?('client__') && n != 'profile_url' }.map do |n|
|
||||||
[n, send(n)]
|
[n, send(n)]
|
||||||
@ -34,36 +28,4 @@ class OpenIdConnectProvider < ApplicationRecord
|
|||||||
[n.sub('client__', ''), send(n)]
|
[n.sub('client__', ''), send(n)]
|
||||||
end.to_h
|
end.to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_post_logout_redirect_uri
|
|
||||||
self.post_logout_redirect_uri = "#{ENV.fetch('DEFAULT_PROTOCOL')}://#{ENV.fetch('DEFAULT_HOST')}/sessions/sign_out"
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_redirect_uri
|
|
||||||
self.client__redirect_uri = "#{ENV.fetch('DEFAULT_PROTOCOL')}://#{ENV.fetch('DEFAULT_HOST')}/users/auth/#{auth_provider.strategy_name}/callback"
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_display
|
|
||||||
self.display = 'page'
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_response_mode
|
|
||||||
self.response_mode = 'query'
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_response_type
|
|
||||||
self.response_type = 'code'
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_client_scheme_host_port
|
|
||||||
require 'uri'
|
|
||||||
|
|
||||||
URI.parse(issuer).tap do |uri|
|
|
||||||
self.client__scheme = uri.scheme
|
|
||||||
self.client__host = uri.host
|
|
||||||
self.client__port = uri.port
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
31
app/services/auth_provider_service.rb
Normal file
31
app/services/auth_provider_service.rb
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Provides methods for the configuration of authentication providers.
|
||||||
|
class AuthProviderService
|
||||||
|
class << self
|
||||||
|
def auto_configure(provider)
|
||||||
|
auto_configure_open_id_connect(provider) if provider.providable_type == OpenIdConnectProvider.name
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def auto_configure_open_id_connect(provider)
|
||||||
|
raise NoMethodError unless provider.providable
|
||||||
|
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
provider.providable.post_logout_redirect_uri = "#{ENV.fetch('DEFAULT_PROTOCOL')}://#{ENV.fetch('DEFAULT_HOST')}/sessions/sign_out"
|
||||||
|
provider.providable.client__redirect_uri =
|
||||||
|
"#{ENV.fetch('DEFAULT_PROTOCOL')}://#{ENV.fetch('DEFAULT_HOST')}/users/auth/#{provider.strategy_name}/callback"
|
||||||
|
provider.providable.display = 'page'
|
||||||
|
provider.providable.response_mode = 'query'
|
||||||
|
provider.providable.response_type = 'code'
|
||||||
|
|
||||||
|
URI.parse(provider.providable.issuer).tap do |uri|
|
||||||
|
provider.providable.client__scheme = uri.scheme
|
||||||
|
provider.providable.client__host = uri.host
|
||||||
|
provider.providable.client__port = uri.port
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user