diff --git a/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx b/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx
index 03bbbb14e6..73ff46fa9d 100644
--- a/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx
+++ b/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx
@@ -3,40 +3,49 @@ import {
FormControl,
FormControlLabel,
Grid,
- InputLabel,
- MenuItem,
- Select,
Switch,
TextField,
- SelectChangeEvent,
} 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 {
data?: {
enabled: boolean;
autoCreate: boolean;
defaultRootRole?: string;
+ defaultRootRoleId?: number;
emailDomains?: string;
};
- setValue: (name: string, value: string | boolean) => void;
+ setValue: (
+ name: string,
+ value: string | boolean | number | undefined,
+ ) => void;
}
export const AutoCreateForm = ({
data = { enabled: false, autoCreate: false },
setValue,
}: IAutoCreateFormProps) => {
+ const { roles } = useRoles();
+
const updateAutoCreate = () => {
setValue('autoCreate', !data.autoCreate);
};
- const updateDefaultRootRole = (evt: SelectChangeEvent) => {
- setValue('defaultRootRole', evt.target.value);
+ const updateDefaultRootRoleId = (role: IRole | null) => {
+ setValue('defaultRootRoleId', role?.id);
};
const updateField = (e: ChangeEvent) => {
setValue(e.target.name, e.target.value);
};
+ const roleIdToRole = (rootRoleId: number | undefined): IRole | null => {
+ return roles.find((role: IRole) => role.id === rootRoleId) || null;
+ };
+
return (
@@ -69,24 +78,15 @@ export const AutoCreateForm = ({
-
-
- Default Role
-
-
+ required
+ hideDescription
+ />
diff --git a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx
index 93b08dd2d0..c7f0044200 100644
--- a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx
+++ b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx
@@ -60,7 +60,10 @@ export const OidcAuth = () => {
setData({ ...data, enableSingleSignOut: !data.enableSingleSignOut });
};
- const setValue = (name: string, value: string | boolean) => {
+ const setValue = (
+ name: string,
+ value: string | boolean | number | undefined,
+ ) => {
setData({
...data,
[name]: value,
diff --git a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx
index bc1feed06d..5842e4b18d 100644
--- a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx
+++ b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx
@@ -51,7 +51,10 @@ export const SamlAuth = () => {
setData({ ...data, enabled: !data.enabled });
};
- const setValue = (name: string, value: string | boolean) => {
+ const setValue = (
+ name: string,
+ value: string | boolean | number | undefined,
+ ) => {
setData({
...data,
[name]: value,
diff --git a/frontend/src/component/common/RoleSelect/RoleSelect.tsx b/frontend/src/component/common/RoleSelect/RoleSelect.tsx
index 5fada87da8..42f851579b 100644
--- a/frontend/src/component/common/RoleSelect/RoleSelect.tsx
+++ b/frontend/src/component/common/RoleSelect/RoleSelect.tsx
@@ -23,6 +23,7 @@ interface IRoleSelectProps
value: IRole | null;
setValue: (role: IRole | null) => void;
required?: boolean;
+ hideDescription?: boolean;
}
export const RoleSelect = ({
@@ -30,6 +31,7 @@ export const RoleSelect = ({
value,
setValue,
required,
+ hideDescription,
...rest
}: IRoleSelectProps) => {
const renderRoleOption = (
@@ -60,7 +62,7 @@ export const RoleSelect = ({
{...rest}
/>
(
)}
diff --git a/src/lib/db/access-store.ts b/src/lib/db/access-store.ts
index 5c9a6d13ad..1c6f9202d5 100644
--- a/src/lib/db/access-store.ts
+++ b/src/lib/db/access-store.ts
@@ -12,12 +12,13 @@ import {
IUserRole,
IUserWithProjectRoles,
} 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 {
ENVIRONMENT_PERMISSION_TYPE,
PROJECT_ROLE_TYPES,
ROOT_PERMISSION_TYPE,
+ ROOT_ROLE_TYPES,
} from '../util/constants';
import { Db } from './db';
import {
@@ -407,8 +408,8 @@ export class AccessStore implements IAccessStore {
.select(['id', 'name', 'type', 'description'])
.from(T.ROLES)
.innerJoin(`${T.ROLE_USER} as ru`, 'ru.role_id', 'id')
- .where('ru.user_id', '=', userId)
- .andWhere('type', '=', RoleType.ROOT)
+ .whereIn('type', ROOT_ROLE_TYPES)
+ .andWhere('ru.user_id', '=', userId)
.first();
}
diff --git a/src/test/e2e/services/user-service.e2e.test.ts b/src/test/e2e/services/user-service.e2e.test.ts
index dd7f370f9e..a7562b7213 100644
--- a/src/test/e2e/services/user-service.e2e.test.ts
+++ b/src/test/e2e/services/user-service.e2e.test.ts
@@ -18,12 +18,14 @@ import { BadDataError } from '../../../lib/error';
import PasswordMismatch from '../../../lib/error/password-mismatch';
import { EventService } from '../../../lib/services';
import {
+ CREATE_ADDON,
IUnleashStores,
IUserStore,
USER_CREATED,
USER_DELETED,
USER_UPDATED,
} from '../../../lib/types';
+import { CUSTOM_ROOT_ROLE_TYPE } from '../../../lib/util';
let db: ITestDb;
let stores: IUnleashStores;
@@ -31,9 +33,11 @@ let userService: UserService;
let userStore: IUserStore;
let adminRole: IRole;
let viewerRole: IRole;
+let customRole: IRole;
let sessionService: SessionService;
let settingService: SettingService;
let eventService: EventService;
+let accessService: AccessService;
beforeAll(async () => {
db = await dbInit('user_service_serial', getLogger);
@@ -41,7 +45,7 @@ beforeAll(async () => {
const config = createTestConfig();
eventService = new EventService(stores, config);
const groupService = new GroupService(stores, config, eventService);
- const accessService = new AccessService(
+ accessService = new AccessService(
stores,
config,
groupService,
@@ -64,6 +68,17 @@ beforeAll(async () => {
const rootRoles = await accessService.getRootRoles();
adminRole = rootRoles.find((r) => r.name === RoleName.ADMIN)!;
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 () => {
@@ -401,3 +416,41 @@ test('should throw if autoCreate is false via SSO', async () => {
}),
).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);
+});