mirror of
https://github.com/dadosfera/maestro.git
synced 2026-08-31 19:58:21 +00:00
FEAT: squash - auth guard
nestjs access controll mock swagger implementation middleware changes and lib permissions file back to maestro FEAT: sending permissions to duc on startup FEAT: new permissions.enum format FEAT: permissions injection (maestro -> duc) FIX: changing method invocation order FEAT: refactored permission enum + duc permission injector + grpcHandler FEAT: auth guards FEAT: seq ids updated
This commit is contained in:
committed by
arthur simas
parent
89b571d7f8
commit
1566efc490
+7
-2
@@ -2,7 +2,12 @@
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"assets": ["**/*.proto"],
|
||||
"assets": [
|
||||
"**/*.proto"
|
||||
],
|
||||
"plugins": [
|
||||
"@nestjs/swagger"
|
||||
],
|
||||
"watchAssets": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+27
@@ -22,6 +22,7 @@
|
||||
"@nestjs/schedule": "^1.1.0",
|
||||
"@nestjs/swagger": "^5.2.1",
|
||||
"@victorradael/protospack": "2.5.0",
|
||||
"@victorradael/protospack-v2": "../protospack",
|
||||
"axios": "^0.25.0",
|
||||
"cron-parser": "^4.4.0",
|
||||
"dotenv": "^14.2.0",
|
||||
@@ -66,6 +67,19 @@
|
||||
"node": "18.3.0"
|
||||
}
|
||||
},
|
||||
"../protospack": {
|
||||
"name": "@victorradael/protospack-v2",
|
||||
"version": "1.1.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"rxjs": "^7.5.5",
|
||||
"ts-proto": "^1.112.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.21",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
|
||||
@@ -3226,6 +3240,10 @@
|
||||
"rxjs": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@victorradael/protospack-v2": {
|
||||
"resolved": "../protospack",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
@@ -12670,6 +12688,15 @@
|
||||
"rxjs": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"@victorradael/protospack-v2": {
|
||||
"version": "file:../protospack",
|
||||
"requires": {
|
||||
"@types/node": "^17.0.21",
|
||||
"rxjs": "^7.5.5",
|
||||
"ts-proto": "^1.112.2",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"@nestjs/schedule": "^1.1.0",
|
||||
"@nestjs/swagger": "^5.2.1",
|
||||
"@victorradael/protospack": "2.5.0",
|
||||
"@victorradael/protospack-v2": "../protospack",
|
||||
"axios": "^0.25.0",
|
||||
"cron-parser": "^4.4.0",
|
||||
"dotenv": "^14.2.0",
|
||||
|
||||
+15
-24
@@ -1,9 +1,7 @@
|
||||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ClientsModule } from '@nestjs/microservices';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { LoggerMiddleware } from './middlewares/authentication';
|
||||
|
||||
import { InputsController } from './modules/inputs/inputs.controller';
|
||||
import { TransformationsController } from './modules/transformations/transformations.controller';
|
||||
import { OutputsController } from './modules/outputs/outputs.controllers';
|
||||
@@ -15,18 +13,18 @@ import { InputsService } from './modules/inputs/inputs.service';
|
||||
import { TransformationsService } from './modules/transformations/transformations.service';
|
||||
import { OutputsService } from './modules/outputs/outputs.service';
|
||||
import { PipelinesService } from './modules/pipelines/pipelines.service';
|
||||
// import { AuthService } from './modules/auth/auth.service';
|
||||
import { HealthService } from './modules/health/health.service';
|
||||
|
||||
import { AuthClientService } from './clients/auth/client.service';
|
||||
import { InputsClientService } from './clients/inputs/client.service';
|
||||
import { TransformationsClientService } from './clients/transformations/client.service';
|
||||
import { OutputsClientService } from './clients/outputs/client.service';
|
||||
import { PipelinesClientService } from './clients/pipelines/client.service';
|
||||
import { AuthClientService } from './clients/auth/client.service';
|
||||
import { PermissionsClientService } from './clients/permissions/client.service';
|
||||
|
||||
import { OutputsClientConfiguration } from './clients/outputs/client.config';
|
||||
import { TransformationsClientConfiguration } from './clients/transformations/client.config';
|
||||
import { AuthClient } from './clients/auth/client.config';
|
||||
import { DucClient } from './clients/duc/client.config';
|
||||
import { InputsClientConfiguration } from './clients/inputs/client.config';
|
||||
import { PipelinesClientConfiguration } from './clients/pipelines/client.config';
|
||||
import { CatalogController } from './modules/catalog/catalog.controller';
|
||||
@@ -34,8 +32,10 @@ import { CatalogService } from './modules/catalog/catalog.service';
|
||||
import { HubspotStrategy } from './modules/oauth/passport-strategies/hubspot';
|
||||
import { OauthController } from './modules/oauth/oauth.controller';
|
||||
import { getOauthSecrets } from './utils/OauthSecrets';
|
||||
import { AuthenticationGuard } from './authentication/authentication.guard';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
|
||||
const authClient = new AuthClient();
|
||||
const ducClient = new DucClient();
|
||||
const inputClient = new InputsClientConfiguration();
|
||||
const outputClient = new OutputsClientConfiguration();
|
||||
const pipelineClient = new PipelinesClientConfiguration();
|
||||
@@ -58,15 +58,19 @@ const transformationClient = new TransformationsClientConfiguration();
|
||||
TransformationsService,
|
||||
OutputsService,
|
||||
PipelinesService,
|
||||
// AuthService,
|
||||
HealthService,
|
||||
InputsClientService,
|
||||
TransformationsClientService,
|
||||
OutputsClientService,
|
||||
PipelinesClientService,
|
||||
AuthClientService,
|
||||
PermissionsClientService,
|
||||
CatalogService,
|
||||
HubspotStrategy,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: AuthenticationGuard,
|
||||
},
|
||||
],
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
@@ -78,7 +82,6 @@ const transformationClient = new TransformationsClientConfiguration();
|
||||
name: 'INPUTS_PACKAGE',
|
||||
...inputClient.config(),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'TRANSFORMATIONS_PACKAGE',
|
||||
...transformationClient.config(),
|
||||
@@ -88,8 +91,8 @@ const transformationClient = new TransformationsClientConfiguration();
|
||||
...outputClient.config(),
|
||||
},
|
||||
{
|
||||
name: 'AUTH_PACKAGE',
|
||||
...authClient.config(),
|
||||
name: 'DUC_PACKAGE',
|
||||
...ducClient.config(),
|
||||
},
|
||||
{
|
||||
name: 'PIPELINES_PACKAGE',
|
||||
@@ -98,16 +101,4 @@ const transformationClient = new TransformationsClientConfiguration();
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class AppModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply(LoggerMiddleware)
|
||||
.forRoutes(
|
||||
InputsController,
|
||||
TransformationsController,
|
||||
OutputsController,
|
||||
PipelinesController,
|
||||
CatalogController,
|
||||
);
|
||||
}
|
||||
}
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SetMetadata, applyDecorators } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { Permission } from './permissions.enum';
|
||||
|
||||
export const PERMISSIONS_KEY = '__PERMISSIONS__';
|
||||
export const MUST_BE_AUTHENTICATED_KEY = '__MUST_BE_AUTHENTICATED__';
|
||||
export const CUSTOM_AUTHENTICATION_FUNCTION_KEY =
|
||||
'__CUSTOM_AUTHENTICATION_FUNCTION__';
|
||||
|
||||
export type AuthenticationFunction = (req: Request, user: any) => boolean;
|
||||
|
||||
export function RequirePermissions(...permissions: Permission[]) {
|
||||
return applyDecorators(
|
||||
SetMetadata(PERMISSIONS_KEY, permissions),
|
||||
SetMetadata(MUST_BE_AUTHENTICATED_KEY, true),
|
||||
);
|
||||
}
|
||||
|
||||
export function AuthenticateCondition(func: AuthenticationFunction) {
|
||||
return applyDecorators(
|
||||
SetMetadata(CUSTOM_AUTHENTICATION_FUNCTION_KEY, func),
|
||||
SetMetadata(MUST_BE_AUTHENTICATED_KEY, true),
|
||||
);
|
||||
}
|
||||
|
||||
export function Authenticated() {
|
||||
return SetMetadata(MUST_BE_AUTHENTICATED_KEY, true);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
OnApplicationBootstrap,
|
||||
ExecutionContext,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import assert from 'assert';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import { AuthClientService } from '../clients/auth/client.service';
|
||||
import { Permission } from './permissions.enum';
|
||||
import {
|
||||
AuthenticationFunction,
|
||||
PERMISSIONS_KEY,
|
||||
CUSTOM_AUTHENTICATION_FUNCTION_KEY,
|
||||
MUST_BE_AUTHENTICATED_KEY,
|
||||
} from './authentication.decorator';
|
||||
import ErrorBuilder from '../utils/ErrorBuilder';
|
||||
import ErrorCodes from '../utils/errorCodes';
|
||||
|
||||
@Injectable()
|
||||
export class AuthenticationGuard
|
||||
implements CanActivate, OnApplicationBootstrap
|
||||
{
|
||||
private readonly logger = new Logger(AuthenticationGuard.name);
|
||||
|
||||
pems: Map<string, string>;
|
||||
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private authClient: AuthClientService,
|
||||
) {
|
||||
this.pems = new Map();
|
||||
}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
return this.loadDucJWKS();
|
||||
}
|
||||
|
||||
async loadDucJWKS() {
|
||||
const { keys } = await this.authClient.getPublicKeys();
|
||||
|
||||
keys.forEach((key) => {
|
||||
this.pems.set(key.kid, key.pem);
|
||||
});
|
||||
}
|
||||
|
||||
canActivate(ctx: ExecutionContext): boolean {
|
||||
const requiredPermissions = this.reflector.getAllAndOverride<Permission[]>(
|
||||
PERMISSIONS_KEY,
|
||||
[ctx.getHandler(), ctx.getClass()],
|
||||
);
|
||||
const customAuthenticationFunction =
|
||||
this.reflector.getAllAndOverride<AuthenticationFunction>(
|
||||
CUSTOM_AUTHENTICATION_FUNCTION_KEY,
|
||||
[ctx.getHandler(), ctx.getClass()],
|
||||
);
|
||||
const mustBeAuthenticated = this.reflector.getAllAndOverride<boolean>(
|
||||
MUST_BE_AUTHENTICATED_KEY,
|
||||
[ctx.getHandler(), ctx.getClass()],
|
||||
);
|
||||
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
const accessToken = this.verifyToken(request);
|
||||
|
||||
if (!accessToken) {
|
||||
// couldn't load valid token
|
||||
|
||||
if (!mustBeAuthenticated) {
|
||||
// no need to be authenticated
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof customAuthenticationFunction === 'function' &&
|
||||
!customAuthenticationFunction(request, request.user)
|
||||
) {
|
||||
// custom authentication function forbidden this request
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.FORBIDDEN);
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(requiredPermissions) &&
|
||||
requiredPermissions.length > 0 &&
|
||||
!this.matchPermissions(requiredPermissions, accessToken.permissions)
|
||||
) {
|
||||
// couldn't match permissions
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.FORBIDDEN);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private verifyToken(request) {
|
||||
const accessToken = request.get('Authorization');
|
||||
let accessTokenPayload;
|
||||
|
||||
// If the user isn't authenticated, an error will occurr anywhere here.
|
||||
// Fancy error avoidance isn't performed by purpose, such as avoiding to access null values.
|
||||
try {
|
||||
const accessTokenDecoded: any = jwt.decode(accessToken, {
|
||||
complete: true,
|
||||
});
|
||||
|
||||
/*assert(
|
||||
accessTokenDecoded.payload.iss === 'duc',
|
||||
'token should be issued by DUC',
|
||||
);*/
|
||||
|
||||
assert(
|
||||
accessTokenDecoded.payload.token_use === 'access',
|
||||
'should be an access token',
|
||||
);
|
||||
|
||||
const { kid } = accessTokenDecoded.header;
|
||||
const pemValue: string = this.pems.get(kid);
|
||||
|
||||
jwt.verify(accessToken, pemValue);
|
||||
accessTokenPayload = accessTokenDecoded.payload;
|
||||
} catch (err) {
|
||||
this.logger.log('Unable to verify duc access token\n' + err.stack);
|
||||
return false;
|
||||
}
|
||||
|
||||
request.accessTokenPayload = accessTokenPayload;
|
||||
request.user = {
|
||||
user_id: accessTokenPayload.user_id,
|
||||
username: accessTokenPayload.username,
|
||||
permissions: accessTokenPayload.permissions,
|
||||
customer_id: accessTokenPayload.customer_id,
|
||||
customer_name: accessTokenPayload.customer_name,
|
||||
customer_tier: accessTokenPayload.customer_tier,
|
||||
};
|
||||
// TODO: for backwards compatibility. remove in the future
|
||||
request.body.info = {
|
||||
user_id: accessTokenPayload.user_id,
|
||||
customer_id: accessTokenPayload.customer_id,
|
||||
customer: accessTokenPayload.customer_name,
|
||||
customer_tier: accessTokenPayload.customer_tier,
|
||||
};
|
||||
|
||||
return accessTokenPayload;
|
||||
}
|
||||
|
||||
private matchPermissions(
|
||||
requiredPermissions: Permission[],
|
||||
userPermissions: number[],
|
||||
) {
|
||||
return requiredPermissions.some((permission) =>
|
||||
userPermissions.includes(permission.seqid),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
import assert from 'assert';
|
||||
|
||||
export enum PermissionUsages {
|
||||
PUBLIC = 'public',
|
||||
INTERNAL = 'internal',
|
||||
}
|
||||
|
||||
export interface Permission {
|
||||
seqid: number;
|
||||
claim: string;
|
||||
usage: PermissionUsages;
|
||||
}
|
||||
|
||||
// like field numbers in gRPC, avoid to change (or reuse previously used) seqids once its
|
||||
// deployed to DUC
|
||||
/*export const Permissions: {
|
||||
[P in keyof any]: { [Q in keyof any]: Permission };
|
||||
} = {*/
|
||||
export const Permissions = {
|
||||
AUTH: {
|
||||
CREATE: {
|
||||
seqid: 18,
|
||||
claim: 'POST /auth',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
PIPELINE: {
|
||||
CREATE: {
|
||||
seqid: 23,
|
||||
claim: 'POST /pipelines',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET: {
|
||||
seqid: 13,
|
||||
claim: 'GET /pipelines',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE: {
|
||||
seqid: 29,
|
||||
claim: 'PUT /pipelines',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
DELETE: {
|
||||
seqid: 5,
|
||||
claim: 'DELETE /pipelines',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
INPUT: {
|
||||
CREATE: {
|
||||
seqid: 21,
|
||||
claim: 'POST /inputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET: {
|
||||
seqid: 9,
|
||||
claim: 'GET /inputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE: {
|
||||
seqid: 27,
|
||||
claim: 'PUT /inputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE_PARTIAL: {
|
||||
seqid: 17,
|
||||
claim: 'PATCH /inputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
DELETE: {
|
||||
seqid: 3,
|
||||
claim: 'DELETE /inputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET_ENTITIES: {
|
||||
seqid: 10,
|
||||
claim: 'GET /inputs/available-entities',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
OUTPUT: {
|
||||
CREATE: {
|
||||
seqid: 22,
|
||||
claim: 'POST /outputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET: {
|
||||
seqid: 12,
|
||||
claim: 'GET /outputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE: {
|
||||
seqid: 28,
|
||||
claim: 'PUT /outputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
DELETE: {
|
||||
seqid: 4,
|
||||
claim: 'DELETE /outputs',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
TRANSFORMATIONS: {
|
||||
CREATE: {
|
||||
seqid: 24,
|
||||
claim: 'POST /transformations',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET: {
|
||||
seqid: 15,
|
||||
claim: 'GET /transformations',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE: {
|
||||
seqid: 30,
|
||||
claim: 'PUT /transformations',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
DELETE: {
|
||||
seqid: 6,
|
||||
claim: 'DELETE /transformations',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
CATALOG: {
|
||||
CREATE: {
|
||||
seqid: 19,
|
||||
claim: 'POST /catalog',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET: {
|
||||
seqid: 7,
|
||||
claim: 'GET /catalog',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE: {
|
||||
seqid: 25,
|
||||
claim: 'PUT /catalog',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
DELETE: {
|
||||
seqid: 1,
|
||||
claim: 'DELETE /catalog',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
CONNECTORS: {
|
||||
CREATE: {
|
||||
seqid: 20,
|
||||
claim: 'POST /connectors',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
GET: {
|
||||
seqid: 8,
|
||||
claim: 'GET /connectors',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
UPDATE: {
|
||||
seqid: 26,
|
||||
claim: 'PUT /connectors',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
DELETE: {
|
||||
seqid: 2,
|
||||
claim: 'DELETE /connectors',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
SNOWFLAKE: {
|
||||
OPEN: {
|
||||
seqid: 14,
|
||||
claim: 'GET /snowflake',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
ZENDESK: {
|
||||
OPEN: {
|
||||
seqid: 16,
|
||||
claim: 'GET /zendesk',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
|
||||
METABASE: {
|
||||
OPEN: {
|
||||
seqid: 11,
|
||||
claim: 'GET /metabase',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// traverses the object searching for duplicate seqids or claims (executes at runtime)
|
||||
const seqids = Object.values(Permissions).flatMap((namespace) =>
|
||||
Object.values(namespace).map(({ seqid }) => seqid),
|
||||
);
|
||||
const claims = Object.values(Permissions).flatMap((namespace) =>
|
||||
Object.values(namespace).map(({ claim }) => claim),
|
||||
);
|
||||
Object.values(Permissions).map((namespace) =>
|
||||
Object.values(namespace).map(({ seqid, claim }) => {
|
||||
const seqidsCount = seqids.filter((x) => x === seqid).length;
|
||||
assert(seqidsCount === 1, `seqid ${seqid} count is not 1`);
|
||||
|
||||
const claimsCount = claims.filter((x) => x === claim).length;
|
||||
assert(claimsCount === 1, `claim '${claim}' count is not 1`);
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
|
||||
import ErrorBuilder from '../utils/ErrorBuilder';
|
||||
import ErrorCodes from '../utils/errorCodes';
|
||||
|
||||
export interface RequestUser {
|
||||
user_id: string;
|
||||
username: string;
|
||||
permissions: string;
|
||||
customer_id: string;
|
||||
customer_name: string;
|
||||
customer_tier: string;
|
||||
}
|
||||
|
||||
export const User = createParamDecorator((data: any, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
|
||||
if (!request.user && data?.required) {
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return request.user;
|
||||
});
|
||||
@@ -1,259 +1,150 @@
|
||||
import { OnModuleInit, Inject } from '@nestjs/common';
|
||||
import { Logger, OnModuleInit, Inject } from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
|
||||
import { ProtoServices } from '@victorradael/protospack-v2/dist/lib/Duc';
|
||||
import { AuthProtoService as AuthServiceInterface } from '@victorradael/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
||||
import {
|
||||
DucServicesNames,
|
||||
AuthServiceInterface,
|
||||
AuthGetPublicKeysResponse,
|
||||
AuthSignInRequest,
|
||||
AuthSignInResponse,
|
||||
AuthRefreshAccessTokenRequest,
|
||||
AuthConfirmationRequest,
|
||||
AuthResendConfirmationCodeRequest,
|
||||
AuthRefreshAccessTokenResponse,
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthEnableTotpMfaResponse,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthDisableTotpMfaResponse,
|
||||
AuthDismissTotpMfaRequest,
|
||||
AuthDismissTotpMfaResponse,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
AuthVerifyTotpMfaResponse,
|
||||
AuthChangePasswordRequest,
|
||||
AuthChangePasswordResponse,
|
||||
AuthResetPasswordRequest,
|
||||
AuthResetPasswordResponse,
|
||||
AuthVerifyResetPasswordCodeRequest,
|
||||
AuthVerifyResetPasswordCodeResponse,
|
||||
AuthConfirmResetPasswordRequest,
|
||||
} from '@victorradael/protospack';
|
||||
AuthConfirmResetPasswordResponse,
|
||||
} from '@victorradael/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
|
||||
import grpcHandler from '../../utils/grpcHandler';
|
||||
|
||||
export class AuthClientService implements OnModuleInit {
|
||||
private readonly logger = new Logger(AuthClientService.name);
|
||||
|
||||
private authService: AuthServiceInterface;
|
||||
constructor(
|
||||
@Inject('AUTH_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {}
|
||||
constructor(@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.authService = this.grpcClient.getService<AuthServiceInterface>(
|
||||
DucServicesNames.AuthProtoService,
|
||||
ProtoServices.AuthProtoService,
|
||||
);
|
||||
}
|
||||
|
||||
async signIn({ username, password, totp }: AuthSignInRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'SignIn');
|
||||
async getPublicKeys() {
|
||||
this.logger.log('GetPublicKeys');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.signIn({ username, password, totp }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthGetPublicKeysResponse>(
|
||||
this.authService.AuthGetPublicKeys({}),
|
||||
);
|
||||
}
|
||||
|
||||
async enableTotpMFA({
|
||||
accessToken,
|
||||
password,
|
||||
}: AuthEnableTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'enableTotpMFA');
|
||||
async signIn({ username, password, totp }: AuthSignInRequest) {
|
||||
this.logger.log('SignIn');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.enableTotpMfa({ accessToken, password }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthSignInResponse>(
|
||||
this.authService.AuthSignIn({ username, password, totp }),
|
||||
);
|
||||
}
|
||||
|
||||
async disableTotpMFA({
|
||||
accessToken,
|
||||
password,
|
||||
}: AuthDisableTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'disableTotpMFA');
|
||||
async refreshAccessToken({ refreshToken }: AuthRefreshAccessTokenRequest) {
|
||||
this.logger.log('RefreshAccessToken');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.disableTotpMfa({ accessToken, password }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async dismissTotpMFA({
|
||||
accessToken,
|
||||
}: AuthDismissTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'dismissTotpMFA');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.dismissTotpMfa({ accessToken }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async verifyTotp({
|
||||
accessToken,
|
||||
totp,
|
||||
}: AuthVerifyTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'disableTotpMFA');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.verifyTotp({ accessToken, totp }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async confirmRegister({
|
||||
username,
|
||||
code,
|
||||
}: AuthConfirmationRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'confirmRegister');
|
||||
|
||||
const tokens = await new Promise((resolve, reject) => {
|
||||
this.authService.confirmRegister({ username, code }).subscribe({
|
||||
next(x) {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
|
||||
async resendeConfirmationCode({
|
||||
username,
|
||||
}: AuthResendConfirmationCodeRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'resendConfirmationCode');
|
||||
|
||||
const authResendConfirmationCodeRequestReturn = await new Promise(
|
||||
(resolve, reject) => {
|
||||
this.authService.resendConfirmationCode({ username }).subscribe({
|
||||
next(x) {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
return authResendConfirmationCodeRequestReturn;
|
||||
}
|
||||
|
||||
async refreshAccessToken({
|
||||
refreshToken,
|
||||
}: AuthRefreshAccessTokenRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'RefreshAccessToken');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.refreshAccessToken({ refreshToken }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthRefreshAccessTokenResponse>(
|
||||
this.authService.AuthRefreshAccessToken({ refreshToken }),
|
||||
);
|
||||
}
|
||||
|
||||
async changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
}: AuthChangePasswordRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'ChangePassword');
|
||||
}: AuthChangePasswordRequest) {
|
||||
this.logger.log('ChangePassword');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService
|
||||
.changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
})
|
||||
.subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthChangePasswordResponse>(
|
||||
this.authService.AuthChangePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async resetPassword({ username }: AuthResetPasswordRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'resetPassword');
|
||||
async resetPassword({ username }: AuthResetPasswordRequest) {
|
||||
this.logger.log('resetPassword');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.resetPassword({ username }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthResetPasswordResponse>(
|
||||
this.authService.AuthResetPassword({ username }),
|
||||
);
|
||||
}
|
||||
|
||||
async verifyResetPasswordCode({
|
||||
username,
|
||||
code,
|
||||
}: AuthVerifyResetPasswordCodeRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'verifyResetPasswordCode');
|
||||
}: AuthVerifyResetPasswordCodeRequest) {
|
||||
this.logger.log('verifyResetPasswordCode');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.verifyResetPasswordCode({ username, code }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthVerifyResetPasswordCodeResponse>(
|
||||
this.authService.AuthVerifyResetPasswordCode({ username, code }),
|
||||
);
|
||||
}
|
||||
|
||||
async confirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
}: AuthConfirmResetPasswordRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'confirmResetPassword');
|
||||
}: AuthConfirmResetPasswordRequest) {
|
||||
this.logger.log('confirmResetPassword');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService
|
||||
.confirmResetPassword({ username, code, newPassword })
|
||||
.subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
return grpcHandler<AuthConfirmResetPasswordResponse>(
|
||||
this.authService.AuthConfirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async enableTotpMFA({ accessToken, password }: AuthEnableTotpMfaRequest) {
|
||||
this.logger.log('enableTotpMFA');
|
||||
|
||||
return grpcHandler<AuthEnableTotpMfaResponse>(
|
||||
this.authService.AuthEnableTotpMfa({ accessToken, password }),
|
||||
);
|
||||
}
|
||||
|
||||
async disableTotpMFA({ accessToken, password }: AuthDisableTotpMfaRequest) {
|
||||
this.logger.log('disableTotpMFA');
|
||||
|
||||
return grpcHandler<AuthDisableTotpMfaResponse>(
|
||||
this.authService.AuthDisableTotpMfa({ accessToken, password }),
|
||||
);
|
||||
}
|
||||
|
||||
async dismissTotpMFA({ accessToken }: AuthDismissTotpMfaRequest) {
|
||||
this.logger.log('dismissTotpMFA');
|
||||
|
||||
return grpcHandler<AuthDismissTotpMfaResponse>(
|
||||
this.authService.AuthDismissTotpMfa({ accessToken }),
|
||||
);
|
||||
}
|
||||
|
||||
async verifyTotp({ accessToken, totp }: AuthVerifyTotpMfaRequest) {
|
||||
this.logger.log('disableTotpMFA');
|
||||
|
||||
return grpcHandler<AuthVerifyTotpMfaResponse>(
|
||||
this.authService.AuthVerifyTotpMfa({ accessToken, totp }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
import {
|
||||
ProtoPackages,
|
||||
ProtoPaths,
|
||||
} from '@victorradael/protospack-v2/dist/lib/Duc';
|
||||
|
||||
import { DucProtoFilePath, DucPackages } from '@victorradael/protospack';
|
||||
|
||||
export class AuthClient {
|
||||
export class DucClient {
|
||||
config(): ClientOptions {
|
||||
return {
|
||||
transport: Transport.GRPC,
|
||||
options: {
|
||||
url: process.env.DUC_URL,
|
||||
package: DucPackages,
|
||||
package: ProtoPackages.WritePackage,
|
||||
credentials: process.env.LOCAL_ENV
|
||||
? undefined
|
||||
: credentials.createSsl(),
|
||||
protoPath: DucProtoFilePath,
|
||||
protoPath: ProtoPaths.WriteFilePath,
|
||||
loader: {
|
||||
keepCase: true,
|
||||
enums: String,
|
||||
objects: true,
|
||||
arrays: true,
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Logger, OnModuleInit, Inject } from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
|
||||
import { ProtoServices } from '@victorradael/protospack-v2/dist/lib/Duc';
|
||||
import { PermissionsProtoService as PermissionsServiceInterface } from '@victorradael/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
||||
import {
|
||||
Empty,
|
||||
InjectPermissionsRequest,
|
||||
} from '@victorradael/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
|
||||
import grpcHandler from '../../utils/grpcHandler';
|
||||
|
||||
export class PermissionsClientService implements OnModuleInit {
|
||||
private readonly logger = new Logger(PermissionsClientService.name);
|
||||
|
||||
private permissionsService: PermissionsServiceInterface;
|
||||
constructor(@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.permissionsService =
|
||||
this.grpcClient.getService<PermissionsServiceInterface>(
|
||||
ProtoServices.PermissionsProtoService,
|
||||
);
|
||||
}
|
||||
|
||||
async injectPermissions({ permissions }: InjectPermissionsRequest) {
|
||||
this.logger.log('InjectPermissions');
|
||||
|
||||
return grpcHandler<Empty>(
|
||||
this.permissionsService.InjectPermissions({ permissions }),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
ForbiddenException,
|
||||
InternalServerErrorException,
|
||||
NestMiddleware,
|
||||
UnauthorizedException,
|
||||
UseFilters,
|
||||
} from '@nestjs/common';
|
||||
import jwkToPem from 'jwk-to-pem';
|
||||
import { decode, verify } from 'jsonwebtoken';
|
||||
import { HttpExceptionFilter } from '../error/http-exception.filter';
|
||||
|
||||
@UseFilters(new HttpExceptionFilter())
|
||||
export class LoggerMiddleware implements NestMiddleware {
|
||||
use = async (request: Request, response: Response, next: NextFunction) => {
|
||||
const idToken = request.get('Dadosfera-User');
|
||||
const accessToken = request.get('Authorization');
|
||||
const privateKey = process.env.JWT_PRIVATE_KEY;
|
||||
let requiredRoute = '';
|
||||
const requiredMethod = request.method.trim();
|
||||
if (request.route.path.split('/').length >= 2) {
|
||||
requiredRoute = request.route.path.split('/')[1].trim();
|
||||
}
|
||||
|
||||
verify(idToken, privateKey, (err) => {
|
||||
if (err) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
});
|
||||
const jwtDecoded: any = decode(idToken);
|
||||
|
||||
const permissions = jwtDecoded.user.permissions;
|
||||
|
||||
const clientId = jwtDecoded.user.customerId;
|
||||
const customer = jwtDecoded.user.customer;
|
||||
const userId = jwtDecoded.user.id;
|
||||
const customer_tier = jwtDecoded.user.customer_tier;
|
||||
|
||||
await verifyToken(accessToken);
|
||||
|
||||
let havePermission = false;
|
||||
const permission = permissions.find((permission) => {
|
||||
permission = permission.split('/');
|
||||
const method = permission[0].trim();
|
||||
const route = permission[1].trim();
|
||||
|
||||
if (method === requiredMethod && route === requiredRoute) {
|
||||
return permission;
|
||||
}
|
||||
});
|
||||
|
||||
if (permission) {
|
||||
havePermission = true;
|
||||
}
|
||||
|
||||
if (havePermission) {
|
||||
request.body.info = {
|
||||
customer_id: clientId,
|
||||
user_id: userId,
|
||||
customer,
|
||||
customer_tier,
|
||||
};
|
||||
next();
|
||||
} else {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let pems: { [key: string]: Record<string, unknown> }[];
|
||||
|
||||
const setUp = async (region: string, id: string) => {
|
||||
const URL = `https://cognito-idp.${region}.amazonaws.com/${id}/.well-known/jwks.json`;
|
||||
|
||||
try {
|
||||
const response = await axios.get(URL);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
const data = await response.data;
|
||||
const { keys } = data;
|
||||
|
||||
pems = keys.map((key: any) => {
|
||||
const modulus = key.n;
|
||||
const exponent = key.e;
|
||||
const keyType = key.kty;
|
||||
const jwk = { kty: keyType, n: modulus, e: exponent };
|
||||
const pem = jwkToPem(jwk);
|
||||
const keyId = key.kid;
|
||||
|
||||
return { [keyId]: pem };
|
||||
});
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
// console.log('Error! Unable to download JWKs');
|
||||
}
|
||||
};
|
||||
|
||||
const verifyToken = async (accessToken: string) => {
|
||||
const awsRegion = process.env.AWS_REGION;
|
||||
const awsPoolId = process.env.AWS_IDENTITY_POOL_ID;
|
||||
|
||||
try {
|
||||
await setUp(awsRegion, awsPoolId);
|
||||
if (!accessToken) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
const user: any = decode(accessToken, { complete: true });
|
||||
|
||||
if (user === null) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
const { kid } = user.header;
|
||||
const pem = pems.filter((item: any) => item[kid]);
|
||||
const pemValue: any = pem[0][kid];
|
||||
|
||||
if (!pem) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
verify(accessToken, pemValue, (err: any) => {
|
||||
if (err) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
} catch (error) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
};
|
||||
+103
-146
@@ -1,60 +1,72 @@
|
||||
import {
|
||||
Logger,
|
||||
Body,
|
||||
Controller,
|
||||
Headers,
|
||||
Post,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
OnApplicationBootstrap,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
AuthSignInRequest,
|
||||
AuthRefreshAccessTokenRequest,
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
AuthChangePasswordRequest,
|
||||
AuthResetPasswordRequest,
|
||||
AuthVerifyResetPasswordCodeRequest,
|
||||
AuthConfirmResetPasswordRequest,
|
||||
} from '@victorradael/protospack';
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
} from '@victorradael/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
|
||||
import { AuthClientService } from 'src/clients/auth/client.service';
|
||||
import { AuthClientService } from '../../clients/auth/client.service';
|
||||
import { PermissionsClientService } from '../../clients/permissions/client.service';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
|
||||
import ErrorBuilder from '../../utils/ErrorBuilder';
|
||||
|
||||
@ApiTags('Auth')
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private authClient: AuthClientService) {}
|
||||
export class AuthController implements OnApplicationBootstrap {
|
||||
private readonly logger = new Logger(AuthController.name);
|
||||
|
||||
// DEPRECADO!
|
||||
@Post()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async signIn_old(@Body() body: AuthSignInRequest) {
|
||||
console.log(`/auth`, 'SignIn_old');
|
||||
constructor(
|
||||
private authClient: AuthClientService,
|
||||
private permissionsClient: PermissionsClientService,
|
||||
) {}
|
||||
|
||||
return this.signIn(body);
|
||||
}
|
||||
// internally used to send permissions to duc on microservice startup
|
||||
async onApplicationBootstrap() {
|
||||
this.logger.log('sending permissions to DUC...');
|
||||
|
||||
@Post('login')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async signIn_login(@Body() body: AuthSignInRequest) {
|
||||
console.log(`/auth`, 'SignIn_login');
|
||||
const permissions = Object.values(Permissions).flatMap((namespace) =>
|
||||
Object.values(namespace),
|
||||
);
|
||||
|
||||
return this.signIn(body);
|
||||
return this.permissionsClient
|
||||
.injectPermissions({ permissions })
|
||||
.catch((err: ErrorBuilder) => {
|
||||
if (
|
||||
err.code === 'No connection established' &&
|
||||
process.env.LOCAL_ENV === 'true'
|
||||
) {
|
||||
return this.logger.log(
|
||||
"couldn't connect to DUC. suppresing in local env",
|
||||
);
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
@Post('sign-in')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async signIn(@Body() { username, password, totp }: AuthSignInRequest) {
|
||||
console.log(`/auth`, 'SignIn');
|
||||
this.logger.log(`/auth`, 'SignIn');
|
||||
|
||||
const tokens = await this.authClient
|
||||
.signIn({ username, password, totp })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return tokens;
|
||||
return this.authClient.signIn({ username, password, totp });
|
||||
}
|
||||
|
||||
@Post('refresh-access-token')
|
||||
@@ -62,15 +74,63 @@ export class AuthController {
|
||||
async refreshAccessToken(
|
||||
@Body() { refreshToken }: AuthRefreshAccessTokenRequest,
|
||||
) {
|
||||
console.log(`/auth`, 'RefreshAccessToken');
|
||||
this.logger.log(`/auth`, 'RefreshAccessToken');
|
||||
|
||||
const accessToken = await this.authClient
|
||||
.refreshAccessToken({ refreshToken })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
return this.authClient.refreshAccessToken({ refreshToken });
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
@Post('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async changePassword(
|
||||
@Body() body: AuthChangePasswordRequest,
|
||||
@Headers() headers,
|
||||
) {
|
||||
this.logger.log(`/auth`, 'change-password');
|
||||
|
||||
const { oldPassword, newPassword } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
return this.authClient.changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async resetPassword(@Body() body: AuthResetPasswordRequest) {
|
||||
this.logger.log(`/auth`, 'reset-password');
|
||||
|
||||
const { username } = body;
|
||||
|
||||
return this.authClient.resetPassword({ username });
|
||||
}
|
||||
|
||||
@Post('verify-reset-password-code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async verifyResetPasswordCode(
|
||||
@Body() body: AuthVerifyResetPasswordCodeRequest,
|
||||
) {
|
||||
this.logger.log(`/auth`, 'verify-reset-password-code');
|
||||
|
||||
const { username, code } = body;
|
||||
|
||||
return this.authClient.verifyResetPasswordCode({ username, code });
|
||||
}
|
||||
|
||||
@Post('confirm-reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async confirmResetPassword(@Body() body: AuthConfirmResetPasswordRequest) {
|
||||
this.logger.log(`/auth`, 'confirm-reset-password');
|
||||
|
||||
const { username, code, newPassword } = body;
|
||||
|
||||
return this.authClient.confirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('enable-totp')
|
||||
@@ -79,18 +139,12 @@ export class AuthController {
|
||||
@Body() body: AuthEnableTotpMfaRequest,
|
||||
@Headers() headers,
|
||||
) {
|
||||
console.log(`/auth`, 'enable-totp');
|
||||
this.logger.log(`/auth`, 'enable-totp');
|
||||
|
||||
const { password } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.enableTotpMFA({ accessToken, password })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.enableTotpMFA({ accessToken, password });
|
||||
}
|
||||
|
||||
@Post('disable-totp')
|
||||
@@ -99,129 +153,32 @@ export class AuthController {
|
||||
@Body() body: AuthDisableTotpMfaRequest,
|
||||
@Headers() headers,
|
||||
) {
|
||||
console.log(`/auth`, 'disable-totp');
|
||||
this.logger.log(`/auth`, 'disable-totp');
|
||||
|
||||
const { password } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.disableTotpMFA({ accessToken, password })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.disableTotpMFA({ accessToken, password });
|
||||
}
|
||||
|
||||
@Post('dismiss-totp')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async dismissTotpMFA(@Headers() headers) {
|
||||
console.log(`/auth`, 'disable-totp');
|
||||
this.logger.log(`/auth`, 'disable-totp');
|
||||
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.dismissTotpMFA({ accessToken })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.dismissTotpMFA({ accessToken });
|
||||
}
|
||||
|
||||
@Post('verify-totp')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async verifyTotp(
|
||||
@Body() body: AuthVerifyTotpMfaRequest,
|
||||
@Headers() headers,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'enable-totp');
|
||||
async verifyTotp(@Body() body: AuthVerifyTotpMfaRequest, @Headers() headers) {
|
||||
this.logger.log(`/auth`, 'enable-totp');
|
||||
|
||||
const { totp } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.verifyTotp({ accessToken, totp })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async changePassword(
|
||||
@Body() body: AuthChangePasswordRequest,
|
||||
@Headers() headers,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'change-password');
|
||||
|
||||
const { oldPassword, newPassword } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
})
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async resetPassword(@Body() body: AuthResetPasswordRequest): Promise<any> {
|
||||
console.log(`/auth`, 'reset-password');
|
||||
|
||||
const { username } = body;
|
||||
|
||||
const response = await this.authClient
|
||||
.resetPassword({ username })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('verify-reset-password-code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async verifyResetPasswordCode(
|
||||
@Body() body: AuthVerifyResetPasswordCodeRequest,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'verify-reset-password-code');
|
||||
|
||||
const { username, code } = body;
|
||||
|
||||
const response = await this.authClient
|
||||
.verifyResetPasswordCode({ username, code })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('confirm-reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async confirmResetPassword(
|
||||
@Body() body: AuthConfirmResetPasswordRequest,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'confirm-reset-password');
|
||||
|
||||
const { username, code, newPassword } = body;
|
||||
|
||||
const response = await this.authClient
|
||||
.confirmResetPassword({ username, code, newPassword })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.verifyTotp({ accessToken, totp });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import nock from 'nock';
|
||||
import { LoggerMiddleware } from '../../middlewares/authentication';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { ConsoleLogger, UnauthorizedException } from '@nestjs/common';
|
||||
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
// let mockRequest: Partial<Request>;
|
||||
// let mockResponse: Partial<Response>;
|
||||
// const nextFunction: NextFunction = jest.fn();
|
||||
|
||||
// beforeEach(() => {
|
||||
// mockRequest = {
|
||||
// headers: {},
|
||||
// body: {
|
||||
// info: {},
|
||||
// },
|
||||
// };
|
||||
// mockResponse = {
|
||||
// json: jest.fn(),
|
||||
// };
|
||||
// });
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
|
||||
// it('Should be able to pass auth', async () => {
|
||||
// const awsRegion = process.env.AWS_REGION;
|
||||
// const awsPoolId = process.env.AWS_IDENTITY_POOL_ID;
|
||||
// const cognitoRes = {
|
||||
// keys: [
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'z3VA3+i9JZY3JFDJTNWOap8RY+B7C6mFedaqCuvLvqA=',
|
||||
// kty: 'RSA',
|
||||
// n: 'vIGCQDkPA_eQUoaGDUsCyK_Whr76mNW0kZ1uac6ZnyY6hsjUIjvwehqv-ux3cQo4rQZQVFcoh8n9cK5jguz4GVdD972vIxoEdyv32nBFVr5e1PBunKJ2Y32GTR_Hl0XiE0hRe1v6cWuTqiC4qm1NY7tXYL3mI9L6s9ztNbhmG_V44y26PdhL4vRVrJaHOAmCs-77U-QYAC7Llkpmjh-8tG1zt9_FJ237cpBUVOuhD-7Nm32_eB9wddBGfBw0F10ko_KJoU-7683_Kv8k9coJzFUSONId-bfnLOzs8j1L6ZAHipXCR7rpmuRYzXztjp4Wmm2zPUToWLdMEy0Hq1OWmw',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'u+1W9pi+clp8LaPhZVrv4dXMqRkTRD02YWhbm0NvsUw=',
|
||||
// kty: 'RSA',
|
||||
// n: 'noGq1cRMAKJPWahqfC_zWasYovSUycaS1basMfEoh3ePLc9zRgmyfiVKYzLRosHMe1uk0Y5ekCBKnWA8Yl7I84Yt7IIIaE44oJjSEGBKT3m8i8YaXzawaNs63KPkRh8553o3KzL75bQWcI_ABKqkf-uAKSCl_XotBGkzLUl4hIOYtAtRGEfKaNMPqyTCT5Zn71pMd0isppaUiTW2T5QLsZV1IBp46aSrl_D5Q_FTsJT7feobQVoHp2zfIorCpkfTXBTBMQTEBFSDyPbc6cULl48VKxp0B0GEwR_kYCEHEVzf41LQcUWZUE0OdBychijkSc9MZJnBWUYQZyedJILWnQ',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// nock(
|
||||
// `https://cognito-idp.${awsRegion}.amazonaws.com/${awsPoolId}/.well-known/jwks.json`,
|
||||
// )
|
||||
// .persist()
|
||||
// .get('')
|
||||
// .reply(200, cognitoRes);
|
||||
|
||||
// mockRequest.headers['Dadosfera-User'] =
|
||||
// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiZTljYjI1ZGEtMTQyOC00NDJjLTg5NjItYTdkNmQxNjhkODUyIiwibmFtZSI6InJvZHJpZ28uemFtYm9uaSIsInVzZXJuYW1lIjoicm9kcmlnby56YW1ib25pQGRhZG9zZmVyYS5haSIsInJ1bGVJZCI6IjIxNzdmZjg4LThmY2ItNDRlNS1hYzYxLWNjYmU2Y2M3MGU4ZCIsImN1c3RvbWVySWQiOiIxMTI5ODBhMy0wYTEyLTQxYzktYmZmZC03OTFjZjdjYTg3YTIiLCJtZmFTdGF0dXMiOiJwZW5kaW5nIiwiY3JlYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwidXBkYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwicGVybWlzc2lvbnMiOlsiUE9TVCAvcGlwZWxpbmVzIiwiR0VUIC9waXBlbGluZXMiLCJQVVQgL3BpcGVsaW5lcyIsIkRFTEVURSAvcGlwZWxpbmVzIiwiUE9TVCAvaW5wdXRzIiwiR0VUIC9pbnB1dHMiLCJQVVQgL2lucHV0cyIsIkRFTEVURSAvaW5wdXRzIiwiUE9TVCAvb3V0cHV0cyIsIkdFVCAvb3V0cHV0cyIsIlBVVCAvb3V0cHV0cyIsIkRFTEVURSAvb3V0cHV0cyIsIlBPU1QgL3RyYW5zZm9ybWF0aW9ucyIsIkdFVCAvdHJhbnNmb3JtYXRpb25zIiwiUFVUIC90cmFuc2Zvcm1hdGlvbnMiLCJERUxFVEUgL3RyYW5zZm9ybWF0aW9ucyIsIlBPU1QgL2NhdGFsb2ciLCJHRVQgL2NhdGFsb2ciLCJQVVQgL2NhdGFsb2ciLCJERUxFVEUgL2NhdGFsb2ciLCJQT1NUIC9hdXRoIiwiR0VUIC9tZXRhYmFzZSIsIkdFVCAvc25vd2ZsYWtlIl0sImN1c3RvbWVyIjoiZGFkb3NmZXJhIn0sImlhdCI6MTY1MzY1NjE2NywiZXhwIjoxNjUzNzQyNTY3fQ.eeNEEUk_95KOxM-objS7gXz-SCVkNFYpEP688MfLkdI';
|
||||
// mockRequest.headers['Authorization'] =
|
||||
// 'eyJraWQiOiJ1KzFXOXBpK2NscDhMYVBoWlZydjRkWE1xUmtUUkQwMllXaGJtME52c1V3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiIxYjkyZmFkMC1iNWVlLTQwMzAtOGRmYy1hZWM0MGQzYzdkYmUiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV9OVXY3WTJTeGoiLCJjbGllbnRfaWQiOiI0N3RrY3Jxc2NuajY3bWhnYmg3dXQ1YnJzNCIsIm9yaWdpbl9qdGkiOiJhZWJjNjA5NC1hYTJmLTRiZmUtOWExZi01ZWVhYTVmYzAwNDAiLCJldmVudF9pZCI6IjBhY2NjMzJiLTllNzktNGY2NS1iMDFiLWZhZGJlYzFhZWFiNiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE2NTM2NTYxNjcsImV4cCI6MTY1MzY1Nzk2NywiaWF0IjoxNjUzNjU2MTY3LCJqdGkiOiIyYTgzNDhmOC0xNDhlLTQ3NjctODcxYi00M2UyOGRjNjc2NTkiLCJ1c2VybmFtZSI6InJvZHJpZ28uemFtYm9uaUBkYWRvc2ZlcmEuYWkifQ.UgIgeGEH2olNgqPly8cymNgWIZJz5n9N4yeKgTusfGsu5h-TWP_jVhPoCvFoixO2XKzFjSCvKwlKQYZyB74jLQLs-j5C5KGBdOea1HX7pUnEfEVtBINd1kcHib5YpGYq3MHG1uVx8vNJS3XviwgYg4rgNWA4du-QbhMicdRyHolYM-dWxuxtkwTRWMe1Vw6KlTctFsvUWx1GMgjp37tsLONcp3B8OsYXfiR764K_pBNs5gMhJ39gJ7NHHLWkJrtrUTpIoHWaZS_HEgvVyQiJENQvK_bPDMPm_lyF1dW-JBgot4TpAxMmvV1XGabkzycIcAOwUaPsKBlMphgunz1Ffw';
|
||||
// mockRequest.method = 'GET';
|
||||
// mockRequest.route = {
|
||||
// path: '/inputs/',
|
||||
// stack: [
|
||||
// {
|
||||
// method: 'get',
|
||||
// },
|
||||
// ],
|
||||
// methods: {
|
||||
// get: true,
|
||||
// },
|
||||
// };
|
||||
// const authMiddleware = new LoggerMiddleware();
|
||||
|
||||
// await authMiddleware.useTest(
|
||||
// mockRequest as Request,
|
||||
// mockResponse as Response,
|
||||
// nextFunction,
|
||||
// );
|
||||
// expect(nextFunction).toHaveBeenCalled();
|
||||
// });
|
||||
|
||||
// it('Should not be able to pass auth', async () => {
|
||||
// const awsRegion = process.env.AWS_REGION;
|
||||
// const awsPoolId = process.env.AWS_IDENTITY_POOL_ID;
|
||||
// const cognitoRes = {
|
||||
// keys: [
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'z3VA3+i9JZY3JFDJTNWOap8RY+B7C6mFedaqCuvLvqA=',
|
||||
// kty: 'RSA',
|
||||
// n: 'vIGCQDkPA_eQUoaGDUsCyK_Whr76mNW0kZ1uac6ZnyY6hsjUIjvwehqv-ux3cQo4rQZQVFcoh8n9cK5jguz4GVdD972vIxoEdyv32nBFVr5e1PBunKJ2Y32GTR_Hl0XiE0hRe1v6cWuTqiC4qm1NY7tXYL3mI9L6s9ztNbhmG_V44y26PdhL4vRVrJaHOAmCs-77U-QYAC7Llkpmjh-8tG1zt9_FJ237cpBUVOuhD-7Nm32_eB9wddBGfBw0F10ko_KJoU-7683_Kv8k9coJzFUSONId-bfnLOzs8j1L6ZAHipXCR7rpmuRYzXztjp4Wmm2zPUToWLdMEy0Hq1OWmw',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'u+1W9pi+clp8LaPhZVrv4dXMqRkTRD02YWhbm0NvsUw=',
|
||||
// kty: 'RSA',
|
||||
// n: 'noGq1cRMAKJPWahqfC_zWasYovSUycaS1basMfEoh3ePLc9zRgmyfiVKYzLRosHMe1uk0Y5ekCBKnWA8Yl7I84Yt7IIIaE44oJjSEGBKT3m8i8YaXzawaNs63KPkRh8553o3KzL75bQWcI_ABKqkf-uAKSCl_XotBGkzLUl4hIOYtAtRGEfKaNMPqyTCT5Zn71pMd0isppaUiTW2T5QLsZV1IBp46aSrl_D5Q_FTsJT7feobQVoHp2zfIorCpkfTXBTBMQTEBFSDyPbc6cULl48VKxp0B0GEwR_kYCEHEVzf41LQcUWZUE0OdBychijkSc9MZJnBWUYQZyedJILWnQ',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// nock(
|
||||
// `https://cognito-idp.${awsRegion}.amazonaws.com/${awsPoolId}/.well-known/jwks.json`,
|
||||
// )
|
||||
// .persist()
|
||||
// .get('')
|
||||
// .reply(200, cognitoRes);
|
||||
|
||||
// mockRequest.headers['Dadosfera-User'] =
|
||||
// 'asdeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiZTljYjI1ZGEtMTQyOC00NDJjLTg5NjItYTdkNmQxNjhkODUyIiwibmFtZSI6InJvZHJpZ28uemFtYm9uaSIsInVzZXJuYW1lIjoicm9kcmlnby56YW1ib25pQGRhZG9zZmVyYS5haSIsInJ1bGVJZCI6IjIxNzdmZjg4LThmY2ItNDRlNS1hYzYxLWNjYmU2Y2M3MGU4ZCIsImN1c3RvbWVySWQiOiIxMTI5ODBhMy0wYTEyLTQxYzktYmZmZC03OTFjZjdjYTg3YTIiLCJtZmFTdGF0dXMiOiJwZW5kaW5nIiwiY3JlYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwidXBkYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwicGVybWlzc2lvbnMiOlsiUE9TVCAvcGlwZWxpbmVzIiwiR0VUIC9waXBlbGluZXMiLCJQVVQgL3BpcGVsaW5lcyIsIkRFTEVURSAvcGlwZWxpbmVzIiwiUE9TVCAvaW5wdXRzIiwiR0VUIC9pbnB1dHMiLCJQVVQgL2lucHV0cyIsIkRFTEVURSAvaW5wdXRzIiwiUE9TVCAvb3V0cHV0cyIsIkdFVCAvb3V0cHV0cyIsIlBVVCAvb3V0cHV0cyIsIkRFTEVURSAvb3V0cHV0cyIsIlBPU1QgL3RyYW5zZm9ybWF0aW9ucyIsIkdFVCAvdHJhbnNmb3JtYXRpb25zIiwiUFVUIC90cmFuc2Zvcm1hdGlvbnMiLCJERUxFVEUgL3RyYW5zZm9ybWF0aW9ucyIsIlBPU1QgL2NhdGFsb2ciLCJHRVQgL2NhdGFsb2ciLCJQVVQgL2NhdGFsb2ciLCJERUxFVEUgL2NhdGFsb2ciLCJQT1NUIC9hdXRoIiwiR0VUIC9tZXRhYmFzZSIsIkdFVCAvc25vd2ZsYWtlIl0sImN1c3RvbWVyIjoiZGFkb3NmZXJhIn0sImlhdCI6MTY1MzY1NjE2NywiZXhwIjoxNjUzNzQyNTY3fQ.eeNEEUk_95KOxM-objS7gXz-SCVkNFYpEP688MfLkdI';
|
||||
// mockRequest.headers['Authorization'] =
|
||||
// 'asdeyJraWQiOiJ1KzFXOXBpK2NscDhMYVBoWlZydjRkWE1xUmtUUkQwMllXaGJtME52c1V3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiIxYjkyZmFkMC1iNWVlLTQwMzAtOGRmYy1hZWM0MGQzYzdkYmUiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV9OVXY3WTJTeGoiLCJjbGllbnRfaWQiOiI0N3RrY3Jxc2NuajY3bWhnYmg3dXQ1YnJzNCIsIm9yaWdpbl9qdGkiOiJhZWJjNjA5NC1hYTJmLTRiZmUtOWExZi01ZWVhYTVmYzAwNDAiLCJldmVudF9pZCI6IjBhY2NjMzJiLTllNzktNGY2NS1iMDFiLWZhZGJlYzFhZWFiNiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE2NTM2NTYxNjcsImV4cCI6MTY1MzY1Nzk2NywiaWF0IjoxNjUzNjU2MTY3LCJqdGkiOiIyYTgzNDhmOC0xNDhlLTQ3NjctODcxYi00M2UyOGRjNjc2NTkiLCJ1c2VybmFtZSI6InJvZHJpZ28uemFtYm9uaUBkYWRvc2ZlcmEuYWkifQ.UgIgeGEH2olNgqPly8cymNgWIZJz5n9N4yeKgTusfGsu5h-TWP_jVhPoCvFoixO2XKzFjSCvKwlKQYZyB74jLQLs-j5C5KGBdOea1HX7pUnEfEVtBINd1kcHib5YpGYq3MHG1uVx8vNJS3XviwgYg4rgNWA4du-QbhMicdRyHolYM-dWxuxtkwTRWMe1Vw6KlTctFsvUWx1GMgjp37tsLONcp3B8OsYXfiR764K_pBNs5gMhJ39gJ7NHHLWkJrtrUTpIoHWaZS_HEgvVyQiJENQvK_bPDMPm_lyF1dW-JBgot4TpAxMmvV1XGabkzycIcAOwUaPsKBlMphgunz1Ffw';
|
||||
// mockRequest.method = 'GET';
|
||||
// mockRequest.route = {
|
||||
// path: '/inputs/',
|
||||
// stack: [
|
||||
// {
|
||||
// method: 'get',
|
||||
// },
|
||||
// ],
|
||||
// methods: {
|
||||
// get: true,
|
||||
// },
|
||||
// };
|
||||
// const authMiddleware = new LoggerMiddleware();
|
||||
|
||||
// // const t = await authMiddleware.useTest(
|
||||
// // mockRequest as Request,
|
||||
// // mockResponse as Response,
|
||||
// // nextFunction,
|
||||
// // );
|
||||
// // expect(nextFunction).toHaveBeenCalled();
|
||||
|
||||
// expect(async () => {
|
||||
// const t = await authMiddleware.useTest(
|
||||
// mockRequest as Request,
|
||||
// mockResponse as Response,
|
||||
// nextFunction,
|
||||
// );
|
||||
// console.log(t);
|
||||
// }).toThrow('Unauthorized');
|
||||
|
||||
// expect(1).toBe(2);
|
||||
// // expect(t).toThrow(UnauthorizedException);
|
||||
// });
|
||||
});
|
||||
@@ -7,9 +7,31 @@ import {
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
import { CatalogService } from './catalog.service';
|
||||
|
||||
@ApiTags('Catalog')
|
||||
@Controller('catalog')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(Permissions.CATALOG[action].seqid);
|
||||
})
|
||||
export class CatalogController {
|
||||
constructor(private catalogService: CatalogService) {}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { HealthService } from './health.service';
|
||||
|
||||
@ApiTags('Health')
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
constructor(private readonly healthService: HealthService) {}
|
||||
|
||||
@@ -10,6 +10,9 @@ import {
|
||||
import { InputsService } from './inputs.service';
|
||||
import { InputsClientService } from 'src/clients/inputs/client.service';
|
||||
import { UpdateInputRequest } from 'src/clients/inputs/interfaces';
|
||||
import { InputNewCreateRequest } from '@victorradael/protospack';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
CreateInputReq,
|
||||
@@ -24,6 +27,35 @@ import {
|
||||
|
||||
@ApiTags('inputs')
|
||||
@Controller('inputs')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
case 'PATCH':
|
||||
action = 'UPDATE_PARTIAL';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
if (
|
||||
`${req.method} ${req.route.path}` ===
|
||||
'GET /inputs/available-entities/:plugin'
|
||||
) {
|
||||
action = 'GET_ENTITIES';
|
||||
}
|
||||
|
||||
return user.permissions.includes(Permissions.INPUT[action].seqid);
|
||||
})
|
||||
export class InputsController {
|
||||
inputService: InputsService;
|
||||
constructor(private inputsClientService: InputsClientService) {
|
||||
|
||||
@@ -8,12 +8,34 @@ import {
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { Payload } from '@nestjs/microservices';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { OutputsClientService } from 'src/clients/outputs/client.service';
|
||||
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
import { OutputsService } from './outputs.service';
|
||||
|
||||
@ApiTags('Outputs')
|
||||
@Controller('outputs')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(Permissions.OUTPUT[action].seqid);
|
||||
})
|
||||
export class OutputsController {
|
||||
constructor(private outputsClientService: OutputsClientService) {}
|
||||
constructor(private outputsClientService: OutputsClientService) { }
|
||||
|
||||
@Post()
|
||||
async create(@Body() createOutputDto) {
|
||||
|
||||
@@ -7,10 +7,32 @@ import {
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { PipelinesClientService } from 'src/clients/pipelines/client.service';
|
||||
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
import { PipelinesService } from './pipelines.service';
|
||||
|
||||
@ApiTags('Pipelines')
|
||||
@Controller('pipelines')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(Permissions.PIPELINE[action].seqid);
|
||||
})
|
||||
export class PipelinesController {
|
||||
constructor(private pipelinesClientService: PipelinesClientService) {}
|
||||
|
||||
|
||||
@@ -8,11 +8,33 @@ import {
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { Payload } from '@nestjs/microservices';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { TransformationsClientService } from 'src/clients/transformations/client.service';
|
||||
import { IIdRequest } from 'src/clients/transformations/interfaces';
|
||||
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
import { TransformationsService } from './transformations.service';
|
||||
|
||||
@ApiTags('Transformations')
|
||||
@Controller('transformations')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(Permissions.TRANSFORMATIONS[action].seqid);
|
||||
})
|
||||
export class TransformationsController {
|
||||
constructor(
|
||||
private transformationsClientService: TransformationsClientService,
|
||||
|
||||
+50
-36
@@ -1,131 +1,145 @@
|
||||
import { HttpStatus, HttpException } from '@nestjs/common';
|
||||
import { HttpStatus, HttpException, Logger } from '@nestjs/common';
|
||||
import { RpcException } from '@nestjs/microservices';
|
||||
|
||||
import ErrorCodes from './errorCodes';
|
||||
|
||||
function Builder({ statusCode, message, error, code }) {
|
||||
return new HttpException({ statusCode, message, error, code }, statusCode);
|
||||
}
|
||||
|
||||
export default function ErrorBuilder(code: string) {
|
||||
console.log(code);
|
||||
|
||||
function enrichErrorCode(code: string) {
|
||||
switch (code) {
|
||||
case ErrorCodes.AUTH.WRONG_CREDENTIALS:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'Usuário ou senha incorretos',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.WRONG_PASSWORD_CONFIRMATION:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Senha incorreta',
|
||||
message: 'Confirmação de senha incorreta',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_NOT_ENABLED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.PRECONDITION_FAILED,
|
||||
error: 'Não permitido',
|
||||
message: 'A autenticação multifator não está habilitada',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_ALREADY_ENABLED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.PRECONDITION_FAILED,
|
||||
error: 'Não permitido',
|
||||
message: 'A autenticação multifator já está habilitada',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_ALREADY_DISABLED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.PRECONDITION_FAILED,
|
||||
error: 'Não permitido',
|
||||
message: 'A autenticação multifator já está desabilitada',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_REQUIRED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'Informe o token de autenticação multifator',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.CODE_MISMATCH:
|
||||
case ErrorCodes.AUTH.CODE_ALREADY_USED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'Token de autenticação multifator inválido',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.RESET_PASSWORD_CODE_EXPIRED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não permitido',
|
||||
message: 'Token para recuperar senha expirado',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.RESET_PASSWORD_CODE_INVALID:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não permitido',
|
||||
message: 'Token para recuperar senha inválido',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.WEAK_NEW_PASSWORD:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
error: 'Não permitido',
|
||||
message: 'Senha muito fraca. Escolha uma senha mais forte',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.RATE_LIMIT:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.TOO_MANY_REQUESTS,
|
||||
error: 'Limite excedido',
|
||||
message:
|
||||
'Você tentou realizar essa operação muitas vezes. Tente novamente mais tarde',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.UNAUTHORIZED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'É necessário estar logado para realizar essa operação',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.FORBIDDEN:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.FORBIDDEN,
|
||||
error: 'Não autorizado',
|
||||
message:
|
||||
'Você não tem permissões suficientes para realizar essa operação',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.INTERNAL:
|
||||
case ErrorCodes.UNKNOWN:
|
||||
default:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
error: 'Desconhecido',
|
||||
message:
|
||||
'Erro desconhecido. Tente novamente ou entre em contato com o suporte',
|
||||
code: ErrorCodes.UNKNOWN,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const logger = new Logger();
|
||||
|
||||
export default class ErrorBuilder extends HttpException {
|
||||
code: string;
|
||||
|
||||
constructor(code: string | RpcException) {
|
||||
if (typeof code !== 'string') {
|
||||
logger.log(code.stack);
|
||||
code = (code as any).details as string;
|
||||
} else {
|
||||
logger.log(code);
|
||||
}
|
||||
|
||||
const { statusCode, message, error, code: rCode } = enrichErrorCode(code);
|
||||
super({ statusCode, message, error, code: rCode }, statusCode);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { RpcException } from '@nestjs/microservices';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
import ErrorBuilder from './ErrorBuilder';
|
||||
|
||||
const logger = new Logger();
|
||||
|
||||
export default async function grpcHandler<T>(method: Promise<T>) {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
from(method).subscribe({
|
||||
next: resolve,
|
||||
error: reject,
|
||||
complete: () => logger.log('done'),
|
||||
});
|
||||
}).catch((err: RpcException) => {
|
||||
throw new ErrorBuilder(err);
|
||||
});
|
||||
}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user