diff --git a/src/lib/server-impl.ts b/src/lib/server-impl.ts index fdd52a34cc..aaa4f425db 100644 --- a/src/lib/server-impl.ts +++ b/src/lib/server-impl.ts @@ -23,6 +23,7 @@ import Controller from './routes/controller'; import { IAuthRequest } from './routes/unleash-types'; import * as permissions from './types/permissions'; import * as eventType from './types/events'; +import { RoleName } from './types/model'; async function createApp( config: IUnleashConfig, @@ -155,6 +156,7 @@ export { AuthenticationRequired, User, LogLevel, + RoleName, }; export default { diff --git a/src/lib/services/access-service.ts b/src/lib/services/access-service.ts index 177f56d898..64ff4c3f8d 100644 --- a/src/lib/services/access-service.ts +++ b/src/lib/services/access-service.ts @@ -286,7 +286,7 @@ export class AccessService { return this.store.getRootRoles(); } - private async resolveRootRole(rootRole: number | RoleName): Promise { + public async resolveRootRole(rootRole: number | RoleName): Promise { const rootRoles = await this.getRootRoles(); let role: IRole; if (typeof rootRole === 'number') { diff --git a/src/lib/services/user-service.ts b/src/lib/services/user-service.ts index 5c8dcbd42a..ea1e9539f4 100644 --- a/src/lib/services/user-service.ts +++ b/src/lib/services/user-service.ts @@ -39,6 +39,13 @@ export interface IUpdateUser { rootRole?: number | RoleName; } +export interface ILoginUserRequest { + email: string; + name?: string; + rootRole?: number | RoleName; + autoCreate?: boolean; +} + interface IUserWithRole extends IUser { rootRole: number; } @@ -260,18 +267,30 @@ class UserService { email: string, autoCreateUser: boolean = false, ): Promise { + return this.loginUserSSO({ email, autoCreate: autoCreateUser }); + } + + async loginUserSSO({ + email, + name, + rootRole, + autoCreate = false, + }: ILoginUserRequest): Promise { let user: IUser; try { user = await this.store.getByQuery({ email }); + // Update user if autCreate is enabled. + if (user.name !== name) { + user = await this.store.update(user.id, { name, email }); + } } catch (e) { - if (autoCreateUser) { - const defaultRole = await this.accessService.getRootRole( - RoleName.EDITOR, - ); + // User does not exists. Create if "autoCreate" is enabled + if (autoCreate) { user = await this.createUser({ email, - rootRole: defaultRole.id, + name, + rootRole: rootRole || RoleName.EDITOR, }); } else { throw e; diff --git a/src/test/e2e/services/user-service.e2e.test.ts b/src/test/e2e/services/user-service.e2e.test.ts index 96ab48039c..026166281c 100644 --- a/src/test/e2e/services/user-service.e2e.test.ts +++ b/src/test/e2e/services/user-service.e2e.test.ts @@ -16,6 +16,7 @@ let stores; let userService: UserService; let userStore: UserStore; let adminRole: IRole; +let viewerRole: IRole; let sessionService: SessionService; beforeAll(async () => { @@ -36,6 +37,7 @@ beforeAll(async () => { userStore = stores.userStore; const rootRoles = await accessService.getRootRoles(); adminRole = rootRoles.find((r) => r.name === RoleName.ADMIN); + viewerRole = rootRoles.find((r) => r.name === RoleName.VIEWER); }); afterAll(async () => { @@ -141,3 +143,93 @@ test("deleting a user should delete the user's sessions", async () => { sessionService.getSessionsForUser(user.id), ).rejects.toThrow(NotFoundError); }); + +test('should login and create user via SSO', async () => { + const email = 'some@test.com'; + const user = await userService.loginUserSSO({ + email, + rootRole: RoleName.VIEWER, + name: 'some', + autoCreate: true, + }); + + const userWithRole = await userService.getUser(user.id); + expect(user.email).toBe(email); + expect(user.name).toBe('some'); + expect(userWithRole.name).toBe('some'); + expect(userWithRole.rootRole).toBe(viewerRole.id); +}); + +test('should throw if rootRole is wrong via SSO', async () => { + expect.assertions(1); + + try { + await userService.loginUserSSO({ + email: 'some@test.com', + rootRole: RoleName.MEMBER, + name: 'some', + autoCreate: true, + }); + } catch (e) { + expect(e.message).toBe('Could not find rootRole=Member'); + } +}); + +test('should update user name when signing in via SSO', async () => { + const email = 'some@test.com'; + const originalUser = await userService.createUser({ + email, + rootRole: RoleName.VIEWER, + name: 'some', + }); + + await userService.loginUserSSO({ + email, + rootRole: RoleName.ADMIN, + name: 'New name!', + autoCreate: true, + }); + + const actualUser = await userService.getUser(originalUser.id); + + expect(actualUser.email).toBe(email); + expect(actualUser.name).toBe('New name!'); + expect(actualUser.rootRole).toBe(viewerRole.id); +}); + +test('should update name if it is different via SSO', async () => { + const email = 'some@test.com'; + const originalUser = await userService.createUser({ + email, + rootRole: RoleName.VIEWER, + name: 'some', + }); + + await userService.loginUserSSO({ + email, + rootRole: RoleName.ADMIN, + name: 'New name!', + autoCreate: false, + }); + + const actualUser = await userService.getUser(originalUser.id); + + expect(actualUser.email).toBe(email); + expect(actualUser.name).toBe('New name!'); + expect(actualUser.rootRole).toBe(viewerRole.id); +}); + +test('should throw if autoCreate is false via SSO', async () => { + expect.assertions(1); + + try { + await userService.loginUserSSO({ + email: 'some@test.com', + rootRole: RoleName.MEMBER, + name: 'some', + autoCreate: false, + }); + } catch (e) { + expect(e.message).toBe('No user found'); + } +});