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:
commit
6585359144
@ -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
|
||||
|
@ -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]]])
|
||||
|
@ -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"
|
||||
|
@ -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} > {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} />}
|
||||
|
@ -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,
|
||||
|
@ -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}` } })} />
|
||||
)}
|
||||
|
@ -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)} />
|
||||
)}
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user