mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-04 00:18:40 +01:00
feat: list all root roles in SSO config (#5887)
Lists all root roles in SSO config, including custom root roles. 
This commit is contained in:
parent
9d839299e2
commit
0ba37e8622
@ -3,40 +3,49 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Grid,
|
Grid,
|
||||||
InputLabel,
|
|
||||||
MenuItem,
|
|
||||||
Select,
|
|
||||||
Switch,
|
Switch,
|
||||||
TextField,
|
TextField,
|
||||||
SelectChangeEvent,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
import { RoleSelect } from 'component/common/RoleSelect/RoleSelect';
|
||||||
|
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
|
||||||
|
import { IRole } from 'interfaces/role';
|
||||||
|
|
||||||
interface IAutoCreateFormProps {
|
interface IAutoCreateFormProps {
|
||||||
data?: {
|
data?: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
autoCreate: boolean;
|
autoCreate: boolean;
|
||||||
defaultRootRole?: string;
|
defaultRootRole?: string;
|
||||||
|
defaultRootRoleId?: number;
|
||||||
emailDomains?: string;
|
emailDomains?: string;
|
||||||
};
|
};
|
||||||
setValue: (name: string, value: string | boolean) => void;
|
setValue: (
|
||||||
|
name: string,
|
||||||
|
value: string | boolean | number | undefined,
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AutoCreateForm = ({
|
export const AutoCreateForm = ({
|
||||||
data = { enabled: false, autoCreate: false },
|
data = { enabled: false, autoCreate: false },
|
||||||
setValue,
|
setValue,
|
||||||
}: IAutoCreateFormProps) => {
|
}: IAutoCreateFormProps) => {
|
||||||
|
const { roles } = useRoles();
|
||||||
|
|
||||||
const updateAutoCreate = () => {
|
const updateAutoCreate = () => {
|
||||||
setValue('autoCreate', !data.autoCreate);
|
setValue('autoCreate', !data.autoCreate);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDefaultRootRole = (evt: SelectChangeEvent) => {
|
const updateDefaultRootRoleId = (role: IRole | null) => {
|
||||||
setValue('defaultRootRole', evt.target.value);
|
setValue('defaultRootRoleId', role?.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateField = (e: ChangeEvent<HTMLInputElement>) => {
|
const updateField = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
setValue(e.target.name, e.target.value);
|
setValue(e.target.name, e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const roleIdToRole = (rootRoleId: number | undefined): IRole | null => {
|
||||||
|
return roles.find((role: IRole) => role.id === rootRoleId) || null;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Grid container spacing={3} mb={2}>
|
<Grid container spacing={3} mb={2}>
|
||||||
@ -69,24 +78,15 @@ export const AutoCreateForm = ({
|
|||||||
</p>
|
</p>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item md={6}>
|
<Grid item md={6}>
|
||||||
<FormControl style={{ minWidth: '200px' }}>
|
<FormControl style={{ width: '400px' }}>
|
||||||
<InputLabel id='defaultRootRole-label'>
|
<RoleSelect
|
||||||
Default Role
|
roles={roles}
|
||||||
</InputLabel>
|
value={roleIdToRole(data.defaultRootRoleId)}
|
||||||
<Select
|
setValue={updateDefaultRootRoleId}
|
||||||
label='Default Role'
|
|
||||||
labelId='defaultRootRole-label'
|
|
||||||
id='defaultRootRole'
|
|
||||||
name='defaultRootRole'
|
|
||||||
disabled={!data.autoCreate || !data.enabled}
|
disabled={!data.autoCreate || !data.enabled}
|
||||||
value={data.defaultRootRole || 'Editor'}
|
required
|
||||||
onChange={updateDefaultRootRole}
|
hideDescription
|
||||||
>
|
/>
|
||||||
{/*consider these from API or constants. */}
|
|
||||||
<MenuItem value='Viewer'>Viewer</MenuItem>
|
|
||||||
<MenuItem value='Editor'>Editor</MenuItem>
|
|
||||||
<MenuItem value='Admin'>Admin</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -60,7 +60,10 @@ export const OidcAuth = () => {
|
|||||||
setData({ ...data, enableSingleSignOut: !data.enableSingleSignOut });
|
setData({ ...data, enableSingleSignOut: !data.enableSingleSignOut });
|
||||||
};
|
};
|
||||||
|
|
||||||
const setValue = (name: string, value: string | boolean) => {
|
const setValue = (
|
||||||
|
name: string,
|
||||||
|
value: string | boolean | number | undefined,
|
||||||
|
) => {
|
||||||
setData({
|
setData({
|
||||||
...data,
|
...data,
|
||||||
[name]: value,
|
[name]: value,
|
||||||
|
@ -51,7 +51,10 @@ export const SamlAuth = () => {
|
|||||||
setData({ ...data, enabled: !data.enabled });
|
setData({ ...data, enabled: !data.enabled });
|
||||||
};
|
};
|
||||||
|
|
||||||
const setValue = (name: string, value: string | boolean) => {
|
const setValue = (
|
||||||
|
name: string,
|
||||||
|
value: string | boolean | number | undefined,
|
||||||
|
) => {
|
||||||
setData({
|
setData({
|
||||||
...data,
|
...data,
|
||||||
[name]: value,
|
[name]: value,
|
||||||
|
@ -23,6 +23,7 @@ interface IRoleSelectProps
|
|||||||
value: IRole | null;
|
value: IRole | null;
|
||||||
setValue: (role: IRole | null) => void;
|
setValue: (role: IRole | null) => void;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
|
hideDescription?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RoleSelect = ({
|
export const RoleSelect = ({
|
||||||
@ -30,6 +31,7 @@ export const RoleSelect = ({
|
|||||||
value,
|
value,
|
||||||
setValue,
|
setValue,
|
||||||
required,
|
required,
|
||||||
|
hideDescription,
|
||||||
...rest
|
...rest
|
||||||
}: IRoleSelectProps) => {
|
}: IRoleSelectProps) => {
|
||||||
const renderRoleOption = (
|
const renderRoleOption = (
|
||||||
@ -60,7 +62,7 @@ export const RoleSelect = ({
|
|||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(value)}
|
condition={Boolean(value) && !hideDescription}
|
||||||
show={() => (
|
show={() => (
|
||||||
<RoleDescription sx={{ marginTop: 1 }} roleId={value!.id} />
|
<RoleDescription sx={{ marginTop: 1 }} roleId={value!.id} />
|
||||||
)}
|
)}
|
||||||
|
@ -12,12 +12,13 @@ import {
|
|||||||
IUserRole,
|
IUserRole,
|
||||||
IUserWithProjectRoles,
|
IUserWithProjectRoles,
|
||||||
} from '../types/stores/access-store';
|
} from '../types/stores/access-store';
|
||||||
import { IPermission, IUserAccessOverview, RoleType } from '../types/model';
|
import { IPermission, IUserAccessOverview } from '../types/model';
|
||||||
import NotFoundError from '../error/notfound-error';
|
import NotFoundError from '../error/notfound-error';
|
||||||
import {
|
import {
|
||||||
ENVIRONMENT_PERMISSION_TYPE,
|
ENVIRONMENT_PERMISSION_TYPE,
|
||||||
PROJECT_ROLE_TYPES,
|
PROJECT_ROLE_TYPES,
|
||||||
ROOT_PERMISSION_TYPE,
|
ROOT_PERMISSION_TYPE,
|
||||||
|
ROOT_ROLE_TYPES,
|
||||||
} from '../util/constants';
|
} from '../util/constants';
|
||||||
import { Db } from './db';
|
import { Db } from './db';
|
||||||
import {
|
import {
|
||||||
@ -407,8 +408,8 @@ export class AccessStore implements IAccessStore {
|
|||||||
.select(['id', 'name', 'type', 'description'])
|
.select(['id', 'name', 'type', 'description'])
|
||||||
.from<IRole[]>(T.ROLES)
|
.from<IRole[]>(T.ROLES)
|
||||||
.innerJoin(`${T.ROLE_USER} as ru`, 'ru.role_id', 'id')
|
.innerJoin(`${T.ROLE_USER} as ru`, 'ru.role_id', 'id')
|
||||||
.where('ru.user_id', '=', userId)
|
.whereIn('type', ROOT_ROLE_TYPES)
|
||||||
.andWhere('type', '=', RoleType.ROOT)
|
.andWhere('ru.user_id', '=', userId)
|
||||||
.first();
|
.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +18,14 @@ import { BadDataError } from '../../../lib/error';
|
|||||||
import PasswordMismatch from '../../../lib/error/password-mismatch';
|
import PasswordMismatch from '../../../lib/error/password-mismatch';
|
||||||
import { EventService } from '../../../lib/services';
|
import { EventService } from '../../../lib/services';
|
||||||
import {
|
import {
|
||||||
|
CREATE_ADDON,
|
||||||
IUnleashStores,
|
IUnleashStores,
|
||||||
IUserStore,
|
IUserStore,
|
||||||
USER_CREATED,
|
USER_CREATED,
|
||||||
USER_DELETED,
|
USER_DELETED,
|
||||||
USER_UPDATED,
|
USER_UPDATED,
|
||||||
} from '../../../lib/types';
|
} from '../../../lib/types';
|
||||||
|
import { CUSTOM_ROOT_ROLE_TYPE } from '../../../lib/util';
|
||||||
|
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
@ -31,9 +33,11 @@ let userService: UserService;
|
|||||||
let userStore: IUserStore;
|
let userStore: IUserStore;
|
||||||
let adminRole: IRole;
|
let adminRole: IRole;
|
||||||
let viewerRole: IRole;
|
let viewerRole: IRole;
|
||||||
|
let customRole: IRole;
|
||||||
let sessionService: SessionService;
|
let sessionService: SessionService;
|
||||||
let settingService: SettingService;
|
let settingService: SettingService;
|
||||||
let eventService: EventService;
|
let eventService: EventService;
|
||||||
|
let accessService: AccessService;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('user_service_serial', getLogger);
|
db = await dbInit('user_service_serial', getLogger);
|
||||||
@ -41,7 +45,7 @@ beforeAll(async () => {
|
|||||||
const config = createTestConfig();
|
const config = createTestConfig();
|
||||||
eventService = new EventService(stores, config);
|
eventService = new EventService(stores, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
const accessService = new AccessService(
|
accessService = new AccessService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
groupService,
|
groupService,
|
||||||
@ -64,6 +68,17 @@ beforeAll(async () => {
|
|||||||
const rootRoles = await accessService.getRootRoles();
|
const rootRoles = await accessService.getRootRoles();
|
||||||
adminRole = rootRoles.find((r) => r.name === RoleName.ADMIN)!;
|
adminRole = rootRoles.find((r) => r.name === RoleName.ADMIN)!;
|
||||||
viewerRole = rootRoles.find((r) => r.name === RoleName.VIEWER)!;
|
viewerRole = rootRoles.find((r) => r.name === RoleName.VIEWER)!;
|
||||||
|
customRole = await accessService.createRole({
|
||||||
|
name: 'Custom role',
|
||||||
|
type: CUSTOM_ROOT_ROLE_TYPE,
|
||||||
|
description: 'A custom role',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
name: CREATE_ADDON,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
createdByUserId: 1,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -401,3 +416,41 @@ test('should throw if autoCreate is false via SSO', async () => {
|
|||||||
}),
|
}),
|
||||||
).rejects.toThrow(new NotFoundError('No user found'));
|
).rejects.toThrow(new NotFoundError('No user found'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should support a root role id when logging in and creating user via SSO', async () => {
|
||||||
|
const name = 'root-role-id';
|
||||||
|
const email = `${name}@test.com`;
|
||||||
|
const user = await userService.loginUserSSO({
|
||||||
|
email,
|
||||||
|
rootRole: viewerRole.id,
|
||||||
|
name,
|
||||||
|
autoCreate: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const userWithRole = await userService.getUser(user.id);
|
||||||
|
expect(user.email).toBe(email);
|
||||||
|
expect(user.name).toBe(name);
|
||||||
|
expect(userWithRole.name).toBe(name);
|
||||||
|
expect(userWithRole.rootRole).toBe(viewerRole.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support a custom root role id when logging in and creating user via SSO', async () => {
|
||||||
|
const name = 'custom-root-role-id';
|
||||||
|
const email = `${name}@test.com`;
|
||||||
|
const user = await userService.loginUserSSO({
|
||||||
|
email,
|
||||||
|
rootRole: customRole.id,
|
||||||
|
name,
|
||||||
|
autoCreate: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const userWithRole = await userService.getUser(user.id);
|
||||||
|
expect(user.email).toBe(email);
|
||||||
|
expect(user.name).toBe(name);
|
||||||
|
expect(userWithRole.name).toBe(name);
|
||||||
|
expect(userWithRole.rootRole).toBe(customRole.id);
|
||||||
|
|
||||||
|
const permissions = await accessService.getPermissionsForUser(user);
|
||||||
|
expect(permissions).toHaveLength(1);
|
||||||
|
expect(permissions[0].permission).toBe(CREATE_ADDON);
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user