1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(bug) use arrays for OIDC scopes in front and DB.

Send the scope as a string separated with spaces to the OIDC provider.
This commit is contained in:
Sylvain 2022-05-31 18:06:35 +02:00
parent 78f7cdcb8c
commit 2172c102c9
6 changed files with 39 additions and 18 deletions

View File

@ -10,6 +10,7 @@ import { OpenIdConnectProvider } from '../../models/authentication-provider';
import SsoClient from '../../api/external/sso';
import { FieldPathValue } from 'react-hook-form/dist/types/path';
import { FormMultiSelect } from '../form/form-multi-select';
import { difference } from 'lodash';
interface OpenidConnectFormProps<TFieldValues, TContext extends object> {
register: UseFormRegister<TFieldValues>,
@ -60,6 +61,21 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
];
};
/**
* Return the list of scopes that are available for the current configuration.
* The resulting list is provided through the callback parameter.
*/
const loadScopes = (inputValue: string, callback: (options: Array<{ value: string, label: string }>) => void): void => {
const current = currentFormValues.scope || [];
if (scopesAvailable) {
// add custom scopes to the list of available scopes
const unlisted = difference(current, scopesAvailable);
callback(scopesAvailable.concat(unlisted).map(scope => ({ value: scope, label: scope })));
} else {
current.map(scope => ({ value: scope, label: scope }));
}
};
/**
* Callback that check for the existence of the .well-known/openid-configuration endpoint, for the given issuer.
* This callback is triggered when the user changes the issuer field.
@ -102,18 +118,12 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
]}
valueDefault={'basic'}
control={control} />
{!scopesAvailable && <FormInput id="providable_attributes.scope"
register={register}
label={t('app.admin.authentication.openid_connect_form.scope')}
placeholder="openid profile email"
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />} />}
{scopesAvailable && <FormMultiSelect id="providable_attributes.scope"
label={t('app.admin.authentication.openid_connect_form.scope')}
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />}
options={scopesAvailable.map((scope) => ({ value: scope, label: scope }))}
delimiter={' '}
creatable
control={control} />}
<FormMultiSelect id="providable_attributes.scope"
label={t('app.admin.authentication.openid_connect_form.scope')}
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />}
loadOptions={loadScopes}
creatable
control={control} />
<FormSelect id="providable_attributes.prompt"
label={t('app.admin.authentication.openid_connect_form.prompt')}
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.prompt_help_html" />}

View File

@ -16,7 +16,6 @@ interface CommonProps<TFieldValues, TContext extends object, TOptionValue> exten
onChange?: (values: Array<TOptionValue>) => void,
placeholder?: string,
creatable?: boolean,
delimiter?: string
}
// we should provide either an array of options or a function that returns a promise, but not both
@ -36,7 +35,7 @@ type selectOption<TOptionValue> = { value: TOptionValue, label: string, select?:
* This component is a wrapper around react-select to use with react-hook-form.
* 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, formState, warning, loadOptions, creatable, delimiter }: 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, formState, warning, loadOptions, creatable }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
const { t } = useTranslation('shared');
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
@ -121,7 +120,6 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
className: 'rs',
ref,
value: getCurrentValues(value),
delimiter,
placeholder,
isDisabled,
isMulti: true,

View File

@ -47,7 +47,7 @@ export interface OpenIdConnectProvider {
issuer: string,
discovery: boolean,
client_auth_method?: 'basic' | 'jwks',
scope?: string,
scope?: Array<string>,
prompt?: 'none' | 'login' | 'consent' | 'select_account',
send_scope_to_token_endpoint?: string,
client__identifier: string,

View File

@ -17,6 +17,10 @@ class OpenIdConnectProvider < ApplicationRecord
validates :prompt, inclusion: { in: %w[none login consent select_account], allow_nil: true }
validates :client_auth_method, inclusion: { in: %w[basic jwks] }
def scope
self[:scope].join(' ')
end
def config
OpenIdConnectProvider.columns.map(&:name).filter { |n| !n.start_with?('client__') && n != 'profile_url' }.map do |n|
[n, send(n)]

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
# Previously, the OpenID Connect scope was a string, scopes were separated by commas.
# To be more fron-end friendly, we now use an array.
class ChangeOidcScopeToArray < ActiveRecord::Migration[5.2]
def change
change_column :open_id_connect_providers, :scope, "varchar[] USING (string_to_array(scope, ','))"
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_05_17_140916) do
ActiveRecord::Schema.define(version: 2022_05_31_160223) do
# These are extensions that must be enabled in order to support this database
enable_extension "fuzzystrmatch"
@ -415,7 +415,7 @@ ActiveRecord::Schema.define(version: 2022_05_17_140916) do
t.string "issuer"
t.boolean "discovery"
t.string "client_auth_method"
t.string "scope"
t.string "scope", array: true
t.string "response_type"
t.string "response_mode"
t.string "display"