1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-23 00:16:25 +01:00
unleash.unleash/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx

296 lines
11 KiB
TypeScript
Raw Normal View History

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';
2022-03-28 10:49:59 +02:00
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',
});
refactor: fix misc TS errors (#729) * refactor: update test deps * refactor: remove unused ts-expect-error annotations * refactor: add missing arg and return types * refactor: the loading prop is optional * refactor: add missing arg and return types * reafactor: fix value arg type * refactor: fix missing array type * refactor: the parameters field is an array * refactor: use undefined instead of null in state * refactor: add missing params type * refactor: add missing children prop * refactor: add missing array type * refactor: add missing React imports * refactor: use correct IProjectEnvironment type * refactor: type errors as unknown * refactor: the index prop is required * refactor: fix date prop type * refactor: fix tooltip placement prop type * refactor: fix environments state type * refactor: add missing arg types * refactor: add guard for undefined field * refactor: fix ChangePassword prop types * refactor: fix MUI import paths * refactor: add missing arg type * refactor: fix showDialog prop type * refactor: remove unused openUpdateDialog prop * refactor: add missing non-null assertion * refactor: remove unused types prop * refactor: stricten API error handler types * refactor: add missing undefined check * refactor: add missing IProject id field * refactor: fix ConditionallyRender condition prop types * refactor: remove unused args * refactor: add AddVariant prop types * refactor: add types to UIContext * refactor: fix event arg type * refactor: add missing default impressionData field * refactor: fix handleDeleteEnvironment prop args * refactor: fix IFeatureMetrics field requirements * refactor: add missing element types to ConditionallyRender * refactor: remove unused ProjectAccess projectId prop * refactor: add missing undefined check * refactor: fix getCreateTogglePath arg type * refactor: add missing IStrategyPayload import * refactor: remove unused user arg * refactor: add missing event arg type * refactor: add missing style object types * refactor: improve userApiErrors prop type * refactor: the Dialogue onClose prop is optional * refactor: fix the AddonEvents setEventValue prop type
2022-02-25 10:55:39 +01:00
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
return (
<>
fix: add confirmation to disable password login (#3829) https://linear.app/unleash/issue/2-1071/prevent-users-from-disabling-password-authentication-when-there-are-no Improves the behavior of disabling password based login by adding some relevant information and a confirmation dialog with a warning. This felt better than trying to disable the toggle, by still allowing the end users to make the decision, except now it should be a properly informed decision with confirmation. ![image](https://github.com/Unleash/unleash/assets/14320932/2ca754d8-cfa2-4fda-984d-0c34b89750f3) - **Password based administrators**: Admin accounts that have a password set; - **Other administrators**: Other admin users that do not have a password. May be SSO, but may also be users that did not set a password yet; - **Admin service accounts**: Service accounts that have the admin root role. Depending on how you're using the SA this may not necessarily mean locking yourself out of an admin account, especially if you secured its token beforehand; - **Admin API tokens**: Similar to the above. If you secured an admin API token beforehand, you still have access to all features through the API; Each one of them link to the respective page inside Unleash (e.g. users page, service accounts page, tokens page...); If you try to disable and press "save", and only in that scenario, you are presented with the following confirmation dialog: ![image](https://github.com/Unleash/unleash/assets/14320932/5ad6d105-ad47-4d31-a1df-04737aed4e00)
2023-05-23 16:56:34 +02:00
<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>
</>
);
};