Compare commits
	
		
			1 Commits
		
	
	
		
			dependabot
			...
			aut-1180
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | db966eae2d | 
| @@ -0,0 +1,12 @@ | ||||
| export async function up(knex) { | ||||
|   await knex('config').insert({ | ||||
|     key: 'userManagement.preventUsersFromUpdatingTheirProfile', | ||||
|     value: { | ||||
|       data: false | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| export async function down(knex) { | ||||
|   await knex('config').where({ key: 'userManagement.preventUsersFromUpdatingTheirProfile' }).delete(); | ||||
| }; | ||||
| @@ -7,6 +7,7 @@ import DialogContentText from '@mui/material/DialogContentText'; | ||||
| import DialogTitle from '@mui/material/DialogTitle'; | ||||
| import * as React from 'react'; | ||||
| import { useNavigate, useSearchParams } from 'react-router-dom'; | ||||
| import { useQueryClient } from '@tanstack/react-query'; | ||||
|  | ||||
| import { AppPropType } from 'propTypes/propTypes'; | ||||
| import AppAuthClientsDialog from 'components/AppAuthClientsDialog/index.ee'; | ||||
| @@ -17,7 +18,6 @@ import useFormatMessage from 'hooks/useFormatMessage'; | ||||
| import { generateExternalLink } from 'helpers/translationValues'; | ||||
| import { Form } from './style'; | ||||
| import useAppAuth from 'hooks/useAppAuth'; | ||||
| import { useQueryClient } from '@tanstack/react-query'; | ||||
|  | ||||
| function AddAppConnection(props) { | ||||
|   const { application, connectionId, onClose } = props; | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import TextField from 'components/TextField'; | ||||
| import useFormatMessage from 'hooks/useFormatMessage'; | ||||
| import useCreateAccessToken from 'hooks/useCreateAccessToken'; | ||||
| import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; | ||||
| import usePreventUsersFromUpdatingTheirProfile from 'hooks/usePreventUsersFromUpdatingTheirProfile'; | ||||
|  | ||||
| function LoginForm() { | ||||
|   const isCloud = useCloud(); | ||||
| @@ -19,6 +20,7 @@ function LoginForm() { | ||||
|   const formatMessage = useFormatMessage(); | ||||
|   const enqueueSnackbar = useEnqueueSnackbar(); | ||||
|   const authentication = useAuthentication(); | ||||
|   const preventUsersFromUpdatingTheirProfile = usePreventUsersFromUpdatingTheirProfile(); | ||||
|   const { mutateAsync: createAccessToken, isPending: loading } = | ||||
|     useCreateAccessToken(); | ||||
|  | ||||
| @@ -84,7 +86,7 @@ function LoginForm() { | ||||
|           sx={{ mb: 1 }} | ||||
|         /> | ||||
|  | ||||
|         {isCloud && ( | ||||
|         {isCloud && !preventUsersFromUpdatingTheirProfile && ( | ||||
|           <Link | ||||
|             component={RouterLink} | ||||
|             to={URLS.FORGOT_PASSWORD} | ||||
|   | ||||
| @@ -40,6 +40,10 @@ function Switch(props) { | ||||
|         <FormControlLabel | ||||
|           className={className} | ||||
|           {...FormControlLabelProps} | ||||
|           componentsProps={{ | ||||
|             typography: { | ||||
|               display: 'flex', | ||||
|           }}} | ||||
|           control={ | ||||
|             <MuiSwitch | ||||
|               {...switchProps} | ||||
|   | ||||
| @@ -0,0 +1,13 @@ | ||||
| import useAutomatischConfig from 'hooks/useAutomatischConfig'; | ||||
|  | ||||
| export default function usePreventUsersFromUpdatingTheirProfile() { | ||||
|   const { data, isSuccess } = | ||||
|     useAutomatischConfig(); | ||||
|   const automatischConfig = data?.data; | ||||
|  | ||||
|   const preventUsersFromUpdatingTheirProfile = isSuccess ? automatischConfig['userManagement.preventUsersFromUpdatingTheirProfile'] : false; | ||||
|  | ||||
|   console.log('preventUsersFromUpdatingTheirProfile', preventUsersFromUpdatingTheirProfile, automatischConfig) | ||||
|  | ||||
|   return preventUsersFromUpdatingTheirProfile; | ||||
| } | ||||
| @@ -259,20 +259,26 @@ | ||||
|   "userInterfacePage.primaryLightColorFieldLabel": "Primary light color", | ||||
|   "userInterfacePage.svgDataFieldLabel": "Logo SVG code", | ||||
|   "userInterfacePage.submit": "Update", | ||||
|   "authenticationPage.title": "Single Sign-On with SAML", | ||||
|   "authenticationForm.active": "Active", | ||||
|   "authenticationForm.name": "Name", | ||||
|   "authenticationForm.certificate": "Certificate", | ||||
|   "authenticationForm.signatureAlgorithm": "Signature algorithm", | ||||
|   "authenticationForm.issuer": "Issuer", | ||||
|   "authenticationForm.entryPoint": "Entry point", | ||||
|   "authenticationForm.firstnameAttributeName": "Firstname attribute name", | ||||
|   "authenticationForm.surnameAttributeName": "Surname attribute name", | ||||
|   "authenticationForm.emailAttributeName": "Email attribute name", | ||||
|   "authenticationForm.roleAttributeName": "Role attribute name", | ||||
|   "authenticationForm.defaultRole": "Default role", | ||||
|   "authenticationForm.successfullySaved": "The provider has been saved.", | ||||
|   "authenticationForm.save": "Save", | ||||
|   "authenticationPage.title": "Authentication", | ||||
|   "authenticationConfig.title": "User management", | ||||
|   "authenticationConfig.userManagementPreventUsersFromUpdatingTheirProfile": "Prevent users from updating their profile", | ||||
|   "authenticationConfig.userManagementPreventUsersFromUpdatingTheirProfileTooltip": "This will prevent users from updating their full name, email and password. This is useful when you want to manage users from your own identity provider.", | ||||
|   "authenticationConfig.save": "Save", | ||||
|   "authenticationConfig.successfullySaved": "The configuration has been saved.", | ||||
|   "samlAuthenticationPage.title": "Single Sign-On with SAML", | ||||
|   "samlAuthenticationForm.active": "Active", | ||||
|   "samlAuthenticationForm.name": "Name", | ||||
|   "samlAuthenticationForm.certificate": "Certificate", | ||||
|   "samlAuthenticationForm.signatureAlgorithm": "Signature algorithm", | ||||
|   "samlAuthenticationForm.issuer": "Issuer", | ||||
|   "samlAuthenticationForm.entryPoint": "Entry point", | ||||
|   "samlAuthenticationForm.firstnameAttributeName": "Firstname attribute name", | ||||
|   "samlAuthenticationForm.surnameAttributeName": "Surname attribute name", | ||||
|   "samlAuthenticationForm.emailAttributeName": "Email attribute name", | ||||
|   "samlAuthenticationForm.roleAttributeName": "Role attribute name", | ||||
|   "samlAuthenticationForm.defaultRole": "Default role", | ||||
|   "samlAuthenticationForm.successfullySaved": "The provider has been saved.", | ||||
|   "samlAuthenticationForm.save": "Save", | ||||
|   "roleMappingsForm.title": "Role mappings", | ||||
|   "roleMappingsForm.remoteRoleName": "Remote role name", | ||||
|   "roleMappingsForm.role": "Role", | ||||
|   | ||||
| @@ -0,0 +1,98 @@ | ||||
| import { useMutation } from '@apollo/client'; | ||||
| import LoadingButton from '@mui/lab/LoadingButton'; | ||||
| import Stack from '@mui/material/Stack'; | ||||
| import Switch from 'components/Switch'; | ||||
| import Typography from '@mui/material/Typography'; | ||||
| import Tooltip from '@mui/material/Tooltip'; | ||||
| import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; | ||||
| import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; | ||||
|  | ||||
| import Form from 'components/Form'; | ||||
| import useFormatMessage from 'hooks/useFormatMessage'; | ||||
| import useAutomatischConfig from 'hooks/useAutomatischConfig'; | ||||
| import { UPDATE_CONFIG } from 'graphql/mutations/update-config.ee'; | ||||
| import { useQueryClient } from '@tanstack/react-query'; | ||||
|  | ||||
| function AuthenticationConfig() { | ||||
|   const formatMessage = useFormatMessage(); | ||||
|   const enqueueSnackbar = useEnqueueSnackbar(); | ||||
|   const queryClient = useQueryClient(); | ||||
|   const { data, isLoading: isAutomatischConfigLoading } = | ||||
|     useAutomatischConfig(); | ||||
|   const automatischConfig = data?.data; | ||||
|  | ||||
|   const [ | ||||
|     updateConfig, | ||||
|     { loading: updateConfigLoading }, | ||||
|   ] = useMutation(UPDATE_CONFIG); | ||||
|  | ||||
|   const handleSubmit = async (values) => { | ||||
|     try { | ||||
|       await updateConfig({ | ||||
|         variables: { | ||||
|           input: { | ||||
|             'userManagement.preventUsersFromUpdatingTheirProfile': values.userManagement.preventUsersFromUpdatingTheirProfile, | ||||
|           }, | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       await queryClient.invalidateQueries({ | ||||
|         queryKey: ['automatisch', 'config'], | ||||
|       }); | ||||
|  | ||||
|       enqueueSnackbar(formatMessage('authenticationConfig.successfullySaved'), { | ||||
|         variant: 'success', | ||||
|         SnackbarProps: { | ||||
|           'data-test': 'snackbar-update-role-mappings-success', | ||||
|         }, | ||||
|       }); | ||||
|     } catch (error) { | ||||
|       throw new Error('Failed while saving!'); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   if (isAutomatischConfigLoading) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <Typography variant="h4"> | ||||
|         {formatMessage('authenticationConfig.title')} | ||||
|       </Typography> | ||||
|  | ||||
|       <Form defaultValues={automatischConfig} onSubmit={handleSubmit}> | ||||
|         <Stack direction="column" spacing={2}> | ||||
|           <Switch | ||||
|             name="userManagement.preventUsersFromUpdatingTheirProfile" | ||||
|             label={<> | ||||
|               {formatMessage('authenticationConfig.userManagementPreventUsersFromUpdatingTheirProfile')} | ||||
|  | ||||
|               <Tooltip | ||||
|                 title={formatMessage('authenticationConfig.userManagementPreventUsersFromUpdatingTheirProfileTooltip')} | ||||
|                 sx={{ ml: 1 }} | ||||
|               > | ||||
|                 <InfoOutlinedIcon /> | ||||
|               </Tooltip> | ||||
|             </>} | ||||
|           /> | ||||
|  | ||||
|           <LoadingButton | ||||
|             type="submit" | ||||
|             variant="contained" | ||||
|             color="primary" | ||||
|             sx={{ boxShadow: 2 }} | ||||
|             loading={updateConfigLoading} | ||||
|           > | ||||
|             {formatMessage('authenticationConfig.save')} | ||||
|           </LoadingButton> | ||||
|         </Stack> | ||||
|       </Form> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| AuthenticationConfig.propTypes = { | ||||
| }; | ||||
|  | ||||
| export default AuthenticationConfig; | ||||
| @@ -1,7 +1,6 @@ | ||||
| import PropTypes from 'prop-types'; | ||||
| import { useMutation } from '@apollo/client'; | ||||
| import LoadingButton from '@mui/lab/LoadingButton'; | ||||
| import Divider from '@mui/material/Divider'; | ||||
| import Stack from '@mui/material/Stack'; | ||||
| import Typography from '@mui/material/Typography'; | ||||
| import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; | ||||
| @@ -85,7 +84,6 @@ function RoleMappings({ provider, providerLoading }) { | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <Divider sx={{ pt: 2 }} /> | ||||
|       <Typography variant="h3"> | ||||
|         {formatMessage('roleMappingsForm.title')} | ||||
|       </Typography> | ||||
|   | ||||
| @@ -75,7 +75,7 @@ function SamlConfiguration({ provider, providerLoading }) { | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       enqueueSnackbar(formatMessage('authenticationForm.successfullySaved'), { | ||||
|       enqueueSnackbar(formatMessage('samlAuthenticationForm.successfullySaved'), { | ||||
|         variant: 'success', | ||||
|         SnackbarProps: { | ||||
|           'data-test': 'snackbar-save-saml-provider-success', | ||||
| @@ -98,18 +98,18 @@ function SamlConfiguration({ provider, providerLoading }) { | ||||
|       <Stack direction="column" gap={2}> | ||||
|         <Switch | ||||
|           name="active" | ||||
|           label={formatMessage('authenticationForm.active')} | ||||
|           label={formatMessage('samlAuthenticationForm.active')} | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="name" | ||||
|           label={formatMessage('authenticationForm.name')} | ||||
|           label={formatMessage('samlAuthenticationForm.name')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="certificate" | ||||
|           label={formatMessage('authenticationForm.certificate')} | ||||
|           label={formatMessage('samlAuthenticationForm.certificate')} | ||||
|           fullWidth | ||||
|           multiline | ||||
|         /> | ||||
| @@ -126,44 +126,44 @@ function SamlConfiguration({ provider, providerLoading }) { | ||||
|           renderInput={(params) => ( | ||||
|             <MuiTextField | ||||
|               {...params} | ||||
|               label={formatMessage('authenticationForm.signatureAlgorithm')} | ||||
|               label={formatMessage('samlAuthenticationForm.signatureAlgorithm')} | ||||
|             /> | ||||
|           )} | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="issuer" | ||||
|           label={formatMessage('authenticationForm.issuer')} | ||||
|           label={formatMessage('samlAuthenticationForm.issuer')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="entryPoint" | ||||
|           label={formatMessage('authenticationForm.entryPoint')} | ||||
|           label={formatMessage('samlAuthenticationForm.entryPoint')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="firstnameAttributeName" | ||||
|           label={formatMessage('authenticationForm.firstnameAttributeName')} | ||||
|           label={formatMessage('samlAuthenticationForm.firstnameAttributeName')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="surnameAttributeName" | ||||
|           label={formatMessage('authenticationForm.surnameAttributeName')} | ||||
|           label={formatMessage('samlAuthenticationForm.surnameAttributeName')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="emailAttributeName" | ||||
|           label={formatMessage('authenticationForm.emailAttributeName')} | ||||
|           label={formatMessage('samlAuthenticationForm.emailAttributeName')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <TextField | ||||
|           required={true} | ||||
|           name="roleAttributeName" | ||||
|           label={formatMessage('authenticationForm.roleAttributeName')} | ||||
|           label={formatMessage('samlAuthenticationForm.roleAttributeName')} | ||||
|           fullWidth | ||||
|         /> | ||||
|         <ControlledAutocomplete | ||||
| @@ -175,7 +175,7 @@ function SamlConfiguration({ provider, providerLoading }) { | ||||
|           renderInput={(params) => ( | ||||
|             <MuiTextField | ||||
|               {...params} | ||||
|               label={formatMessage('authenticationForm.defaultRole')} | ||||
|               label={formatMessage('samlAuthenticationForm.defaultRole')} | ||||
|             /> | ||||
|           )} | ||||
|           loading={isRolesLoading} | ||||
| @@ -187,7 +187,7 @@ function SamlConfiguration({ provider, providerLoading }) { | ||||
|           sx={{ boxShadow: 2 }} | ||||
|           loading={loading} | ||||
|         > | ||||
|           {formatMessage('authenticationForm.save')} | ||||
|           {formatMessage('samlAuthenticationForm.save')} | ||||
|         </LoadingButton> | ||||
|       </Stack> | ||||
|     </Form> | ||||
|   | ||||
| @@ -2,11 +2,14 @@ import Grid from '@mui/material/Grid'; | ||||
| import Stack from '@mui/material/Stack'; | ||||
| import PageTitle from 'components/PageTitle'; | ||||
| import Container from 'components/Container'; | ||||
| import Divider from '@mui/material/Divider'; | ||||
| import useFormatMessage from 'hooks/useFormatMessage'; | ||||
| import useSamlAuthProvider from 'hooks/useSamlAuthProvider'; | ||||
| import AuthenticationConfig from './AuthenticationConfig'; | ||||
| import SamlConfiguration from './SamlConfiguration'; | ||||
| import RoleMappings from './RoleMappings'; | ||||
| import useAdminSamlAuthProviders from 'hooks/useAdminSamlAuthProviders.ee'; | ||||
|  | ||||
| function AuthenticationPage() { | ||||
|   const formatMessage = useFormatMessage(); | ||||
|  | ||||
| @@ -16,20 +19,37 @@ function AuthenticationPage() { | ||||
|   const { data, isLoading: isProviderLoading } = useSamlAuthProvider({ | ||||
|     samlAuthProviderId, | ||||
|   }); | ||||
|  | ||||
|   const provider = data?.data; | ||||
|  | ||||
|   return ( | ||||
|     <Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}> | ||||
|       <Grid container item xs={12} sm={10} md={9}> | ||||
|         <Grid container item xs={12} sx={{ mb: [2, 5] }}> | ||||
|         <Grid container item xs={12}> | ||||
|           <PageTitle>{formatMessage('authenticationPage.title')}</PageTitle> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid item xs={12} sx={{ pt: 5, pb: 5 }}> | ||||
|           <Stack spacing={5}> | ||||
|             <AuthenticationConfig /> | ||||
|  | ||||
|             <Divider /> | ||||
|           </Stack> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid container item xs={12}> | ||||
|           <PageTitle variant="h4">{formatMessage('samlAuthenticationPage.title')}</PageTitle> | ||||
|         </Grid> | ||||
|  | ||||
|         <Grid item xs={12} sx={{ pt: 5, pb: 5 }}> | ||||
|           <Stack spacing={5}> | ||||
|             <SamlConfiguration | ||||
|               provider={provider} | ||||
|               providerLoading={isProviderLoading} | ||||
|             /> | ||||
|  | ||||
|             <Divider /> | ||||
|  | ||||
|             <RoleMappings | ||||
|               provider={provider} | ||||
|               providerLoading={isProviderLoading} | ||||
|   | ||||
| @@ -10,6 +10,7 @@ export default function Login() { | ||||
|       <Container maxWidth="sm"> | ||||
|         <Stack direction="column" gap={2}> | ||||
|           <LoginForm /> | ||||
|  | ||||
|           <SsoProviders /> | ||||
|         </Stack> | ||||
|       </Container> | ||||
|   | ||||
| @@ -29,12 +29,14 @@ import adminSettingsRoutes from './adminSettingsRoutes'; | ||||
| import Notifications from 'pages/Notifications'; | ||||
| import useAutomatischConfig from 'hooks/useAutomatischConfig'; | ||||
| import useAuthentication from 'hooks/useAuthentication'; | ||||
| import usePreventUsersFromUpdatingTheirProfile from 'hooks/usePreventUsersFromUpdatingTheirProfile'; | ||||
| import useAutomatischInfo from 'hooks/useAutomatischInfo'; | ||||
| import Installation from 'pages/Installation'; | ||||
|  | ||||
| function Routes() { | ||||
|   const { data: automatischInfo, isSuccess } = useAutomatischInfo(); | ||||
|   const { data: configData } = useAutomatischConfig(); | ||||
|   const preventUsersFromUpdatingTheirProfile = usePreventUsersFromUpdatingTheirProfile(); | ||||
|   const { isAuthenticated } = useAuthentication(); | ||||
|   const config = configData?.data; | ||||
|  | ||||
| @@ -134,14 +136,14 @@ function Routes() { | ||||
|         } | ||||
|       /> | ||||
|  | ||||
|       <Route | ||||
|       {preventUsersFromUpdatingTheirProfile === false && <Route | ||||
|         path={URLS.FORGOT_PASSWORD} | ||||
|         element={ | ||||
|           <PublicLayout> | ||||
|             <ForgotPassword /> | ||||
|           </PublicLayout> | ||||
|         } | ||||
|       /> | ||||
|       />} | ||||
|  | ||||
|       <Route | ||||
|         path={URLS.RESET_PASSWORD} | ||||
|   | ||||
| @@ -95,11 +95,11 @@ export const defaultTheme = createTheme({ | ||||
|       }, | ||||
|     }, | ||||
|     h4: { | ||||
|       fontSize: referenceTheme.typography.pxToRem(32), | ||||
|       fontSize: referenceTheme.typography.pxToRem(28), | ||||
|       lineHeight: 1.3, | ||||
|       fontWeight: 700, | ||||
|       [referenceTheme.breakpoints.down('sm')]: { | ||||
|         fontSize: referenceTheme.typography.pxToRem(16), | ||||
|         fontSize: referenceTheme.typography.pxToRem(22), | ||||
|       }, | ||||
|     }, | ||||
|     h5: { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user