Skip to content

Commit

Permalink
BC-3991 - Exchange AuthorizationReferenceService with AuthorizationCl…
Browse files Browse the repository at this point in the history
…ientAdapter (#5154)
  • Loading branch information
bischofmax authored Aug 5, 2024
1 parent 736c083 commit 4925cd3
Show file tree
Hide file tree
Showing 30 changed files with 399 additions and 362 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export const AuthorizationBodyParamsReferenceType = {
SUBMISSIONS: 'submissions',
SCHOOL_EXTERNAL_TOOLS: 'school-external-tools',
BOARDNODES: 'boardnodes',
CONTEXT_EXTERNAL_TOOLS: 'context-external-tools'
CONTEXT_EXTERNAL_TOOLS: 'context-external-tools',
EXTERNAL_TOOLS: 'external-tools',
INSTANCES: 'instances'
} as const;

export type AuthorizationBodyParamsReferenceType = typeof AuthorizationBodyParamsReferenceType[keyof typeof AuthorizationBodyParamsReferenceType];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
* This is v3 of Schulcloud-Verbund-Software Server. Checkout /docs for v1.
*
* The version of the OpenAPI document: 3.0
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/


// May contain unused imports in some cases
// @ts-ignore
import type { Action } from './action';
Expand All @@ -21,24 +20,21 @@ import type { Action } from './action';
import type { Permission } from './permission';

/**
*
*
* @export
* @interface AuthorizationContextParams
*/
export interface AuthorizationContextParams {
/**
*
* @type {Action}
* @memberof AuthorizationContextParams
*/
'action': Action;
/**
* User permissions that are needed to execute the operation.
* @type {Array<Permission>}
* @memberof AuthorizationContextParams
*/
'requiredPermissions': Array<Permission>;
/**
*
* @type {Action}
* @memberof AuthorizationContextParams
*/
action: Action;
/**
* User permissions that are needed to execute the operation.
* @type {Array<Permission>}
* @memberof AuthorizationContextParams
*/
requiredPermissions: Array<Permission>;
}



Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedOptions = { headers: { authorization: `Bearer ${jwtToken}` } };

await service.checkPermissionsByReference(params);
await service.checkPermissionsByReference(params.referenceType, params.referenceId, params.context);

expect(authorizationApi.authorizationReferenceControllerAuthorizeByReference).toHaveBeenCalledWith(
params,
Expand All @@ -95,7 +95,9 @@ describe(AuthorizationClientAdapter.name, () => {
it('should resolve', async () => {
const { params } = setup({ isAuthorized: true });

await expect(service.checkPermissionsByReference(params)).resolves.toBeUndefined();
await expect(
service.checkPermissionsByReference(params.referenceType, params.referenceId, params.context)
).resolves.toBeUndefined();
});
});

Expand All @@ -105,7 +107,9 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedError = new AuthorizationForbiddenLoggableException(params);

await expect(service.checkPermissionsByReference(params)).rejects.toThrowError(expectedError);
await expect(
service.checkPermissionsByReference(params.referenceType, params.referenceId, params.context)
).rejects.toThrowError(expectedError);
});
});
});
Expand All @@ -132,7 +136,9 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedError = new AuthorizationErrorLoggableException(error, params);

await expect(service.checkPermissionsByReference(params)).rejects.toThrowError(expectedError);
await expect(
service.checkPermissionsByReference(params.referenceType, params.referenceId, params.context)
).rejects.toThrowError(expectedError);
});
});
});
Expand Down Expand Up @@ -165,7 +171,7 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedOptions = { headers: { authorization: `Bearer ${jwtToken}` } };

await service.hasPermissionsByReference(params);
await service.hasPermissionsByReference(params.referenceType, params.referenceId, params.context);

