diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.model.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.model.ts index 8f2e2b89cddb4de2e1427c5525652ca6c409d208..4a2a5d7eabcfd524c1846a53c47f4d31d54f75a1 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.model.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.model.ts @@ -3,9 +3,9 @@ export const KeycloakDefaults = { realmManagement: { clientName: 'realm-management', roles: { - managerUsers: { - roleName: 'manage-users', - }, + managerUsers: { roleName: 'manage-users' }, + queryGroups: { roleName: 'query-groups' }, + viewClients: { roleName: 'view-clients' }, }, }, }, diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.spec.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.spec.ts index 1f0d1042ab81dbde8b55d9a67a21fd31f1881ba1..c0ef6b387b57946efc4336573809b55582b7912e 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.spec.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.spec.ts @@ -21,12 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { RoleMappings, User } from '@admin-client/user-shared'; +import { AdminRoles, RoleMappings, User } from '@admin-client/user-shared'; import { UserRepository } from '@admin/keycloak-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { faker } from '@faker-js/faker'; +import { expect } from '@jest/globals'; import KcAdminClient from '@keycloak/keycloak-admin-client'; import GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation'; import MappingsRepresentation from '@keycloak/keycloak-admin-client/lib/defs/mappingsRepresentation'; @@ -274,20 +275,6 @@ describe('UserRepository', () => { expect(repository._updateUserRolesForClient).toHaveBeenCalledWith(user.id, [UserFormService.POSTSTELLE], clientId); }); - - it('should update user roles for realm-management client', async () => { - await repository._updateUserRoles(user.id, { - realmManagement: [KeycloakDefaults.clients.realmManagement.roles.managerUsers.roleName], - admin: [], - alfa: [], - }); - - expect(repository._updateUserRolesForClient).toHaveBeenCalledWith( - user.id, - [KeycloakDefaults.clients.realmManagement.roles.managerUsers.roleName], - clientId, - ); - }); }); describe('updateUserRolesForClient', () => { @@ -546,6 +533,50 @@ describe('UserRepository', () => { }); }); + describe('update user roles for admin', () => { + const clientId: string = faker.string.uuid(); + + beforeEach(() => { + repository._updateUserRolesForClient = jest.fn(); + repository._getClientId = jest.fn().mockReturnValue(new Promise((resolve) => resolve(clientId))); + }); + + it('should get client id', async () => { + await repository._updateUserRolesForAdmin(user.id, { + admin: [AdminRoles.ADMIN], + alfa: [], + }); + + expect(repository._getClientId).toHaveBeenCalledWith(KeycloakDefaults.clients.realmManagement.clientName); + }); + + it('should update user roles', async () => { + await repository._updateUserRolesForAdmin(user.id, { + admin: [AdminRoles.ADMIN], + alfa: [], + }); + + expect(repository._updateUserRolesForClient).toHaveBeenCalledWith( + user.id, + [ + KeycloakDefaults.clients.realmManagement.roles.managerUsers.roleName, + KeycloakDefaults.clients.realmManagement.roles.queryGroups.roleName, + KeycloakDefaults.clients.realmManagement.roles.viewClients.roleName, + ], + clientId, + ); + }); + + it('should NOT update user roles for no ADMIN_ADMIN role', async () => { + await repository._updateUserRolesForAdmin(user.id, { + admin: [faker.word.noun()], + alfa: [], + }); + + expect(repository._updateUserRolesForClient).not.toHaveBeenCalled(); + }); + }); + describe('sendActivationMail', () => { it('should call kcAdminClient users executeActionsEmail', () => { const userId: string = faker.string.uuid(); diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.ts index 3830c6e430e7cd34c60bc6ccdbcf7e2b6df15182..c510b595f5b6157f20093328d23fa31bcc11e242 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/user.repository.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ClientMapping, ClientRoles, RoleMappings, User } from '@admin-client/user-shared'; +import { AdminRoles, ClientMapping, ClientRoles, RoleMappings, User } from '@admin-client/user-shared'; import { createStateResource, StateResource } from '@alfa-client/tech-shared'; import { inject, Injectable } from '@angular/core'; import KcAdminClient from '@keycloak/keycloak-admin-client'; @@ -105,11 +105,7 @@ export class UserRepository { async _updateUserRoles(userId: string, clientRoles: ClientRoles): Promise<void> { await this._updateUserRolesForClient(userId, clientRoles.admin, await this._getClientId(UserRepository.ADMIN_CLIENT_NAME)); await this._updateUserRolesForClient(userId, clientRoles.alfa, await this._getClientId(UserRepository.ALFA_CLIENT_NAME)); - await this._updateUserRolesForClient( - userId, - clientRoles.realmManagement, - await this._getClientId(KeycloakDefaults.clients.realmManagement.clientName), - ); + await this._updateUserRolesForAdmin(userId, clientRoles); } async _updateUserRolesForClient(userId: string, clientRoles: string[], clientId: string): Promise<void> { @@ -183,6 +179,20 @@ export class UserRepository { }); } + async _updateUserRolesForAdmin(userId: string, clientRoles: ClientRoles): Promise<void> { + if (clientRoles.admin.includes(AdminRoles.ADMIN)) { + await this._updateUserRolesForClient( + userId, + [ + KeycloakDefaults.clients.realmManagement.roles.managerUsers.roleName, + KeycloakDefaults.clients.realmManagement.roles.queryGroups.roleName, + KeycloakDefaults.clients.realmManagement.roles.viewClients.roleName, + ], + await this._getClientId(KeycloakDefaults.clients.realmManagement.clientName), + ); + } + } + _sendActivationMail(userId: string): void { this.kcAdminClient.users.executeActionsEmail({ id: userId, diff --git a/alfa-client/libs/admin/user-shared/src/lib/user.model.ts b/alfa-client/libs/admin/user-shared/src/lib/user.model.ts index 2f2b72c49957f4e232a114dbe8e6277f59be9ddd..45d8e171c2c2b1cde1e96e25d76cac23bcf6e5e2 100644 --- a/alfa-client/libs/admin/user-shared/src/lib/user.model.ts +++ b/alfa-client/libs/admin/user-shared/src/lib/user.model.ts @@ -33,13 +33,11 @@ export interface User { groups: string[]; groupIds?: string[]; clientRoles: ClientRoles; - realmManagement?: string[]; } export interface ClientRoles { alfa: string[]; admin: string[]; - realmManagement?: string[]; } export interface ClientMapping { @@ -50,3 +48,7 @@ export interface RoleMappings { newClientRoleMappings: RoleMappingPayload[]; oldClientRoleMappings: RoleMappingPayload[]; } + +export enum AdminRoles { + ADMIN = 'ADMIN_ADMIN', +} diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts index 1542040a3df53398092639cd5517193ce935a429..99e28aabc946516a2076a89eb850c1df7b5594cd 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts @@ -39,7 +39,6 @@ import { createUser } from 'libs/admin/user-shared/test/user'; import { Observable, of, throwError } from 'rxjs'; import { createUrlSegment } from '../../../../../navigation-shared/test/navigation-test-factory'; import { singleCold, singleColdCompleted, singleHot } from '../../../../../tech-shared/test/marbles'; -import { KeycloakDefaults } from '../../../../keycloak-shared/src/lib/keycloak.model'; import { createAdminOrganisationsEinheit } from '../../../../organisations-einheit-shared/src/test/organisations-einheit'; import { UserFormService } from './user.formservice'; import SpyInstance = jest.SpyInstance; @@ -504,90 +503,4 @@ describe('UserFormService', () => { expect(userName).toBe('userNameDummy'); }); }); - - describe('map administration role to realm-management role', () => { - it('should map ADMIN role', () => { - expect(formService._mapAdministrationRoleToManagementRealmRole(UserFormService.ADMIN)).toEqual( - KeycloakDefaults.clients.realmManagement.roles.managerUsers.roleName, - ); - }); - - it('should throw error', () => { - expect(() => formService._mapAdministrationRoleToManagementRealmRole(UserFormService.USER)).toThrow(); - }); - }); - - describe('can map administration role to realm-management role', () => { - it('should return true for ADMIN', () => { - expect(formService._canMapAdministrationRoleToManagementRealmRole(UserFormService.ADMIN)).toBe(true); - }); - - it('should return false', () => { - expect(formService._canMapAdministrationRoleToManagementRealmRole(UserFormService.USER)).toBe(false); - }); - }); - - describe('map administration group roles to realm-management roles', () => { - beforeEach(() => { - formService._canMapAdministrationRoleToManagementRealmRole = jest.fn(); - formService._mapAdministrationRoleToManagementRealmRole = jest.fn(); - }); - - it('should check if can map', () => { - formService._mapAdministrationRolesToManagementRealmRoles([UserFormService.ADMIN]); - - expect(formService._canMapAdministrationRoleToManagementRealmRole).toHaveBeenCalledWith(UserFormService.ADMIN); - }); - - it('should map', () => { - formService._canMapAdministrationRoleToManagementRealmRole = jest.fn().mockReturnValue(true); - - formService._mapAdministrationRolesToManagementRealmRoles([UserFormService.ADMIN]); - - expect(formService._mapAdministrationRoleToManagementRealmRole).toHaveBeenCalledWith(UserFormService.ADMIN); - }); - - it('should NOT map', () => { - formService._canMapAdministrationRoleToManagementRealmRole = jest.fn().mockReturnValue(false); - - formService._mapAdministrationRolesToManagementRealmRoles([UserFormService.ADMIN]); - - expect(formService._mapAdministrationRoleToManagementRealmRole).not.toHaveBeenCalled(); - }); - - it('should return mapping result', () => { - const mappingResult: string = faker.word.noun(); - formService._canMapAdministrationRoleToManagementRealmRole = jest.fn().mockReturnValue(true); - formService._mapAdministrationRoleToManagementRealmRole = jest.fn().mockReturnValue(mappingResult); - - const realmManagementRoles: string[] = formService._mapAdministrationRolesToManagementRealmRoles([UserFormService.ADMIN]); - - expect(realmManagementRoles).toEqual([mappingResult]); - }); - }); - - describe('create user', () => { - beforeEach(() => { - formService._getRoles = jest.fn(); - formService._getFormValue = jest.fn(); - formService._mapAdministrationRolesToManagementRealmRoles = jest.fn(); - formService._getActiveOrganisationsEinheiten = jest.fn(); - }); - - it('should map administration roles to realm-management roles', () => { - formService._getRoles = jest.fn().mockReturnValue([UserFormService.ADMIN]); - - formService._createUser(); - - expect(formService._mapAdministrationRolesToManagementRealmRoles).toHaveBeenCalledWith([UserFormService.ADMIN]); - }); - - it('should set realmManagement clientRoles', () => { - formService._mapAdministrationRolesToManagementRealmRoles = jest.fn().mockReturnValue([UserFormService.ADMIN]); - - const user: User = formService._createUser(); - - expect(user.clientRoles.realmManagement).toEqual([UserFormService.ADMIN]); - }); - }); }); diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts index f4c623a7f40063464739bf814c4cd874377f302b..5fce973d6369a67c26e25706b9c28ebeb5d6fbe4 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts @@ -23,7 +23,7 @@ */ import { AdminOrganisationsEinheit, AdminOrganisationsEinheitService } from '@admin-client/organisations-einheit-shared'; import { ROUTES } from '@admin-client/shared'; -import { User, UserService } from '@admin-client/user-shared'; +import { AdminRoles, User, UserService } from '@admin-client/user-shared'; import { KeycloakFormService, PatchConfig } from '@admin/keycloak-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; import { createEmptyStateResource, EMPTY_STRING, isLoaded, mapToResource, StateResource } from '@alfa-client/tech-shared'; @@ -32,7 +32,6 @@ import { Injectable, OnDestroy } from '@angular/core'; import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormGroup, ValidationErrors, ValidatorFn, Validators, } from '@angular/forms'; import { UrlSegment } from '@angular/router'; import { catchError, filter, Observable, of, Subscription, tap } from 'rxjs'; -import { KeycloakDefaults } from '../../../../keycloak-shared/src/lib/keycloak.model'; @Injectable() export class UserFormService extends KeycloakFormService<User> implements OnDestroy { @@ -44,7 +43,7 @@ export class UserFormService extends KeycloakFormService<User> implements OnDest public static readonly CLIENT_ROLES: string = 'clientRoles'; public static readonly GROUPS: string = 'groups'; public static readonly ADMINISTRATION_GROUP: string = 'admin'; - public static readonly ADMIN: string = 'ADMIN_ADMIN'; + public static readonly ADMIN: string = AdminRoles.ADMIN; public static readonly DATENBEAUFTRAGUNG: string = 'DATENBEAUFTRAGUNG'; public static readonly ALFA_GROUP: string = 'alfa'; public static readonly LOESCHEN: string = 'VERWALTUNG_LOESCHEN'; @@ -234,7 +233,6 @@ export class UserFormService extends KeycloakFormService<User> implements OnDest clientRoles: { alfa: this._getRoles(UserFormService.ALFA_GROUP), admin: administrationRoles, - realmManagement: this._mapAdministrationRolesToManagementRealmRoles(administrationRoles), }, groups: this._getActiveOrganisationsEinheiten(), }; @@ -244,30 +242,6 @@ export class UserFormService extends KeycloakFormService<User> implements OnDest return this.getActive(this.getRoleGroup(roleGroup)); } - _mapAdministrationRolesToManagementRealmRoles(administrationRoles: string[]): string[] { - return administrationRoles - .filter((role: string) => this._canMapAdministrationRoleToManagementRealmRole(role)) - .map((role: string) => this._mapAdministrationRoleToManagementRealmRole(role)); - } - - _canMapAdministrationRoleToManagementRealmRole(administrationRole: string): boolean { - switch (administrationRole) { - case UserFormService.ADMIN: - return true; - default: - return false; - } - } - - _mapAdministrationRoleToManagementRealmRole(administrationRole: string): string { - switch (administrationRole) { - case UserFormService.ADMIN: - return KeycloakDefaults.clients.realmManagement.roles.managerUsers.roleName; - default: - throw new Error(`Cannot map administration role ${administrationRole}. Check before if map is possible.`); - } - } - _getActiveOrganisationsEinheiten(): string[] { return this.getActive(this.getOrganisationsEinheitenGroup()); }