Skip to content

Commit

Permalink
refactor(contexto-de-acesso): reorganize
Browse files Browse the repository at this point in the history
  • Loading branch information
guesant committed May 1, 2024
1 parent cf34754 commit 2ac09f1
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -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<Statement extends IAuthzStatement, Action extends Statement['action']>(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<Statement extends IAuthzStatement, Action extends Statement['action']>(action: Action) {
return (this.statements.find((statement) => statement.action === action) ?? null) as Statement | null;
}

private createAuthzStatementContext<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(action: Action, payload: Payload | null) {
return {
action,
payload,
contextoDeAcesso: this,
} as IBaseAuthzStatementContext<Action, Payload>;
}

async aplicarFiltro<Statement extends IAuthzStatementFilter, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
qb: SelectQueryBuilder<any>,
Expand All @@ -62,6 +48,70 @@ export class ContextoDeAcesso implements IContextoDeAcesso {
}
}

async verifyPermission<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
payload: Payload,
id: any = null,
qbInput: SelectQueryBuilder<any> | null = null,
): Promise<boolean> {
const statement = this.getStatementForAction<Statement, Action>(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 = <IAuthzStatementFilter['action']>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<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
payload: Payload,
id: (number | string) | null = null,
qb: SelectQueryBuilder<any> | null = null,
): Promise<void> {
const can = await this.verifyPermission<Statement, Action, Payload>(action, payload, id, qb);

if (!can) {
throw createForbiddenExceptionForAction<Statement, Action>(action);
}
}

private getStatementForAction<Statement extends IAuthzStatement, Action extends Statement['action']>(action: Action) {
return (this.statements.find((statement) => statement.action === action) ?? null) as Statement | null;
}

private createAuthzStatementContext<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(action: Action, payload: Payload | null) {
return {
action,
payload,
contextoDeAcesso: this,
} as IBaseAuthzStatementContext<Action, Payload>;
}

private getQueryBuilderForAction<Action extends IAuthzStatementFilter['action']>(action: Action) {
switch (action) {
case 'estado:find': {
Expand Down Expand Up @@ -150,56 +200,4 @@ export class ContextoDeAcesso implements IContextoDeAcesso {
}
}
}

async verifyPermission<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
payload: Payload,
id: any = null,
qbInput: SelectQueryBuilder<any> | null = null,
): Promise<boolean> {
const statement = this.getStatementForAction<Statement, Action>(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 = <IAuthzStatementFilter['action']>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<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
payload: Payload,
id: (number | string) | null = null,
qb: SelectQueryBuilder<any> | null = null,
): Promise<void> {
const can = await this.verifyPermission<Statement, Action, Payload>(action, payload, id, qb);

if (!can) {
throw createForbiddenExceptionForAction<Statement, Action>(action);
}
}
}
17 changes: 17 additions & 0 deletions src/contexto-de-acesso/ContextoDeAcessoResolvers.ts
Original file line number Diff line number Diff line change
@@ -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);
29 changes: 29 additions & 0 deletions src/contexto-de-acesso/IContextoDeAcesso.ts
Original file line number Diff line number Diff line change
@@ -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: <Statement extends IAuthzStatementFilter, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
qb: SelectQueryBuilder<any>,
alias?: string,
payload?: Payload | null,
) => Promise<void>;

//
ensurePermission: <Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
payload: Payload,
id?: any,
qb?: SelectQueryBuilder<any> | null,
) => Promise<void>;

verifyPermission<Statement extends IAuthzStatement, Action extends Statement['action'], Payload extends Statement['payload']>(
action: Action,
payload: Payload,
id?: any,
qb?: SelectQueryBuilder<any> | null,
): Promise<boolean>;
}
3 changes: 3 additions & 0 deletions src/contexto-de-acesso/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './ContextoDeAcesso';
export * from './ContextoDeAcessoResolvers';
export * from './IContextoDeAcesso';

This file was deleted.

This file was deleted.

2 changes: 0 additions & 2 deletions src/infrastructure/contexto-de-acesso/adapters/index.ts

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion src/infrastructure/contexto-de-acesso/core/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions src/infrastructure/contexto-de-acesso/index.ts

This file was deleted.

0 comments on commit 2ac09f1

Please sign in to comment.