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

Merge branch 'dev' for release 5.4.11

This commit is contained in:
Sylvain 2022-07-06 14:37:40 +02:00
commit 6585359144
10 changed files with 50 additions and 21 deletions

View File

@ -2,6 +2,13 @@
## next deploy
## v5.4.11 2022 July 06
- Fix a bug: social networks icons not shown in firefox
- Fix a bug: Gender, Address and Birthday are not mapped properly from SSO (#365)
- Fix a bug: OIDC scopes are not shown in the configuration form select
- Fix a bug: OIDC scopes are not saved
## v5.4.10 2022 July 05
- Increased About page title's size

View File

@ -85,10 +85,10 @@ class API::AuthProvidersController < API::ApiController
def provider_params
if params['auth_provider']['providable_type'] == DatabaseProvider.name
params.require(:auth_provider).permit(:name, :providable_type, providable_attributes: [:id])
params.require(:auth_provider).permit(:id, :name, :providable_type, providable_attributes: [:id])
elsif params['auth_provider']['providable_type'] == OAuth2Provider.name
params.require(:auth_provider)
.permit(:name, :providable_type,
.permit(:id, :name, :providable_type,
providable_attributes: %i[id base_url token_endpoint authorization_endpoint
profile_url client_id client_secret scopes],
auth_provider_mappings_attributes: [:id, :local_model, :local_field, :api_field, :api_endpoint, :api_data_type,
@ -96,10 +96,11 @@ class API::AuthProvidersController < API::ApiController
mapping: %i[from to]]])
elsif params['auth_provider']['providable_type'] == OpenIdConnectProvider.name
params.require(:auth_provider)
.permit(:name, :providable_type,
providable_attributes: %i[id issuer discovery client_auth_method scope prompt send_scope_to_token_endpoint
client__identifier client__secret client__authorization_endpoint client__token_endpoint
client__userinfo_endpoint client__jwks_uri client__end_session_endpoint profile_url],
.permit(:id, :name, :providable_type,
providable_attributes: [:id, :issuer, :discovery, :client_auth_method, :prompt, :send_scope_to_token_endpoint,
:client__identifier, :client__secret, :client__authorization_endpoint, :client__token_endpoint,
:client__userinfo_endpoint, :client__jwks_uri, :client__end_session_endpoint, :profile_url,
scope: []],
auth_provider_mappings_attributes: [:id, :local_model, :local_field, :api_field, :api_endpoint, :api_data_type,
:_destroy, transformation: [:type, :format, :true_value, :false_value,
mapping: %i[from to]]])

View File

@ -29,6 +29,8 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
// saves the state of the discovery endpoint
const [discoveryAvailable, setDiscoveryAvailable] = useState<boolean>(false);
const [scopesAvailable, setScopesAvailable] = useState<string[]>(null);
// this is a workaround for https://github.com/JedWatson/react-select/issues/1879
const [selectKey, setSelectKey] = useState<number>(0);
// when we have detected a discovery endpoint, we mark it as available
useEffect(() => {
@ -38,6 +40,11 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
);
}, [discoveryAvailable]);
// this will force the scope "select" to re-fetch the options
useEffect(() => {
setSelectKey(selectKey + 1);
}, [scopesAvailable]);
// when the component is mounted, we try to discover the discovery endpoint for the current configuration (if any)
useEffect(() => {
checkForDiscoveryEndpoint({ target: { value: currentFormValues?.issuer } } as React.ChangeEvent<HTMLInputElement>);
@ -125,6 +132,7 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
label={t('app.admin.authentication.openid_connect_form.scope')}
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_form.scope_help_html" />}
loadOptions={loadScopes}
selectKey={selectKey.toString()}
creatable
control={control} />
<FormSelect id="providable_attributes.prompt"

View File

@ -9,6 +9,7 @@ import { mappingType } from '../../models/authentication-provider';
import { BooleanMappingForm } from './boolean-mapping-form';
import { DateMappingForm } from './date-mapping-form';
import { StringMappingForm } from './string-mapping-form';
import { FormInput } from '../form/form-input';
interface TypeMappingModalProps<TFieldValues, TContext extends object> {
model: string,
@ -38,6 +39,10 @@ export const TypeMappingModal = <TFieldValues extends FieldValues, TContext exte
confirmButton={<i className="fa fa-check" />}
onConfirm={toggleModal}>
<span>{model} &gt; {field} ({t('app.admin.authentication.type_mapping_modal.TYPE_expected', { TYPE: t(`app.admin.authentication.type_mapping_modal.types.${type}`) })})</span>
<FormInput register={register}
id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.type`}
type="hidden"
defaultValue={type} />
{type === 'integer' && <IntegerMappingForm register={register} control={control} fieldMappingId={fieldMappingId} />}
{type === 'boolean' && <BooleanMappingForm register={register} fieldMappingId={fieldMappingId} />}
{type === 'date' && <DateMappingForm control={control} fieldMappingId={fieldMappingId} />}

View File

@ -16,6 +16,7 @@ interface CommonProps<TFieldValues, TContext extends object, TOptionValue> exten
onChange?: (values: Array<TOptionValue>) => void,
placeholder?: string,
creatable?: boolean,
selectKey?: string,
}
// we should provide either an array of options or a function that returns a promise, but not both
@ -35,7 +36,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 }: 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, selectKey }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
const { t } = useTranslation('shared');
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
@ -52,7 +53,7 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
useEffect(() => {
if (typeof loadOptions === 'function') {
loadOptions('', options => {
setAllOptions(options);
setTimeout(() => setAllOptions(options), 1);
});
}
}, [loadOptions]);
@ -73,7 +74,7 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
* This function will return the currently selected options, according to the selectedOptions state.
*/
const getCurrentValues = (value: Array<TOptionValue>): Array<selectOption<TOptionValue>> => {
return allOptions.filter(c => value?.includes(c.value));
return allOptions?.filter(c => value?.includes(c.value));
};
/**
@ -119,6 +120,7 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
classNamePrefix: 'rs',
className: 'rs',
ref,
key: selectKey,
value: getCurrentValues(value),
placeholder,
isDisabled,

View File

@ -59,7 +59,9 @@ export const EditSocials = <TFieldValues extends FieldValues>({ register, setVal
<>
<div className='social-icons'>
{userNetworks.map((network, index) =>
!selectedNetworks.includes(network) && <img key={index} src={`${Icons}#${network.name}`} onClick={() => selectNetwork(network)}></img>
!selectedNetworks.includes(network) && <svg key={index} onClick={() => selectNetwork(network)} viewBox="0 0 24 24" >
<use href={`${Icons}#${network.name}`} />
</svg>
)}
</div>
{selectNetwork.length && <div className='social-inputs'>
@ -79,7 +81,7 @@ export const EditSocials = <TFieldValues extends FieldValues>({ register, setVal
label={network.name}
disabled={disabled}
placeholder={t('app.shared.edit_socials.url_placeholder')}
icon={<img src={`${Icons}#${network.name}`}></img>}
icon={<svg viewBox="0 0 24 24"><use href={`${Icons}#${network.name}`}/></svg>}
addOn={<Trash size={16} />}
addOnAction={() => dispatch({ type: 'delete', payload: { network, field: `profile_attributes.${network.name}` } })} />
)}

View File

@ -85,7 +85,7 @@ export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, o
{fabNetworks.map((network, index) =>
selectedNetworks.includes(network) &&
<a key={index} href={network.url} target='_blank' rel="noreferrer">
<img src={`${Icons}#${network.name}`}></img>
<svg viewBox="0 0 24 24"><use href={`${Icons}#${network.name}`}/></svg>
</a>
)}
</div>
@ -95,7 +95,9 @@ export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, o
<div className='social-icons'>
{fabNetworks.map((network, index) =>
!selectedNetworks.includes(network) &&
<img key={index} src={`${Icons}#${network.name}`} onClick={() => selectNetwork(network)}></img>
<svg viewBox="0 0 24 24" key={index} onClick={() => selectNetwork(network)}>
<use href={`${Icons}#${network.name}`} />
</svg>
)}
</div>
{selectNetwork.length && <div className='social-inputs'>
@ -114,7 +116,7 @@ export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, o
defaultValue={network.url}
label={network.name}
placeholder={t('app.shared.fab_socials.url_placeholder')}
icon={<img src={`${Icons}#${network.name}`}></img>}
icon={<svg viewBox="0 0 24 24"><use href={`${Icons}#${network.name}`}/></svg>}
addOn={<Trash size={16} />}
addOnAction={() => remove(network)} />
)}

View File

@ -25,10 +25,12 @@ export interface AuthenticationProviderMapping {
format: 'iso8601' | 'rfc2822' | 'rfc3339' | 'timestamp-s' | 'timestamp-ms',
true_value: string,
false_value: string,
mapping: {
from: string,
to: number|string
}
mapping: [
{
from: string,
to: number|string
}
]
}
}

View File

@ -581,7 +581,7 @@ body.container {
border-radius: 3px;
overflow: hidden;
}
& > img {
& > svg {
border: 1px solid var(--gray-soft-dark);
background-color: var(--gray-soft-lightest);
&:hover { opacity: 0.65; }
@ -589,7 +589,7 @@ body.container {
& > a {
transition: transform 200ms ease-in-out;
&:hover { transform: translateY(-4px); }
img {
svg {
max-width: 100%;
height: inherit;
}

View File

@ -1,6 +1,6 @@
{
"name": "fab-manager",
"version": "5.4.10",
"version": "5.4.11",
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
"keywords": [
"fablab",