Skip to content

Commit

Permalink
Spsh 663 (#543)
Browse files Browse the repository at this point in the history
* create function for KC groups and EventHandler

* fix the role rights for admin user on the local, adding roles and groups correctly but with some error messages

* refactored and got the assignments working

* fixed space issue with role assigment

* moved the event handler to a diferent file

* got the failing tests in unrelated modules working

* got most of the tests covered

* fixed nestjs providers

* added to the database with seeding

* fixed tests

* removed es lint comment

* merge confilics

* fixed the last line tests

* Organize modules

* Use LogginTestModule

* Undo formatting

* Move group/roles into its own service

* Add seeding data to deployment

* Rename gruppe/rolle to keycloakGroup/keycloakRole

* Fix merge conflicts

---------

Co-authored-by: Marvin Rode (Cap) <[email protected]>
Co-authored-by: Marvin Rode <[email protected]>
  • Loading branch information
3 people authored Jun 20, 2024
1 parent 512658a commit c4150b8
Show file tree
Hide file tree
Showing 24 changed files with 711 additions and 46 deletions.
48 changes: 36 additions & 12 deletions charts/dbildungs-iam-server/seeding/dev/03_service-provider.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion config/dev-realm-spsh.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"composites": {
"client": {
"realm-management": [
"manage-users"
"manage-users",
"manage-realm"
]
}
},
Expand Down
48 changes: 36 additions & 12 deletions seeding/dev/01/03_service-provider.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/console/dbseed/domain/db-seed.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { ServiceProvider } from '../../../modules/service-provider/domain/servic
import { GleicheRolleAnKlasseWieSchuleError } from '../../../modules/personenkontext/specification/error/gleiche-rolle-an-klasse-wie-schule.error.js';
import { PersonenkontextFactory } from '../../../modules/personenkontext/domain/personenkontext.factory.js';
import { OrganisationRepository } from '../../../modules/organisation/persistence/organisation.repository.js';
import { KeycloakGroupRoleService } from '../../../modules/keycloak-administration/domain/keycloak-group-role.service.js';

describe('DbSeedService', () => {
let module: TestingModule;
Expand Down Expand Up @@ -95,6 +96,10 @@ describe('DbSeedService', () => {
provide: KeycloakUserService,
useValue: createMock<KeycloakUserService>(),
},
{
provide: KeycloakGroupRoleService,
useValue: createMock<KeycloakGroupRoleService>(),
},
],
}).compile();
dbSeedService = module.get(DbSeedService);
Expand Down
2 changes: 2 additions & 0 deletions src/console/dbseed/domain/db-seed.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ export class DbSeedService {
referencedOrga.id,
file.logoBase64 ? Buffer.from(file.logoBase64, 'base64') : undefined,
file.logoMimeType,
file.keycloakGroup,
file.keycloakRole,
);

const persistedServiceProvider: ServiceProvider<true> =
Expand Down
4 changes: 4 additions & 0 deletions src/console/dbseed/file/service-provider-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ export class ServiceProviderFile {
public logoBase64?: string;

public logoMimeType?: string;

public keycloakGroup?: string;

public keycloakRole?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
import { faker } from '@faker-js/faker';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
import { KeycloakAdminClient } from '@s3pweb/keycloak-admin-client-cjs';

import { ConfigTestModule, LoggingTestModule } from '../../../../test/utils/index.js';
import { DomainError, KeycloakClientError } from '../../../shared/error/index.js';
import { PersonService } from '../../person/domain/person.service.js';
import { KeycloakAdministrationService } from './keycloak-admin-client.service.js';
import { KeycloakGroupRoleService } from './keycloak-group-role.service.js';

describe('KeycloakGroupRoleService', () => {
let module: TestingModule;
let service: KeycloakGroupRoleService;
let adminService: DeepMocked<KeycloakAdministrationService>;
let kcGroupsMock: DeepMocked<KeycloakAdminClient['groups']>;
let kcRolesMock: DeepMocked<KeycloakAdminClient['roles']>;

beforeAll(async () => {
kcGroupsMock = createMock<KeycloakAdminClient['groups']>();
kcRolesMock = createMock<KeycloakAdminClient['roles']>();

module = await Test.createTestingModule({
imports: [ConfigTestModule, LoggingTestModule],
providers: [
KeycloakGroupRoleService,
{
provide: KeycloakAdministrationService,
useValue: createMock<KeycloakAdministrationService>({
getAuthedKcAdminClient() {
return Promise.resolve({
ok: true,
value: createMock<KeycloakAdminClient>({
groups: kcGroupsMock,
roles: kcRolesMock,
}),
});
},
}),
},
{
provide: PersonService,
useValue: createMock<PersonService>(),
},
],
}).compile();
service = module.get(KeycloakGroupRoleService);
adminService = module.get(KeycloakAdministrationService);
});

beforeEach(() => {
jest.restoreAllMocks();
});

afterAll(async () => {
await module.close();
});

it('should be defined', () => {
expect(service).toBeDefined();
});

describe('createGroup', () => {
const groupName: string = faker.internet.userName();
const groupId: string = faker.string.numeric();

describe('when KeycloakAdminClient cannot be obtained', () => {
it('should return an error result', async () => {
adminService.getAuthedKcAdminClient.mockResolvedValueOnce({
ok: false,
error: new KeycloakClientError('Authentication failed'),
});

const result: Result<string, DomainError> = await service.createGroup(groupName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Authentication failed'),
});
});
});

describe('when group already exists', () => {
it('should return an error result', async () => {
kcGroupsMock.find.mockResolvedValueOnce([{ name: groupName }]);

const result: Result<string, DomainError> = await service.createGroup(groupName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Group name already exists'),
});
});
});

describe('when group is successfully created', () => {
it('should return the id of the created group', async () => {
kcGroupsMock.find.mockResolvedValueOnce([]);
kcGroupsMock.create.mockResolvedValueOnce({ id: groupId });

const result: Result<string, DomainError> = await service.createGroup(groupName);

expect(result).toEqual({
ok: true,
value: groupId,
});
});
});

describe('when an error occurs during group creation', () => {
it('should return an error result', async () => {
kcGroupsMock.find.mockResolvedValueOnce([]);
kcGroupsMock.create.mockRejectedValueOnce(new Error('Creation failed'));

const result: Result<string, DomainError> = await service.createGroup(groupName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Could not create group'),
});
});
});
});

describe('createRole', () => {
const roleName: string = faker.internet.userName();

describe('when KeycloakAdminClient cannot be obtained', () => {
it('should return an error result', async () => {
adminService.getAuthedKcAdminClient.mockResolvedValueOnce({
ok: false,
error: new KeycloakClientError('Authentication failed'),
});

const result: Result<string, DomainError> = await service.createRole(roleName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Authentication failed'),
});
});
});

describe('when role already exists', () => {
it('should return an error result', async () => {
kcRolesMock.findOneByName.mockResolvedValueOnce({ name: roleName });

const result: Result<string, DomainError> = await service.createRole(roleName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Role name already exists'),
});
});
});

describe('when role is successfully created', () => {
it('should return the name of the created role', async () => {
kcRolesMock.findOneByName.mockResolvedValueOnce(undefined);
kcRolesMock.create.mockResolvedValueOnce({ roleName });

const result: Result<string, DomainError> = await service.createRole(roleName);

expect(result).toEqual({
ok: true,
value: roleName,
});
});
});

describe('when an error occurs during role creation', () => {
it('should return an error result', async () => {
kcRolesMock.findOneByName.mockResolvedValueOnce(undefined);
kcRolesMock.create.mockRejectedValueOnce(new Error('Creation failed'));

const result: Result<string, DomainError> = await service.createRole(roleName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Could not create role'),
});
});
});
});

