From 3e60913de057a792c67693443f90d81055c9274a Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Mon, 8 Aug 2022 13:16:51 +0200 Subject: [PATCH 1/2] feat: initialize the PermissionService --- src/model/enum/PermissionCollectionType.ts | 5 + src/model/enum/PermissionType.ts | 3 + src/model/security/ClassPermission.ts | 19 + src/model/security/GroupClassPermission.ts | 16 + src/model/security/GroupInstancePermission.ts | 16 + src/model/security/InstancePermission.ts | 19 + src/model/security/PermissionCollection.ts | 20 + src/model/security/UserClassPermission.ts | 16 + src/model/security/UserInstancePermission.ts | 16 + src/service/GenericService/index.ts | 10 +- src/service/PermissionService/index.spec.ts | 807 ++++++++++++++++++ src/service/PermissionService/index.ts | 462 ++++++++++ 12 files changed, 1407 insertions(+), 2 deletions(-) create mode 100644 src/model/enum/PermissionCollectionType.ts create mode 100644 src/model/enum/PermissionType.ts create mode 100644 src/model/security/ClassPermission.ts create mode 100644 src/model/security/GroupClassPermission.ts create mode 100644 src/model/security/GroupInstancePermission.ts create mode 100644 src/model/security/InstancePermission.ts create mode 100644 src/model/security/PermissionCollection.ts create mode 100644 src/model/security/UserClassPermission.ts create mode 100644 src/model/security/UserInstancePermission.ts create mode 100644 src/service/PermissionService/index.spec.ts create mode 100644 src/service/PermissionService/index.ts diff --git a/src/model/enum/PermissionCollectionType.ts b/src/model/enum/PermissionCollectionType.ts new file mode 100644 index 000000000..90a99daa5 --- /dev/null +++ b/src/model/enum/PermissionCollectionType.ts @@ -0,0 +1,5 @@ +export type PermissionCollectionType = 'CREATE' | 'READ' | 'UPDATE' | 'DELETE' | 'CREATE_READ' | 'CREATE_UPDATE' | + 'CREATE_DELETE' | 'READ_UPDATE' | 'READ_DELETE' | 'UPDATE_DELETE' | 'CREATE_READ_UPDATE' | 'CREATE_READ_DELETE' | + 'CREATE_UPDATE_DELETE' | 'READ_UPDATE_DELETE' | 'ADMIN'; + +export default PermissionCollectionType; diff --git a/src/model/enum/PermissionType.ts b/src/model/enum/PermissionType.ts new file mode 100644 index 000000000..be2b31939 --- /dev/null +++ b/src/model/enum/PermissionType.ts @@ -0,0 +1,3 @@ +export type PermissionType = 'ADMIN' | 'CREATE' | 'DELETE' | 'UPDATE' | 'READ'; + +export default PermissionType; diff --git a/src/model/security/ClassPermission.ts b/src/model/security/ClassPermission.ts new file mode 100644 index 000000000..6ca2d8f32 --- /dev/null +++ b/src/model/security/ClassPermission.ts @@ -0,0 +1,19 @@ +import BaseEntity, { BaseEntityArgs } from '../BaseEntity'; +import PermissionCollection from './PermissionCollection'; + +export interface ClassPermissionArgs extends BaseEntityArgs { + className: string; + permission: PermissionCollection; +} + +export default class ClassPermission extends BaseEntity { + className: string; + permission: PermissionCollection; + + constructor({ id, created, modified, className, permission }: ClassPermissionArgs) { + super({ id, created, modified }); + + this.className = className; + this.permission = permission; + } +} diff --git a/src/model/security/GroupClassPermission.ts b/src/model/security/GroupClassPermission.ts new file mode 100644 index 000000000..380d133cd --- /dev/null +++ b/src/model/security/GroupClassPermission.ts @@ -0,0 +1,16 @@ +import Group from '../Group'; +import ClassPermission, { ClassPermissionArgs } from './ClassPermission'; + +export interface GroupClassPermissionArgs extends ClassPermissionArgs { + group: Group; +} + +export default class GroupClassPermission extends ClassPermission { + group: Group; + + constructor({ id, created, modified, className, permission, group }: GroupClassPermissionArgs) { + super({ id, created, modified, className, permission }); + + this.group = group; + } +} diff --git a/src/model/security/GroupInstancePermission.ts b/src/model/security/GroupInstancePermission.ts new file mode 100644 index 000000000..a76da62de --- /dev/null +++ b/src/model/security/GroupInstancePermission.ts @@ -0,0 +1,16 @@ +import Group from '../Group'; +import InstancePermission, { InstancePermissionArgs } from './InstancePermission'; + +export interface GroupInstancePermissionArgs extends InstancePermissionArgs { + group: Group; +} + +export default class GroupInstancePermission extends InstancePermission { + group: Group; + + constructor({ id, created, modified, entityId, permission, group }: GroupInstancePermissionArgs) { + super({ id, created, modified, entityId, permission }); + + this.group = group; + } +} diff --git a/src/model/security/InstancePermission.ts b/src/model/security/InstancePermission.ts new file mode 100644 index 000000000..97925287c --- /dev/null +++ b/src/model/security/InstancePermission.ts @@ -0,0 +1,19 @@ +import BaseEntity, { BaseEntityArgs } from '../BaseEntity'; +import PermissionCollection from './PermissionCollection'; + +export interface InstancePermissionArgs extends BaseEntityArgs { + entityId: number; + permission: PermissionCollection; +} + +export default class InstancePermission extends BaseEntity { + entityId: number; + permission: PermissionCollection; + + constructor({ id, created, modified, entityId, permission }: InstancePermissionArgs) { + super({ id, created, modified }); + + this.entityId = entityId; + this.permission = permission; + } +} diff --git a/src/model/security/PermissionCollection.ts b/src/model/security/PermissionCollection.ts new file mode 100644 index 000000000..ff4756e16 --- /dev/null +++ b/src/model/security/PermissionCollection.ts @@ -0,0 +1,20 @@ +import BaseEntity, { BaseEntityArgs } from '../BaseEntity'; +import PermissionCollectionType from '../enum/PermissionCollectionType'; +import PermissionType from '../enum/PermissionType'; + +export interface PermissionCollectionArgs extends BaseEntityArgs { + permissions: PermissionType[]; + name: PermissionCollectionType; +} + +export default class PermissionCollection extends BaseEntity { + permissions: PermissionType[]; + name: PermissionCollectionType; + + constructor({ id, created, modified, permissions, name }: PermissionCollectionArgs) { + super({ id, created, modified }); + + this.permissions = permissions; + this.name = name; + } +} diff --git a/src/model/security/UserClassPermission.ts b/src/model/security/UserClassPermission.ts new file mode 100644 index 000000000..b42b19c06 --- /dev/null +++ b/src/model/security/UserClassPermission.ts @@ -0,0 +1,16 @@ +import User from '../User'; +import ClassPermission, { ClassPermissionArgs } from './ClassPermission'; + +export interface UserClassPermissionArgs extends ClassPermissionArgs { + user: User; +} + +export default class UserClassPermission extends ClassPermission { + user: User; + + constructor({ id, created, modified, className, permission, user }: UserClassPermissionArgs) { + super({ id, created, modified, className, permission }); + + this.user = user; + } +} diff --git a/src/model/security/UserInstancePermission.ts b/src/model/security/UserInstancePermission.ts new file mode 100644 index 000000000..61fb0d701 --- /dev/null +++ b/src/model/security/UserInstancePermission.ts @@ -0,0 +1,16 @@ +import User from '../User'; +import InstancePermission, { InstancePermissionArgs } from './InstancePermission'; + +export interface UserInstancePermissionArgs extends InstancePermissionArgs { + user: User; +} + +export default class UserInstancePermission extends InstancePermission { + user: User; + + constructor({ id, created, modified, entityId, permission, user }: UserInstancePermissionArgs) { + super({ id, created, modified, entityId, permission }); + + this.user = user; + } +} diff --git a/src/service/GenericService/index.ts b/src/service/GenericService/index.ts index 3d151fc73..bcb4f4397 100644 --- a/src/service/GenericService/index.ts +++ b/src/service/GenericService/index.ts @@ -4,19 +4,25 @@ import { getBearerTokenHeader } from '../../security/getBearerTokenHeader'; import { getCsrfTokenHeader } from '../../security/getCsrfTokenHeader'; import BaseEntity from '../../model/BaseEntity'; +import PermissionService from '../PermissionService'; export interface GenericServiceOpts { basePath: string; keycloak?: Keycloak; }; -export abstract class GenericService { +export abstract class GenericService extends PermissionService { basePath: string; keycloak?: Keycloak; constructor(opts: GenericServiceOpts) { + super({ + basePath: opts.basePath, + keycloak: opts.keycloak + }); + this.basePath = opts.basePath; this.keycloak = opts.keycloak; } @@ -37,7 +43,7 @@ export abstract class GenericService { const json: T[] = await response.json(); - return json ; + return json; } catch (error) { throw new Error(`Error while requesting all entities: ${error}`); } diff --git a/src/service/PermissionService/index.spec.ts b/src/service/PermissionService/index.spec.ts new file mode 100644 index 000000000..04c7227e6 --- /dev/null +++ b/src/service/PermissionService/index.spec.ts @@ -0,0 +1,807 @@ +import Keycloak from 'keycloak-js'; + +import PermissionService from '.'; +import GroupClassPermission from '../../model/security/GroupClassPermission'; +import GroupInstancePermission from '../../model/security/GroupInstancePermission'; +import UserClassPermission from '../../model/security/UserClassPermission'; +import UserInstancePermission from '../../model/security/UserInstancePermission'; +import fetchSpy, { + failureResponse, + successResponse +} from '../../spec/fetchSpy'; + +describe('PermissionService', () => { + let fetchMock: jest.SpyInstance; + let service: PermissionService; + + beforeEach(() => { + const keycloak: Keycloak = new Keycloak(); + keycloak.token = 'ThisIsNotAValidBearerToken'; + + service = new PermissionService({ + basePath: '', + keycloak + }); + }); + + afterEach(() => { + if (fetchMock) { + fetchMock.mockReset(); + fetchMock.mockRestore(); + } + }); + + it('is defined', () => { + expect(PermissionService).toBeDefined(); + }); + + it('sends all required parameters to return all user instance ' + + 'permissions for an entity (getUserInstancePermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getUserInstancePermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/user', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns all user instance permissions for an entity (getUserInstancePermissions)', async () => { + const response: UserInstancePermission[] = [{ + id: 1, + created: new Date(), + modified: new Date(), + entityId: 1909, + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + user: { + id: 20, + created: new Date(), + modified: new Date() + } + }, { + id: 2, + created: new Date(), + modified: new Date(), + entityId: 1909, + permission: { + id: 11, + created: new Date(), + modified: new Date(), + name: 'CREATE', + permissions: [ + 'CREATE' + ] + }, + user: { + id: 30, + created: new Date(), + modified: new Date() + } + }]; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getUserInstancePermissions(1909); + + expect(resp).toEqual(response); + }); + + it('throws an error if the user instance permissions for an entity ' + + 'couldn\'t be fetched (getUserInstancePermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getUserInstancePermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to return all group instance ' + + 'permissions for an entity (getGroupInstancePermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getGroupInstancePermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/group', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns all group instance permissions for an entity (getGroupInstancePermissions)', async () => { + const response: GroupInstancePermission[] = [{ + id: 1, + created: new Date(), + modified: new Date(), + entityId: 1909, + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + group: { + id: 20, + created: new Date(), + modified: new Date() + } + }, { + id: 2, + created: new Date(), + modified: new Date(), + entityId: 1909, + permission: { + id: 11, + created: new Date(), + modified: new Date(), + name: 'CREATE', + permissions: [ + 'CREATE' + ] + }, + group: { + id: 30, + created: new Date(), + modified: new Date() + } + }]; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getGroupInstancePermissions(1909); + + expect(resp).toEqual(response); + }); + + it('throws an error if the group instance permissions for an entity ' + + 'couldn\'t be fetched (getGroupInstancePermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getGroupInstancePermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to return all user class ' + + 'permissions for an entity (getUserClassPermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getUserClassPermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/user', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns all user class permissions for an entity (getUserClassPermissions)', async () => { + const response: UserClassPermission[] = [{ + id: 1, + created: new Date(), + modified: new Date(), + className: 'Application', + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + user: { + id: 20, + created: new Date(), + modified: new Date() + } + }]; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getUserClassPermissions(1909); + + expect(resp).toEqual(response); + }); + + it('throws an error if the user class permissions for an entity ' + + 'couldn\'t be fetched (getUserClassPermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getUserClassPermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to return all group class ' + + 'permissions for an entity (getGroupClassPermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getGroupClassPermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/group', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns all group class permissions for an entity (getGroupClassPermissions)', async () => { + const response: GroupClassPermission[] = [{ + id: 1, + created: new Date(), + modified: new Date(), + className: 'Application', + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + group: { + id: 20, + created: new Date(), + modified: new Date() + } + }]; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getGroupClassPermissions(1909); + + expect(resp).toEqual(response); + }); + + it('throws an error if the group class permissions for an entity ' + + 'couldn\'t be fetched (getGroupClassPermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getGroupClassPermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to return the user instance ' + + 'permission for an entity (getUserInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getUserInstancePermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/user/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns the user instance permission for an entity (getUserInstancePermission)', async () => { + const response: UserInstancePermission = { + id: 1, + created: new Date(), + modified: new Date(), + entityId: 1909, + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + user: { + id: 20, + created: new Date(), + modified: new Date() + } + }; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getUserInstancePermission(1909, 10); + + expect(resp).toEqual(response); + }); + + it('throws an error if the user instance permission for an entity ' + + 'couldn\'t be fetched (getUserInstancePermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getUserInstancePermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to return the group instance ' + + 'permission for an entity (getGroupInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getGroupInstancePermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/group/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns the group instance permission for an entity (getGroupInstancePermission)', async () => { + const response: GroupInstancePermission = { + id: 1, + created: new Date(), + modified: new Date(), + entityId: 1909, + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + group: { + id: 20, + created: new Date(), + modified: new Date() + } + }; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getGroupInstancePermission(1909, 10); + + expect(resp).toEqual(response); + }); + + it('throws an error if the group instance permission for an entity ' + + 'couldn\'t be fetched (getGroupInstancePermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getGroupInstancePermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to return the user class ' + + 'permission for an entity (getUserClassPermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getUserClassPermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/user/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns the user class permission for an entity (getUserClassPermission)', async () => { + const response: UserClassPermission = { + id: 1, + created: new Date(), + modified: new Date(), + className: 'Application', + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + user: { + id: 20, + created: new Date(), + modified: new Date() + } + }; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getUserClassPermission(1909, 10); + + expect(resp).toEqual(response); + }); + + it('throws an error if the user class permission for an entity ' + + 'couldn\'t be fetched (getUserClassPermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getUserClassPermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to return the group class ' + + 'permission for an entity (getGroupClassPermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.getGroupClassPermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/group/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'GET' + }); + }); + + it('returns the group class permission for an entity (getGroupClassPermission)', async () => { + const response: GroupClassPermission = { + id: 1, + created: new Date(), + modified: new Date(), + className: 'Application', + permission: { + id: 10, + created: new Date(), + modified: new Date(), + name: 'ADMIN', + permissions: [ + 'ADMIN' + ] + }, + group: { + id: 20, + created: new Date(), + modified: new Date() + } + }; + + fetchMock = fetchSpy(successResponse(response)); + + const resp = await service.getGroupClassPermission(1909, 20); + + expect(resp).toEqual(response); + }); + + it('throws an error if the group class permission for an entity ' + + 'couldn\'t be fetched (getGroupClassPermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.getGroupClassPermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to set the user instance ' + + 'permission for an entity (setUserInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.setUserInstancePermission(1909, 10, 'ADMIN'); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/user/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken', + 'Content-Type': 'application/json' + }, + body: '{\"permission\":\"ADMIN\"}', + method: 'POST' + }); + }); + + it('sets the user instance permission for an entity (setUserInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.setUserInstancePermission(1909, 10, 'ADMIN')).resolves.not.toThrow(); + }); + + it('throws an error if the user instance permission for an entity ' + + 'couldn\'t be set (setUserInstancePermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.setUserInstancePermission(1909, 10, 'ADMIN')).rejects.toThrow(); + }); + + it('sends all required parameters to set the group instance ' + + 'permission for an entity (setGroupInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.setGroupInstancePermission(1909, 10, 'ADMIN'); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/group/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken', + 'Content-Type': 'application/json' + }, + body: '{\"permission\":\"ADMIN\"}', + method: 'POST' + }); + }); + + it('sets the group instance permission for an entity (setGroupInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.setGroupInstancePermission(1909, 10, 'ADMIN')).resolves.not.toThrow(); + }); + + it('throws an error if the group instance permission for an entity ' + + 'couldn\'t be set (setGroupInstancePermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.setGroupInstancePermission(1909, 10, 'ADMIN')).rejects.toThrow(); + }); + + it('sends all required parameters to set the user class ' + + 'permission for an entity (setUserClassPermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.setUserClassPermission(1909, 10, 'ADMIN'); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/user/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken', + 'Content-Type': 'application/json' + }, + body: '{\"permission\":\"ADMIN\"}', + method: 'POST' + }); + }); + + it('sets the user class permission for an entity (setUserClassPermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.setUserClassPermission(1909, 10, 'ADMIN')).resolves.not.toThrow(); + }); + + it('throws an error if the user class permission for an entity ' + + 'couldn\'t be set (setUserClassPermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.setUserClassPermission(1909, 10, 'ADMIN')).rejects.toThrow(); + }); + + it('sends all required parameters to set the group class ' + + 'permission for an entity (setGroupClassPermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.setGroupClassPermission(1909, 10, 'ADMIN'); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/group/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken', + 'Content-Type': 'application/json' + }, + body: '{\"permission\":\"ADMIN\"}', + method: 'POST' + }); + }); + + it('sets the group class permission for an entity (setGroupClassPermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.setGroupClassPermission(1909, 10, 'ADMIN')).resolves.not.toThrow(); + }); + + it('throws an error if the group class permission for an entity ' + + 'couldn\'t be set (setGroupClassPermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.setGroupClassPermission(1909, 10, 'ADMIN')).rejects.toThrow(); + }); + + it('sends all required parameters to delete the user instance ' + + 'permission for an entity (deleteUserInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteUserInstancePermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/user/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes the user instance permission for an entity (deleteUserInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteUserInstancePermission(1909, 10)).resolves.not.toThrow(); + }); + + it('throws an error if the user instance permission for an entity ' + + 'couldn\'t be deleted (deleteUserInstancePermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteUserInstancePermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to delete the group instance ' + + 'permission for an entity (deleteGroupInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteGroupInstancePermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/group/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes the group instance permission for an entity (deleteGroupInstancePermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteGroupInstancePermission(1909, 10)).resolves.not.toThrow(); + }); + + it('throws an error if the group instance permission for an entity ' + + 'couldn\'t be deleted (deleteGroupInstancePermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteGroupInstancePermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to delete the user class ' + + 'permission for an entity (deleteUserClassPermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteUserClassPermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/user/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes the user class permission for an entity (deleteUserClassPermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteUserClassPermission(1909, 10)).resolves.not.toThrow(); + }); + + it('throws an error if the user class permission for an entity ' + + 'couldn\'t be deleted (deleteUserClassPermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteUserClassPermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to delete the group class ' + + 'permission for an entity (deleteGroupClassPermission)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteGroupClassPermission(1909, 10); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/group/10', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes the group class permission for an entity (deleteGroupClassPermission)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteGroupClassPermission(1909, 10)).resolves.not.toThrow(); + }); + + it('throws an error if the group class permission for an entity ' + + 'couldn\'t be deleted (deleteGroupClassPermission)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteGroupClassPermission(1909, 10)).rejects.toThrow(); + }); + + it('sends all required parameters to delete all user instance ' + + 'permissions for an entity (deleteUserInstancePermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteUserInstancePermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/user', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes all user instance permissions for an entity (deleteUserInstancePermissions)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteUserInstancePermissions(1909)).resolves.not.toThrow(); + }); + + it('throws an error if the user instance permissions for an entity ' + + 'couldn\'t be deleted (deleteUserInstancePermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteUserInstancePermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to delete all group instance ' + + 'permissions for an entity (deleteGroupInstancePermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteGroupInstancePermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/instance/group', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes all group instance permissions for an entity (deleteGroupInstancePermissions)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteGroupInstancePermissions(1909)).resolves.not.toThrow(); + }); + + it('throws an error if the group instance permissions for an entity ' + + 'couldn\'t be deleted (deleteGroupInstancePermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteGroupInstancePermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to delete all user class ' + + 'permissions for an entity (deleteUserClassPermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteUserClassPermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/user', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes all user class permissions for an entity (deleteUserClassPermissions)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteUserClassPermissions(1909)).resolves.not.toThrow(); + }); + + it('throws an error if the user class permissions for an entity ' + + 'couldn\'t be deleted (deleteUserClassPermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteUserClassPermissions(1909)).rejects.toThrow(); + }); + + it('sends all required parameters to delete all group class ' + + 'permissions for an entity (deleteGroupClassPermissions)', async () => { + fetchMock = fetchSpy(successResponse([])); + + await service.deleteGroupClassPermissions(1909); + + expect(fetchMock).toHaveBeenCalledWith('/1909/permissions/class/group', { + headers: { + 'Authorization': 'Bearer ThisIsNotAValidBearerToken' + }, + method: 'DELETE' + }); + }); + + it('deletes all group class permissions for an entity (deleteGroupClassPermissions)', async () => { + fetchMock = fetchSpy(successResponse(null)); + + await expect(service.deleteGroupClassPermissions(1909)).resolves.not.toThrow(); + }); + + it('throws an error if the group class permissions for an entity ' + + 'couldn\'t be deleted (deleteGroupClassPermissions)', async () => { + fetchMock = fetchSpy(failureResponse()); + + await expect(service.deleteGroupClassPermissions(1909)).rejects.toThrow(); + }); +}); diff --git a/src/service/PermissionService/index.ts b/src/service/PermissionService/index.ts new file mode 100644 index 000000000..26c24cac7 --- /dev/null +++ b/src/service/PermissionService/index.ts @@ -0,0 +1,462 @@ +import Keycloak from 'keycloak-js'; + +import { getBearerTokenHeader } from '../../security/getBearerTokenHeader'; +import { getCsrfTokenHeader } from '../../security/getCsrfTokenHeader'; + +import UserInstancePermission from '../../model/security/UserInstancePermission'; +import GroupInstancePermission from '../../model/security/GroupInstancePermission'; +import UserClassPermission from '../../model/security/UserClassPermission'; +import GroupClassPermission from '../../model/security/GroupClassPermission'; +import PermissionCollectionType from '../../model/enum/PermissionCollectionType'; + +export interface PermissionServiceOpts { + basePath: string; + keycloak?: Keycloak; +}; + +export class PermissionService { + + basePath: string; + + keycloak?: Keycloak; + + constructor(opts: PermissionServiceOpts) { + this.basePath = opts.basePath; + this.keycloak = opts.keycloak; + } + + async getUserInstancePermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/user`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: UserInstancePermission[] = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the user instance permissions: ${error}`); + } + } + + async getGroupInstancePermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/group`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: GroupInstancePermission[] = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the group instance permissions: ${error}`); + } + } + + async getUserClassPermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/user`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: UserClassPermission[] = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the user class permissions: ${error}`); + } + } + + async getGroupClassPermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/group`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: GroupClassPermission[] = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the group class permissions: ${error}`); + } + } + + async getUserInstancePermission(id: string | number, userId: string | number, fetchOpts?: RequestInit): + Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/user/${userId}`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: UserInstancePermission = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the user instance permission: ${error}`); + } + } + + async getGroupInstancePermission(id: string | number, groupId: string | number, fetchOpts?: RequestInit): + Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/group/${groupId}`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: UserInstancePermission = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the group instance permission: ${error}`); + } + } + + async getUserClassPermission(id: string | number, userId: string | number, fetchOpts?: RequestInit): + Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/user/${userId}`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: UserInstancePermission = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the user class permission: ${error}`); + } + } + + async getGroupClassPermission(id: string | number, groupId: string | number, fetchOpts?: RequestInit): + Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/group/${groupId}`, { + method: 'GET', + headers: { + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + + const json: UserInstancePermission = await response.json(); + + return json; + } catch (error) { + throw new Error(`Error while requesting the group class permission: ${error}`); + } + } + + async setUserInstancePermission(id: string | number, userId: string | number, + permissionType: PermissionCollectionType, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/user/${userId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + body: JSON.stringify({ + permission: permissionType + }), + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while adding the user instance permission: ${error}`); + } + } + + async setGroupInstancePermission(id: string | number, groupId: string | number, + permissionType: PermissionCollectionType, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/group/${groupId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + body: JSON.stringify({ + permission: permissionType + }), + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while adding the group instance permission: ${error}`); + } + } + + async setUserClassPermission(id: string | number, userId: string | number, + permissionType: PermissionCollectionType, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/user/${userId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + body: JSON.stringify({ + permission: permissionType + }), + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while adding the user class permission: ${error}`); + } + } + + async setGroupClassPermission(id: string | number, groupId: string | number, + permissionType: PermissionCollectionType, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/group/${groupId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + body: JSON.stringify({ + permission: permissionType + }), + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while adding the group class permission: ${error}`); + } + } + + async deleteUserInstancePermission(id: string | number, userId: string | number, + fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/user/${userId}`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing the user instance permission: ${error}`); + } + } + + async deleteGroupInstancePermission(id: string | number, groupId: string | number, + fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/group/${groupId}`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing the group instance permission: ${error}`); + } + } + + async deleteUserClassPermission(id: string | number, userId: string | number, + fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/user/${userId}`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing the user class permission: ${error}`); + } + } + + async deleteGroupClassPermission(id: string | number, groupId: string | number, + fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/group/${groupId}`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing the group class permission: ${error}`); + } + } + + async deleteUserInstancePermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/user`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing all user instance permissions: ${error}`); + } + } + + async deleteGroupInstancePermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/instance/group`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing all group instance permissions: ${error}`); + } + } + + async deleteUserClassPermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/user`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing all user class permissions: ${error}`); + } + } + + async deleteGroupClassPermissions(id: string | number, fetchOpts?: RequestInit): Promise { + try { + const response = await fetch(`${this.basePath}/${id}/permissions/class/group`, { + method: 'DELETE', + headers: { + ...getCsrfTokenHeader(), + ...getBearerTokenHeader(this.keycloak) + }, + ...fetchOpts + }); + + if (!response.ok) { + throw new Error(`HTTP error status: ${response.status}`); + } + } catch (error) { + throw new Error(`Error while removing all group class permissions: ${error}`); + } + } + +} + +export default PermissionService; From f6d3c6747e38a40baa75d93426e82ba1aab7b940 Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Mon, 8 Aug 2022 13:17:22 +0200 Subject: [PATCH 2/2] fix: provide a default type for the group provider details --- src/model/Group.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/Group.ts b/src/model/Group.ts index f517a6ae7..cee32f543 100644 --- a/src/model/Group.ts +++ b/src/model/Group.ts @@ -19,12 +19,12 @@ export interface KeycloakGroupRepresentation extends ProviderGroupDetails { }; } -export interface GroupArgs extends BaseEntityArgs { +export interface GroupArgs extends BaseEntityArgs { authProviderId?: string; providerDetails?: T; } -export default class Group extends BaseEntity { +export default class Group extends BaseEntity { authProviderId?: string; providerDetails?: T;