From 2ac09f12d8e9261765b8475b5038d5f765e271af Mon Sep 17 00:00:00 2001 From: "Gabriel R. Antunes" Date: Wed, 1 May 2024 10:46:41 -0400 Subject: [PATCH] refactor(contexto-de-acesso): reorganize --- .../ContextoDeAcesso.ts | 144 +++++++++--------- .../ContextoDeAcessoResolvers.ts | 17 +++ src/contexto-de-acesso/IContextoDeAcesso.ts | 29 ++++ src/contexto-de-acesso/index.ts | 3 + .../adapters/ContextoDeAcessoGraphQl.ts | 4 - .../adapters/ContextoDeAcessoHttp.ts | 4 - .../contexto-de-acesso/adapters/index.ts | 2 - ...ContextoDeAcessoFromCurrentUsuario.pipe.ts | 13 -- .../contexto-de-acesso/core/index.ts | 1 - .../contexto-de-acesso/index.ts | 2 - 10 files changed, 120 insertions(+), 99 deletions(-) rename src/{infrastructure/contexto-de-acesso/core => contexto-de-acesso}/ContextoDeAcesso.ts (91%) create mode 100644 src/contexto-de-acesso/ContextoDeAcessoResolvers.ts create mode 100644 src/contexto-de-acesso/IContextoDeAcesso.ts create mode 100644 src/contexto-de-acesso/index.ts delete mode 100644 src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoGraphQl.ts delete mode 100644 src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoHttp.ts delete mode 100644 src/infrastructure/contexto-de-acesso/adapters/index.ts delete mode 100644 src/infrastructure/contexto-de-acesso/adapters/pipes/ResolveContextoDeAcessoFromCurrentUsuario.pipe.ts delete mode 100644 src/infrastructure/contexto-de-acesso/core/index.ts delete mode 100644 src/infrastructure/contexto-de-acesso/index.ts diff --git a/src/infrastructure/contexto-de-acesso/core/ContextoDeAcesso.ts b/src/contexto-de-acesso/ContextoDeAcesso.ts similarity index 91% rename from src/infrastructure/contexto-de-acesso/core/ContextoDeAcesso.ts rename to src/contexto-de-acesso/ContextoDeAcesso.ts index 091e20e2..fdcc58f8 100644 --- a/src/infrastructure/contexto-de-acesso/core/ContextoDeAcesso.ts +++ b/src/contexto-de-acesso/ContextoDeAcesso.ts @@ -1,43 +1,29 @@ import { ForbiddenException } from '@nestjs/common'; import { castArray } from 'lodash'; import { SelectQueryBuilder } from 'typeorm'; -import { AuthzPolicyPublic } from '../../../application/authorization-policies/00-AuthzPolicyPublic'; -import { IAuthzStatement, IAuthzStatementFilter } from '../../../application/authorization-policies/statements/IAuthzStatement'; -import { IBaseAuthzFilterFn, IBaseAuthzStatementContext } from '../../../domain'; -import type { IContextoDeAcesso } from '../../../domain/contexto-de-acesso/IContextoDeAcesso'; -import { ICurrentUsuario } from '../../authentication/interfaces'; -import { DatabaseContextService } from '../../integrate-database'; +import { IUsuarioDaRequisicao } from '../autenticacao'; +import { IAuthzStatement, AuthzPolicyPublic, IAuthzStatementFilter, IBaseAuthzFilterFn, IBaseAuthzStatementContext } from '../autorizacao'; +import { DatabaseContextService } from '../infraestrutura/integrations/integrate-database'; +import { IContextoDeAcesso } from './IContextoDeAcesso'; function createForbiddenExceptionForAction(action: Action) { return new ForbiddenException(`Insufficient permissions to perform '${action}'.`); } export class ContextoDeAcesso implements IContextoDeAcesso { + #policy = new AuthzPolicyPublic(); + constructor( readonly databaseContext: DatabaseContextService, - readonly usuario: ICurrentUsuario | null, + readonly usuario: IUsuarioDaRequisicao | null, ) { // } - #policy = new AuthzPolicyPublic(); - get statements() { return this.#policy.statements; } - private getStatementForAction(action: Action) { - return (this.statements.find((statement) => statement.action === action) ?? null) as Statement | null; - } - - private createAuthzStatementContext(action: Action, payload: Payload | null) { - return { - action, - payload, - contextoDeAcesso: this, - } as IBaseAuthzStatementContext; - } - async aplicarFiltro( action: Action, qb: SelectQueryBuilder, @@ -62,6 +48,70 @@ export class ContextoDeAcesso implements IContextoDeAcesso { } } + async verifyPermission( + action: Action, + payload: Payload, + id: any = null, + qbInput: SelectQueryBuilder | null = null, + ): Promise { + const statement = this.getStatementForAction(action); + + const context = this.createAuthzStatementContext(action, payload); + + if (statement) { + if (statement.statementKind === 'check') { + const withResultFactory = statement.withCheck; + + if (typeof withResultFactory === 'boolean') { + return withResultFactory; + } else { + const result = await withResultFactory(context as any); + return result; + } + } else if (statement.statementKind === 'filter') { + const filterAction = action; + + const qb = qbInput ?? this.getQueryBuilderForAction(filterAction); + + await this.aplicarFiltro(filterAction, qb, qb.alias, payload as any); + + if (id) { + qb.andWhereInIds(castArray(id)); + } + + const hasTarget = await qb.getExists(); + return hasTarget; + } + } + + return false; + } + + async ensurePermission( + action: Action, + payload: Payload, + id: (number | string) | null = null, + qb: SelectQueryBuilder | null = null, + ): Promise { + const can = await this.verifyPermission(action, payload, id, qb); + + if (!can) { + throw createForbiddenExceptionForAction(action); + } + } + + private getStatementForAction(action: Action) { + return (this.statements.find((statement) => statement.action === action) ?? null) as Statement | null; + } + + private createAuthzStatementContext(action: Action, payload: Payload | null) { + return { + action, + payload, + contextoDeAcesso: this, + } as IBaseAuthzStatementContext; + } + private getQueryBuilderForAction(action: Action) { switch (action) { case 'estado:find': { @@ -150,56 +200,4 @@ export class ContextoDeAcesso implements IContextoDeAcesso { } } } - - async verifyPermission( - action: Action, - payload: Payload, - id: any = null, - qbInput: SelectQueryBuilder | null = null, - ): Promise { - const statement = this.getStatementForAction(action); - - const context = this.createAuthzStatementContext(action, payload); - - if (statement) { - if (statement.statementKind === 'check') { - const withResultFactory = statement.withCheck; - - if (typeof withResultFactory === 'boolean') { - return withResultFactory; - } else { - const result = await withResultFactory(context as any); - return result; - } - } else if (statement.statementKind === 'filter') { - const filterAction = action; - - const qb = qbInput ?? this.getQueryBuilderForAction(filterAction); - - await this.aplicarFiltro(filterAction, qb, qb.alias, payload as any); - - if (id) { - qb.andWhereInIds(castArray(id)); - } - - const hasTarget = await qb.getExists(); - return hasTarget; - } - } - - return false; - } - - async ensurePermission( - action: Action, - payload: Payload, - id: (number | string) | null = null, - qb: SelectQueryBuilder | null = null, - ): Promise { - const can = await this.verifyPermission(action, payload, id, qb); - - if (!can) { - throw createForbiddenExceptionForAction(action); - } - } } diff --git a/src/contexto-de-acesso/ContextoDeAcessoResolvers.ts b/src/contexto-de-acesso/ContextoDeAcessoResolvers.ts new file mode 100644 index 00000000..da16224f --- /dev/null +++ b/src/contexto-de-acesso/ContextoDeAcessoResolvers.ts @@ -0,0 +1,17 @@ +import { IUsuarioDaRequisicao, UsuarioDaRequisicaoGql, UsuarioDaRequisicaoHttp } from '@/autenticacao'; +import { ContextoDeAcesso } from '@/contexto-de-acesso/ContextoDeAcesso'; +import { DatabaseContextService } from '@/infraestrutura/integrations/integrate-database'; +import { Injectable, PipeTransform } from '@nestjs/common'; + +@Injectable() +export class ResolveContextoDeAcessoPipe implements PipeTransform { + constructor(private databaseContextService: DatabaseContextService) {} + + async transform(currentUsuario: IUsuarioDaRequisicao | null /* _metadata: ArgumentMetadata */) { + return new ContextoDeAcesso(this.databaseContextService, currentUsuario ?? null); + } +} + +export const ContextoDeAcessoGraphQl = (options?: any) => UsuarioDaRequisicaoGql(options, ResolveContextoDeAcessoPipe); + +export const ContextoDeAcessoHttp = (options?: any) => UsuarioDaRequisicaoHttp(options, ResolveContextoDeAcessoPipe); diff --git a/src/contexto-de-acesso/IContextoDeAcesso.ts b/src/contexto-de-acesso/IContextoDeAcesso.ts new file mode 100644 index 00000000..2986643a --- /dev/null +++ b/src/contexto-de-acesso/IContextoDeAcesso.ts @@ -0,0 +1,29 @@ +import { IAuthzStatement, IAuthzStatementFilter } from '@/autorizacao/regras/types/IAuthzStatement'; +import { SelectQueryBuilder } from 'typeorm'; +import { IUsuarioDaRequisicao } from '@/autenticacao'; + +export interface IContextoDeAcesso { + readonly usuario: IUsuarioDaRequisicao | null; + + aplicarFiltro: ( + action: Action, + qb: SelectQueryBuilder, + alias?: string, + payload?: Payload | null, + ) => Promise; + + // + ensurePermission: ( + action: Action, + payload: Payload, + id?: any, + qb?: SelectQueryBuilder | null, + ) => Promise; + + verifyPermission( + action: Action, + payload: Payload, + id?: any, + qb?: SelectQueryBuilder | null, + ): Promise; +} diff --git a/src/contexto-de-acesso/index.ts b/src/contexto-de-acesso/index.ts new file mode 100644 index 00000000..d2c01362 --- /dev/null +++ b/src/contexto-de-acesso/index.ts @@ -0,0 +1,3 @@ +export * from './ContextoDeAcesso'; +export * from './ContextoDeAcessoResolvers'; +export * from './IContextoDeAcesso'; diff --git a/src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoGraphQl.ts b/src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoGraphQl.ts deleted file mode 100644 index 445399c3..00000000 --- a/src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoGraphQl.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { CurrentUsuarioGql } from '../../authentication'; -import { ResolveContextoDeAcessoPipe } from './pipes/ResolveContextoDeAcessoFromCurrentUsuario.pipe'; - -export const ContextoDeAcessoGraphQl = (options?: any) => CurrentUsuarioGql(options, ResolveContextoDeAcessoPipe); diff --git a/src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoHttp.ts b/src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoHttp.ts deleted file mode 100644 index dd19cf87..00000000 --- a/src/infrastructure/contexto-de-acesso/adapters/ContextoDeAcessoHttp.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { CurrentUsuarioHttp } from '../../authentication'; -import { ResolveContextoDeAcessoPipe } from './pipes/ResolveContextoDeAcessoFromCurrentUsuario.pipe'; - -export const ContextoDeAcessoHttp = (options?: any) => CurrentUsuarioHttp(options, ResolveContextoDeAcessoPipe); diff --git a/src/infrastructure/contexto-de-acesso/adapters/index.ts b/src/infrastructure/contexto-de-acesso/adapters/index.ts deleted file mode 100644 index 023a58a1..00000000 --- a/src/infrastructure/contexto-de-acesso/adapters/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ContextoDeAcessoGraphQl'; -export * from './ContextoDeAcessoHttp'; diff --git a/src/infrastructure/contexto-de-acesso/adapters/pipes/ResolveContextoDeAcessoFromCurrentUsuario.pipe.ts b/src/infrastructure/contexto-de-acesso/adapters/pipes/ResolveContextoDeAcessoFromCurrentUsuario.pipe.ts deleted file mode 100644 index 1a140172..00000000 --- a/src/infrastructure/contexto-de-acesso/adapters/pipes/ResolveContextoDeAcessoFromCurrentUsuario.pipe.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable, PipeTransform } from '@nestjs/common'; -import { ICurrentUsuario } from '../../../authentication/interfaces'; -import { DatabaseContextService } from '../../../integrate-database'; -import { ContextoDeAcesso } from '../../core/ContextoDeAcesso'; - -@Injectable() -export class ResolveContextoDeAcessoPipe implements PipeTransform { - constructor(private databaseContextService: DatabaseContextService) {} - - async transform(currentUsuario: ICurrentUsuario | null /* _metadata: ArgumentMetadata */) { - return new ContextoDeAcesso(this.databaseContextService, currentUsuario ?? null); - } -} diff --git a/src/infrastructure/contexto-de-acesso/core/index.ts b/src/infrastructure/contexto-de-acesso/core/index.ts deleted file mode 100644 index 08293cc8..00000000 --- a/src/infrastructure/contexto-de-acesso/core/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ContextoDeAcesso'; diff --git a/src/infrastructure/contexto-de-acesso/index.ts b/src/infrastructure/contexto-de-acesso/index.ts deleted file mode 100644 index 1bc7170a..00000000 --- a/src/infrastructure/contexto-de-acesso/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './adapters'; -export * from './core';