describe('addRoleToGroup', () => {
const groupId: string = faker.string.uuid();
const roleName: string = faker.internet.userName();

describe('when KeycloakAdminClient cannot be obtained', () => {
it('should return an error result', async () => {
adminService.getAuthedKcAdminClient.mockResolvedValueOnce({
ok: false,
error: new KeycloakClientError('Authentication failed'),
});

const result: Result<boolean, DomainError> = await service.addRoleToGroup(groupId, roleName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Authentication failed'),
});
});
});

describe('when role does not exist or id/name is undefined', () => {
it('should return an error result', async () => {
adminService.getAuthedKcAdminClient.mockResolvedValueOnce({
ok: true,
value: createMock<KeycloakAdminClient>({
roles: {
findOneByName: jest.fn().mockResolvedValueOnce(undefined),
},
}),
});

const result: Result<boolean, DomainError> = await service.addRoleToGroup(groupId, roleName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Role not found or id/name is undefined'),
});
});
});

describe('when role is successfully added to group', () => {
it('should return true', async () => {
adminService.getAuthedKcAdminClient.mockResolvedValueOnce({
ok: true,
value: createMock<KeycloakAdminClient>({
roles: {
findOneByName: jest.fn().mockResolvedValueOnce({ id: faker.string.uuid(), name: roleName }),
},
groups: {
addRealmRoleMappings: jest.fn().mockResolvedValueOnce(undefined),
},
}),
});

const result: Result<boolean, DomainError> = await service.addRoleToGroup(groupId, roleName);

expect(result).toEqual({
ok: true,
value: true,
});
});
});

describe('when an error occurs during adding role to group', () => {
it('should return an error result', async () => {
adminService.getAuthedKcAdminClient.mockResolvedValueOnce({
ok: true,
value: createMock<KeycloakAdminClient>({
roles: {
findOneByName: jest.fn().mockResolvedValueOnce({ id: faker.string.uuid(), name: roleName }),
},
groups: {
addRealmRoleMappings: jest
.fn()
.mockRejectedValueOnce(new Error('Add role to group failed')),
},
}),
});

const result: Result<boolean, DomainError> = await service.addRoleToGroup(groupId, roleName);

expect(result).toEqual({
ok: false,
error: new KeycloakClientError('Could not add role to group'),
});
});
});
});
});
Loading

0 comments on commit c4150b8

Please sign in to comment.