expect(authorizationApi.authorizationReferenceControllerAuthorizeByReference).toHaveBeenCalledWith(
params,
Expand All @@ -176,7 +182,11 @@ describe(AuthorizationClientAdapter.name, () => {
it('should return isAuthorized', async () => {
const { params, response } = setup();

const result = await service.hasPermissionsByReference(params);
const result = await service.hasPermissionsByReference(
params.referenceType,
params.referenceId,
params.context
);

expect(result).toEqual(response.data.isAuthorized);
});
Expand Down Expand Up @@ -216,7 +226,7 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedOptions = { headers: { authorization: `Bearer ${jwtToken}` } };

await adapter.hasPermissionsByReference(params);
await adapter.hasPermissionsByReference(params.referenceType, params.referenceId, params.context);

expect(authorizationApi.authorizationReferenceControllerAuthorizeByReference).toHaveBeenCalledWith(
params,
Expand Down Expand Up @@ -259,7 +269,7 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedOptions = { headers: { authorization: `Bearer ${jwtToken}` } };

await adapter.hasPermissionsByReference(params);
await adapter.hasPermissionsByReference(params.referenceType, params.referenceId, params.context);

expect(authorizationApi.authorizationReferenceControllerAuthorizeByReference).toHaveBeenCalledWith(
params,
Expand Down Expand Up @@ -294,7 +304,9 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedError = new AuthorizationErrorLoggableException(error, params);

await expect(adapter.hasPermissionsByReference(params)).rejects.toThrowError(expectedError);
await expect(
adapter.hasPermissionsByReference(params.referenceType, params.referenceId, params.context)
).rejects.toThrowError(expectedError);
});
});

Expand All @@ -320,7 +332,9 @@ describe(AuthorizationClientAdapter.name, () => {

const expectedError = new AuthorizationErrorLoggableException(error, params);

await expect(service.hasPermissionsByReference(params)).rejects.toThrowError(expectedError);
await expect(
service.hasPermissionsByReference(params.referenceType, params.referenceId, params.context)
).rejects.toThrowError(expectedError);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@
import { Inject, Injectable } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { extractJwtFromHeader } from '@shared/common';
import { RawAxiosRequestConfig } from 'axios';
import { Request } from 'express';
import { extractJwtFromHeader } from '@shared/common';
import { AuthorizationApi, AuthorizationBodyParams } from './authorization-api-client';
import {
AuthorizationApi,
AuthorizationBodyParamsReferenceType,
AuthorizationContextParams,
} from './authorization-api-client';
import { AuthorizationErrorLoggableException, AuthorizationForbiddenLoggableException } from './error';

@Injectable()
export class AuthorizationClientAdapter {
constructor(private readonly authorizationApi: AuthorizationApi, @Inject(REQUEST) private request: Request) {}

public async checkPermissionsByReference(params: AuthorizationBodyParams): Promise<void> {
const hasPermission = await this.hasPermissionsByReference(params);
public async checkPermissionsByReference(
referenceType: AuthorizationBodyParamsReferenceType,
referenceId: string,
context: AuthorizationContextParams
): Promise<void> {
const hasPermission = await this.hasPermissionsByReference(referenceType, referenceId, context);

if (!hasPermission) {
throw new AuthorizationForbiddenLoggableException(params);
throw new AuthorizationForbiddenLoggableException({ referenceType, referenceId, context });
}
}

public async hasPermissionsByReference(params: AuthorizationBodyParams): Promise<boolean> {
public async hasPermissionsByReference(
referenceType: AuthorizationBodyParamsReferenceType,
referenceId: string,
context: AuthorizationContextParams
): Promise<boolean> {
const params = {
referenceType,
referenceId,
context,
};

try {
const options = this.createOptionParams();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AuthorizationApi, Configuration, ConfigurationParameters } from './auth
import { AuthorizationClientAdapter } from './authorization-client.adapter';

export interface AuthorizationClientConfig extends ConfigurationParameters {
basePath?: string;
basePath: string;
}

@Module({})
Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/infra/authorization-client/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { Action, AuthorizationBodyParamsReferenceType, AuthorizationContextParams } from './authorization-api-client';
export { AuthorizationClientAdapter } from './authorization-client.adapter';
export { AuthorizationClientModule } from './authorization-client.module';
export { AuthorizationClientConfig, AuthorizationClientModule } from './authorization-client.module';
export { AuthorizationContextBuilder } from './mapper';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Permission } from '@shared/domain/interface';
import { Action, AuthorizationContextParams } from '../authorization-api-client';

export class AuthorizationContextBuilder {
static build(requiredPermissions: Array<Permission>, action: Action): AuthorizationContextParams {
return {
action,
requiredPermissions,
};
}

static write(requiredPermissions: Permission[]): AuthorizationContextParams {
const context = this.build(requiredPermissions, Action.WRITE);

return context;
}

static read(requiredPermissions: Permission[]): AuthorizationContextParams {
const context = this.build(requiredPermissions, Action.READ);

return context;
}
}
1 change: 1 addition & 0 deletions apps/server/src/infra/authorization-client/mapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './authorization-context.builder';
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMock } from '@golevelup/ts-jest';
import { AntivirusService } from '@infra/antivirus';
import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { S3ClientAdapter } from '@infra/s3-client';
import { EntityManager } from '@mikro-orm/mongodb';
import { ICurrentUser } from '@modules/authentication';
Expand Down Expand Up @@ -105,6 +106,8 @@ describe(`${baseRouteName} (api)`, () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ExecutionContext, INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { ApiValidationError } from '@shared/common';

import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { EntityId } from '@shared/domain/types';
import {
cleanupCollections,
Expand Down Expand Up @@ -106,6 +107,8 @@ describe(`${baseRouteName} (api)`, () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { AntivirusService } from '@infra/antivirus';
import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { S3ClientAdapter } from '@infra/s3-client';
import { EntityManager } from '@mikro-orm/mongodb';
import { ICurrentUser } from '@modules/authentication';
Expand Down Expand Up @@ -124,6 +125,8 @@ describe('files-storage controller (API)', () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createMock } from '@golevelup/ts-jest';
import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { EntityManager } from '@mikro-orm/mongodb';
import { ICurrentUser } from '@modules/authentication';
import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard';
Expand Down Expand Up @@ -66,6 +67,8 @@ describe(`${baseRouteName} (api)`, () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { AntivirusService } from '@infra/antivirus';
import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { PreviewProducer } from '@infra/preview-generator';
import { S3ClientAdapter } from '@infra/s3-client';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
Expand Down Expand Up @@ -137,6 +138,8 @@ describe('File Controller (API) - preview', () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createMock } from '@golevelup/ts-jest';
import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { EntityManager } from '@mikro-orm/mongodb';
import { ICurrentUser } from '@modules/authentication';
import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard';
Expand Down Expand Up @@ -65,6 +66,8 @@ describe(`${baseRouteName} (api)`, () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMock } from '@golevelup/ts-jest';
import { AntivirusService } from '@infra/antivirus';
import { AuthorizationClientAdapter } from '@infra/authorization-client';
import { S3ClientAdapter } from '@infra/s3-client';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { ICurrentUser } from '@modules/authentication';
Expand Down Expand Up @@ -132,6 +133,8 @@ describe(`${baseRouteName} (api)`, () => {
})
.overrideProvider(NodeClam)
.useValue(createMock<NodeClam>())
.overrideProvider(AuthorizationClientAdapter)
.useValue(createMock<AuthorizationClientAdapter>())
.compile();

app = module.createNestApplication();
Expand Down
Loading

0 comments on commit 4925cd3

Please sign in to comment.