1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-09 00:18:26 +01:00
unleash.unleash/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx
Christopher Kolstad f245e55391
fix: trim sso URL fields (#7301) (#7303)
What the title says. There are input values that are whitespace
sensitive, so this will trim clientId and entity field, preventing the
form from sending leading or trailing whitespace. Will make a PR on
enterprise as well to trim on the backend as well.
2024-06-06 11:29:53 +02:00

296 lines
11 KiB
TypeScript

import type React from 'react';
import { useEffect, useState } from 'react';
import {
Button,
FormControlLabel,
Grid,
Switch,
TextField,
} from '@mui/material';
import { Alert } from '@mui/material';
import { AutoCreateForm } from '../AutoCreateForm/AutoCreateForm';
import useToast from 'hooks/useToast';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings';
import useAuthSettingsApi from 'hooks/api/actions/useAuthSettingsApi/useAuthSettingsApi';
import { formatUnknownError } from 'utils/formatUnknownError';
import { removeEmptyStringFields } from 'utils/removeEmptyStringFields';
import { SsoGroupSettings } from '../SsoGroupSettings';
import type { IRole } from 'interfaces/role';
const initialState = {
enabled: false,
autoCreate: false,
enableGroupSyncing: false,
addGroupsScope: false,
unleashHostname: location.hostname,
entityId: '',
signOnUrl: '',
certificate: '',
signOutUrl: '',
spCertificate: '',
groupJsonPath: '',
};
type State = typeof initialState & {
defaultRootRole?: string;
defaultRootRoleId?: number;
};
export const SamlAuth = () => {
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const [data, setData] = useState<State>(initialState);
const { config } = useAuthSettings('saml');
const { updateSettings, errors, loading } = useAuthSettingsApi('saml');
useEffect(() => {
if (config.entityId) {
setData(config);
}
}, [config]);
const updateField = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.name, event.target.value);
};
const trimAndUpdateField = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.name, event.target.value.trim());
};
const updateEnabled = () => {
setData({ ...data, enabled: !data.enabled });
};
const setValue = (
name: string,
value: string | boolean | number | undefined,
) => {
setData({
...data,
[name]: value,
});
};
const onUpdateRole = (role: IRole | null) => {
setData({
...data,
defaultRootRole: undefined,
defaultRootRoleId: role?.id,
});
};
const onSubmit = async (event: React.SyntheticEvent) => {
event.preventDefault();
try {
await updateSettings(removeEmptyStringFields(data));
setToastData({
title: 'Settings stored',
type: 'success',
});
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
return (
<>
<Grid container sx={{ mb: 3 }}>
<Grid item md={12}>
<Alert severity='info'>
Please read the{' '}
<a
href='https://www.unleash-hosted.com/docs/enterprise-authentication'
target='_blank'
rel='noreferrer'
>
documentation
</a>{' '}
to learn how to integrate with specific SAML 2.0
providers (Okta, Keycloak, etc). <br />
Callback URL:{' '}
<code>{uiConfig.unleashUrl}/auth/saml/callback</code>
</Alert>
</Grid>
</Grid>
<form onSubmit={onSubmit}>
<Grid container spacing={3}>
<Grid item md={5} mb={2}>
<strong>Enable</strong>
<p>Enable SAML 2.0 Authentication.</p>
</Grid>
<Grid item md={6}>
<FormControlLabel
control={
<Switch
onChange={updateEnabled}
value={data.enabled}
name='enabled'
checked={data.enabled}
/>
}
label={data.enabled ? 'Enabled' : 'Disabled'}
/>
</Grid>
</Grid>
<Grid container spacing={3} mb={2}>
<Grid item md={5}>
<strong>Entity ID</strong>
<p>(Required) The Entity Identity provider issuer.</p>
</Grid>
<Grid item md={6}>
<TextField
onChange={trimAndUpdateField}
label='Entity ID'
name='entityId'
value={data.entityId}
disabled={!data.enabled}
style={{ width: '400px' }}
variant='outlined'
size='small'
required
/>
</Grid>
</Grid>
<Grid container spacing={3} mb={2}>
<Grid item md={5}>
<strong>Single Sign-On URL</strong>
<p>
(Required) The url to redirect the user to for
signing in.
</p>
</Grid>
<Grid item md={6}>
<TextField
onChange={trimAndUpdateField}
label='Single Sign-On URL'
name='signOnUrl'
value={data.signOnUrl}
disabled={!data.enabled}
style={{ width: '400px' }}
variant='outlined'
size='small'
required
/>
</Grid>
</Grid>
<Grid container spacing={3} mb={4}>
<Grid item md={5}>
<strong>X.509 Certificate</strong>
<p>
(Required) The certificate used to sign the SAML 2.0
request.
</p>
</Grid>
<Grid item md={7}>
<TextField
onChange={updateField}
label='X.509 Certificate'
name='certificate'
value={data.certificate}
disabled={!data.enabled}
style={{ width: '100%' }}
InputProps={{
style: {
fontSize: '0.6em',
fontFamily: 'monospace',
},
}}
multiline
rows={14}
maxRows={14}
variant='outlined'
size='small'
required
/>
</Grid>
</Grid>
<h3>Optional Configuration</h3>
<Grid container spacing={3} mb={2}>
<Grid item md={5}>
<strong>Single Sign-out URL</strong>
<p>
(Optional) The url to redirect the user to for
signing out of the IDP.
</p>
</Grid>
<Grid item md={6}>
<TextField
onChange={trimAndUpdateField}
label='Single Sign-out URL'
name='signOutUrl'
value={data.signOutUrl}
disabled={!data.enabled}
style={{ width: '400px' }}
variant='outlined'
size='small'
/>
</Grid>
</Grid>
<Grid container spacing={3} mb={2}>
<Grid item md={5}>
<strong>Service Provider X.509 Certificate</strong>
<p>
(Optional) The private certificate used by the
Service Provider used to sign the SAML 2.0 request
towards the IDP. E.g. used to sign single logout
requests (SLO).
</p>
</Grid>
<Grid item md={7}>
<TextField
onChange={updateField}
label='X.509 Certificate'
name='spCertificate'
value={data.spCertificate}
disabled={!data.enabled}
style={{ width: '100%' }}
InputProps={{
style: {
fontSize: '0.6em',
fontFamily: 'monospace',
},
}}
multiline
rows={14}
maxRows={14}
variant='outlined'
size='small'
/>
</Grid>
</Grid>
<SsoGroupSettings
ssoType='SAML'
data={data}
setValue={setValue}
/>
<AutoCreateForm
data={data}
setValue={setValue}
onUpdateRole={onUpdateRole}
/>
<Grid container spacing={3}>
<Grid item md={5}>
<Button
variant='contained'
color='primary'
type='submit'
disabled={loading}
>
Save
</Button>{' '}
<p>
<small style={{ color: 'red' }}>
{errors?.message}
</small>
</p>
</Grid>
</Grid>
</form>
</>
);
};