authorization presenter
This commit is contained in:
parent
1d2e7da673
commit
7dc6e7795f
|
@ -21,7 +21,7 @@ import { Authentication } from '../../domain/entities/authentication';
|
|||
import { Username } from '../../domain/entities/username';
|
||||
import { ValidateAuthenticationQuery } from '../../queries/validate-authentication.query';
|
||||
import { AuthenticationPresenter } from './authentication.presenter';
|
||||
import { RpcValidationPipe } from './rpc.validation-pipe';
|
||||
import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe';
|
||||
import { UsernamePresenter } from './username.presenter';
|
||||
|
||||
@UsePipes(
|
||||
|
@ -43,10 +43,14 @@ export class AuthenticationController {
|
|||
data: ValidateAuthenticationRequest,
|
||||
): Promise<AuthenticationPresenter> {
|
||||
try {
|
||||
const auth: Authentication = await this._queryBus.execute(
|
||||
const authentication: Authentication = await this._queryBus.execute(
|
||||
new ValidateAuthenticationQuery(data.username, data.password),
|
||||
);
|
||||
return this._mapper.map(auth, Authentication, AuthenticationPresenter);
|
||||
return this._mapper.map(
|
||||
authentication,
|
||||
Authentication,
|
||||
AuthenticationPresenter,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new RpcException({
|
||||
code: 7,
|
||||
|
@ -60,10 +64,14 @@ export class AuthenticationController {
|
|||
data: CreateAuthenticationRequest,
|
||||
): Promise<AuthenticationPresenter> {
|
||||
try {
|
||||
const auth: Authentication = await this._commandBus.execute(
|
||||
const authentication: Authentication = await this._commandBus.execute(
|
||||
new CreateAuthenticationCommand(data),
|
||||
);
|
||||
return this._mapper.map(auth, Authentication, AuthenticationPresenter);
|
||||
return this._mapper.map(
|
||||
authentication,
|
||||
Authentication,
|
||||
AuthenticationPresenter,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof DatabaseException) {
|
||||
if (e.message.includes('Already exists')) {
|
||||
|
@ -135,11 +143,15 @@ export class AuthenticationController {
|
|||
data: UpdatePasswordRequest,
|
||||
): Promise<AuthenticationPresenter> {
|
||||
try {
|
||||
const auth: Authentication = await this._commandBus.execute(
|
||||
const authentication: Authentication = await this._commandBus.execute(
|
||||
new UpdatePasswordCommand(data),
|
||||
);
|
||||
|
||||
return this._mapper.map(auth, Authentication, AuthenticationPresenter);
|
||||
return this._mapper.map(
|
||||
authentication,
|
||||
Authentication,
|
||||
AuthenticationPresenter,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new RpcException({
|
||||
code: 7,
|
||||
|
|
|
@ -1,23 +1,38 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import { Controller, UsePipes } from '@nestjs/common';
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||
import { RpcValidationPipe } from 'src/utils/pipes/rpc.validation-pipe';
|
||||
import { DecisionRequest } from '../../domain/dtos/decision.request';
|
||||
import { Authorization } from '../../domain/entities/authorization';
|
||||
import { DecisionQuery } from '../../queries/decision.query';
|
||||
import { DecisionResult } from '../secondaries/decision-result';
|
||||
import { AuthorizationPresenter } from './authorization.presenter';
|
||||
|
||||
@UsePipes(
|
||||
new RpcValidationPipe({
|
||||
whitelist: true,
|
||||
forbidUnknownValues: false,
|
||||
}),
|
||||
)
|
||||
@Controller()
|
||||
export class AuthorizationController {
|
||||
constructor(private readonly _queryBus: QueryBus) {}
|
||||
constructor(
|
||||
private readonly _queryBus: QueryBus,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('AuthorizationService', 'Decide')
|
||||
async decide(data: DecisionRequest): Promise<DecisionResult> {
|
||||
async decide(data: DecisionRequest): Promise<AuthorizationPresenter> {
|
||||
try {
|
||||
const decision: boolean = await this._queryBus.execute(
|
||||
const authorization: Authorization = await this._queryBus.execute(
|
||||
new DecisionQuery(data.uuid, data.domain, data.action, data.context),
|
||||
);
|
||||
return {
|
||||
allow: decision,
|
||||
};
|
||||
return this._mapper.map(
|
||||
authorization,
|
||||
Authorization,
|
||||
AuthorizationPresenter,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new RpcException({
|
||||
code: 7,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
|
||||
export class AuthorizationPresenter {
|
||||
@AutoMap()
|
||||
allow: boolean;
|
||||
}
|
|
@ -7,6 +7,7 @@ import { Domain } from '../../domain/dtos/domain.enum';
|
|||
import { IMakeDecision } from '../../domain/interfaces/decision-maker';
|
||||
import { ContextItem } from '../../domain/dtos/context-item';
|
||||
import { Decision } from './decision';
|
||||
import { Authorization } from '../../domain/entities/authorization';
|
||||
|
||||
@Injectable()
|
||||
export class OpaDecisionMaker extends IMakeDecision {
|
||||
|
@ -22,7 +23,7 @@ export class OpaDecisionMaker extends IMakeDecision {
|
|||
domain: Domain,
|
||||
action: Action,
|
||||
context: Array<ContextItem>,
|
||||
): Promise<boolean> {
|
||||
): Promise<Authorization> {
|
||||
const { data } = await lastValueFrom(
|
||||
this._httpService.post<Decision>(
|
||||
this._configService.get<string>('OPA_URL') + domain + '/' + action,
|
||||
|
@ -34,6 +35,6 @@ export class OpaDecisionMaker extends IMakeDecision {
|
|||
},
|
||||
),
|
||||
);
|
||||
return data.result.allow;
|
||||
return new Authorization(data.result.allow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@ import { DatabaseModule } from '../database/database.module';
|
|||
import { AuthorizationController } from './adapters/primaries/authorization.controller';
|
||||
import { OpaDecisionMaker } from './adapters/secondaries/opa.decision-maker';
|
||||
import { DecisionUseCase } from './domain/usecases/decision.usecase';
|
||||
import { AuthorizationProfile } from './mappers/authorization.profile';
|
||||
|
||||
@Module({
|
||||
imports: [DatabaseModule, CqrsModule, HttpModule],
|
||||
exports: [],
|
||||
controllers: [AuthorizationController],
|
||||
providers: [OpaDecisionMaker, DecisionUseCase],
|
||||
providers: [OpaDecisionMaker, DecisionUseCase, AuthorizationProfile],
|
||||
})
|
||||
export class AuthorizationModule {}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
|
||||
export class Authorization {
|
||||
@AutoMap()
|
||||
allow: boolean;
|
||||
|
||||
constructor(allow: boolean) {
|
||||
this.allow = allow;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Action } from '../dtos/action.enum';
|
||||
import { Domain } from '../dtos/domain.enum';
|
||||
import { Authorization } from '../entities/authorization';
|
||||
|
||||
@Injectable()
|
||||
export abstract class IMakeDecision {
|
||||
|
@ -9,5 +10,5 @@ export abstract class IMakeDecision {
|
|||
domain: Domain,
|
||||
action: Action,
|
||||
context: Array<{ name: string; value: string }>,
|
||||
): Promise<boolean>;
|
||||
): Promise<Authorization>;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { QueryHandler } from '@nestjs/cqrs';
|
||||
import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker';
|
||||
import { DecisionQuery } from '../../queries/decision.query';
|
||||
import { Authorization } from '../entities/authorization';
|
||||
|
||||
@QueryHandler(DecisionQuery)
|
||||
export class DecisionUseCase {
|
||||
constructor(private readonly _decisionMaker: OpaDecisionMaker) {}
|
||||
|
||||
async execute(decisionQuery: DecisionQuery): Promise<boolean> {
|
||||
async execute(decisionQuery: DecisionQuery): Promise<Authorization> {
|
||||
return this._decisionMaker.decide(
|
||||
decisionQuery.uuid,
|
||||
decisionQuery.domain,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import { createMap, Mapper } from '@automapper/core';
|
||||
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthorizationPresenter } from '../adapters/primaries/authorization.presenter';
|
||||
import { Authorization } from '../domain/entities/authorization';
|
||||
|
||||
@Injectable()
|
||||
export class AuthorizationProfile extends AutomapperProfile {
|
||||
constructor(@InjectMapper() mapper: Mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
override get profile() {
|
||||
return (mapper: any) => {
|
||||
createMap(mapper, Authorization, AuthorizationPresenter);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import { ContextItem } from '../../domain/dtos/context-item';
|
|||
import { DecisionRequest } from '../../domain/dtos/decision.request';
|
||||
import { Domain } from '../../domain/dtos/domain.enum';
|
||||
import { DecisionUseCase } from '../../domain/usecases/decision.usecase';
|
||||
import { AuthorizationProfile } from '../../mappers/authorization.profile';
|
||||
import { DecisionQuery } from '../../queries/decision.query';
|
||||
|
||||
const mockOpaDecisionMaker = {
|
||||
|
@ -25,6 +26,7 @@ describe('DecisionUseCase', () => {
|
|||
useValue: mockOpaDecisionMaker,
|
||||
},
|
||||
DecisionUseCase,
|
||||
AuthorizationProfile,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
|
|
@ -66,23 +66,23 @@ describe('OpaDecisionMaker', () => {
|
|||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should return a truthy decision', async () => {
|
||||
const decision = await opaDecisionMaker.decide(
|
||||
it('should return a truthy authorization', async () => {
|
||||
const authorization = await opaDecisionMaker.decide(
|
||||
'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||
Domain.user,
|
||||
Action.read,
|
||||
[],
|
||||
);
|
||||
expect(decision).toBeTruthy();
|
||||
expect(authorization.allow).toBeTruthy();
|
||||
});
|
||||
it('should return a falsy decision', async () => {
|
||||
const decision = await opaDecisionMaker.decide(
|
||||
it('should return a falsy authorization', async () => {
|
||||
const authorization = await opaDecisionMaker.decide(
|
||||
'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||
Domain.user,
|
||||
Action.read,
|
||||
[],
|
||||
);
|
||||
expect(decision).toBeFalsy();
|
||||
expect(authorization.allow).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue