Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC-3990 add authorisation api #5024

Merged
merged 14 commits into from
May 28, 2024
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication';
import { Body, Controller, InternalServerErrorException, Param, Post, UnauthorizedException } from '@nestjs/common';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiValidationError } from '@shared/common';
import { AuthorizationReferenceUc } from './authorization-reference.uc';
import { AuthorizationBodyParams, AuthorizationUrlParams, AuthorizedReponse } from './dto';

@Authenticate('jwt')
@ApiTags('Authorization')
@Controller('authorization')
export class AuthorizationReferenceController {
constructor(private readonly authorizationReferenceUc: AuthorizationReferenceUc) {}

@ApiResponse({ status: 200, type: AuthorizedReponse })
@ApiResponse({ status: 400, type: ApiValidationError })
@ApiResponse({ status: 401, type: UnauthorizedException })
@ApiResponse({ status: 500, type: InternalServerErrorException })
@Post('authorize-by-reference/referenceType/:referenceType/referenceId/:referenceId')
CeEv marked this conversation as resolved.
Show resolved Hide resolved
public async authorizeByReference(
@Param() urlParams: AuthorizationUrlParams,
@Body() body: AuthorizationBodyParams,
@CurrentUser() user: ICurrentUser
): Promise<AuthorizedReponse> {
const successAuthorizationReponse = await this.authorizationReferenceUc.authorizeByReference(
user.userId,
urlParams.referenceType,
urlParams.referenceId,
body
);

return successAuthorizationReponse;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { EntityId } from '@shared/domain/types';
import { Injectable } from '@nestjs/common';
import { AuthorizableReferenceType, AuthorizationContext, AuthorizationReferenceService } from '../domain';
import { AuthorizedReponse } from './dto';
import { AuthorizationReponseMapper } from './mapper';

@Injectable()
export class AuthorizationReferenceUc {
constructor(private readonly authorizationReferenceService: AuthorizationReferenceService) {}

public async authorizeByReference(
userId: EntityId,
authorizableReferenceType: AuthorizableReferenceType,
authorizableReferenceId: EntityId,
context: AuthorizationContext
): Promise<AuthorizedReponse> {
await this.authorizationReferenceService.checkPermissionByReferences(
userId,
authorizableReferenceType,
authorizableReferenceId,
context
);

const successAuthorizationReponse = AuthorizationReponseMapper.mapToSuccessResponse(
userId,
authorizableReferenceType,
authorizableReferenceId,
context
);

return successAuthorizationReponse;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Permission } from '@shared/domain/interface';
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsEnum } from 'class-validator';
import { Action, AuthorizationContext } from '../../domain';

export class AuthorizationBodyParams implements AuthorizationContext {
@IsEnum(Action)
@ApiProperty({ description: 'Define for which action the operation should be performend.' })
action!: Action;

@IsArray()
@IsEnum(Permission, { each: true })
@ApiProperty({
enum: Permission,
isArray: true,
description: 'Needed user permissions based on user role, that are needed to execute the operation.',
CeEv marked this conversation as resolved.
Show resolved Hide resolved
})
requiredPermissions!: Permission[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsMongoId } from 'class-validator';
import { AuthorizableReferenceType } from '../../domain';

export class AuthorizationUrlParams {
@IsEnum(AuthorizableReferenceType)
@ApiProperty({
enum: AuthorizableReferenceType,
description: 'Define for which known entity, or domain object the operation should be peformend.',
example: AuthorizableReferenceType.User,
})
referenceType!: AuthorizableReferenceType;

@IsMongoId()
@ApiProperty({ description: 'The id of the entity/domain object of the defined referenceType.' })
referenceId!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Permission } from '@shared/domain/interface';
import { ApiProperty } from '@nestjs/swagger';
import { Action, AuthorizableReferenceType, AuthorizationContext } from '../../domain';

export class AuthorizedReponse implements AuthorizationContext {
@ApiProperty({
enum: Permission,
isArray: true,
description: 'Needed user permissions based on user role, that are needed to execute the operation.',
example: [Permission.ACCOUNT_VIEW, Permission.BASE_VIEW],
})
requiredPermissions: Permission[];
CeEv marked this conversation as resolved.
Show resolved Hide resolved

@ApiProperty()
userId: string;

@ApiProperty({
enum: Action,
description: 'Define for which action the operation is performend.',
example: Action.read,
})
action: Action;

@ApiProperty({
enum: AuthorizableReferenceType,
description: 'Define for which known entity, or domain object the operation is peformend.',
example: AuthorizableReferenceType.User,
})
referenceType: AuthorizableReferenceType;

@ApiProperty()
refrenceId: string;

@ApiProperty()
isAuthorized: boolean;

constructor(props: AuthorizedReponse) {
this.requiredPermissions = props.requiredPermissions;
this.userId = props.userId;
this.action = props.action;
this.referenceType = props.referenceType;
this.refrenceId = props.refrenceId;
this.isAuthorized = props.isAuthorized;
}
}
3 changes: 3 additions & 0 deletions apps/server/src/modules/authorization/api/dto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './authorization-url.params';
export * from './authorization-body.params';
export * from './authorization.reponse';
2 changes: 2 additions & 0 deletions apps/server/src/modules/authorization/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './authorization-reference.controller';
export * from './authorization-reference.uc';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { EntityId } from '@shared/domain/types';
import { AuthorizableReferenceType, AuthorizationContext } from '../../domain';
import { AuthorizedReponse } from '../dto/authorization.reponse';

export class AuthorizationReponseMapper {
public static mapToSuccessResponse(
userId: EntityId,
authorizableReferenceType: AuthorizableReferenceType,
authorizableReferenceId: EntityId,
context: AuthorizationContext
): AuthorizedReponse {
const successAuthorizationReponse = AuthorizationReponseMapper.mapToResponse(
userId,
authorizableReferenceType,
authorizableReferenceId,
context,
true
);

return successAuthorizationReponse;
}

private static mapToResponse(
userId: EntityId,
authorizableReferenceType: AuthorizableReferenceType,
authorizableReferenceId: EntityId,
context: AuthorizationContext,
isAuthorized: boolean
): AuthorizedReponse {
const authorizationReponse = new AuthorizedReponse({
userId,
action: context.action,
requiredPermissions: context.requiredPermissions,
referenceType: authorizableReferenceType,
refrenceId: authorizableReferenceId,
isAuthorized,
});

return authorizationReponse;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/authorization/api/mapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './authorization.response.mapper';
Loading
Loading