From 8043635b1cb89c0c8a70889bb440a02615e646ce Mon Sep 17 00:00:00 2001 From: Gabriel Rosa Date: Wed, 8 Feb 2023 15:23:49 -0300 Subject: [PATCH] FIX: add authentication to swagger --- docsfera.json | 616 +++++++++++++++++- .../authentication.decorator.ts | 22 +- src/authentication/authentication.guard.ts | 7 +- src/main.ts | 9 +- src/modules/auth/auth.controller.ts | 3 +- src/modules/catalog/catalog.controller.ts | 1 - .../connection/connection.controller.ts | 3 +- src/modules/connector/connector.controller.ts | 3 +- .../productboard/productboard.controller.ts | 2 + 9 files changed, 621 insertions(+), 45 deletions(-) diff --git a/docsfera.json b/docsfera.json index 4a9cad2..ca5784f 100644 --- a/docsfera.json +++ b/docsfera.json @@ -63,6 +63,11 @@ }, "tags": [ "Auth" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -285,6 +290,14 @@ }, "tags": [ "Auth" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -319,7 +332,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] }, @@ -369,7 +385,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -398,7 +417,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -417,7 +439,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -443,7 +468,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -479,7 +507,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] }, @@ -515,7 +546,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] }, @@ -549,7 +583,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -585,7 +622,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -623,7 +663,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -661,7 +704,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -698,6 +744,11 @@ }, "tags": [ "Permissions" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -775,7 +826,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] } @@ -811,7 +865,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] }, @@ -835,7 +892,7 @@ ], "security": [ { - "bearer": [] + "access-token": [] } ] } @@ -888,7 +945,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] }, @@ -921,7 +981,10 @@ ], "security": [ { - "bearer": [] + "access-token": [] + }, + { + "access-token": [] } ] }, @@ -945,7 +1008,7 @@ ], "security": [ { - "bearer": [] + "access-token": [] } ] } @@ -982,6 +1045,11 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + } ] }, "post": { @@ -1025,6 +1093,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1060,6 +1136,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1095,6 +1179,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1130,6 +1222,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1173,6 +1273,11 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + } ] }, "delete": { @@ -1214,6 +1319,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "patch": { @@ -1265,6 +1378,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1310,6 +1431,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1353,6 +1482,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1404,6 +1541,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "put": { @@ -1447,6 +1592,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1492,6 +1645,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1545,6 +1706,14 @@ }, "tags": [ "Users" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1614,6 +1783,11 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + } ] }, "post": { @@ -1657,6 +1831,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1702,6 +1884,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "delete": { @@ -1745,6 +1935,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "patch": { @@ -1788,6 +1986,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1833,6 +2039,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1876,6 +2090,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "delete": { @@ -1917,6 +2139,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "patch": { @@ -1968,6 +2198,14 @@ }, "tags": [ "Roles" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -1989,6 +2227,11 @@ }, "tags": [ "Network Config" + ], + "security": [ + { + "access-token": [] + } ] }, "post": { @@ -2028,6 +2271,14 @@ }, "tags": [ "Network Config" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2058,6 +2309,11 @@ }, "tags": [ "Network Config" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2088,6 +2344,11 @@ }, "tags": [ "Network Config" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2119,6 +2380,11 @@ }, "tags": [ "Connection Test" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2150,6 +2416,11 @@ }, "tags": [ "Connection Test" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2181,6 +2452,11 @@ }, "tags": [ "Connection Test" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2212,6 +2488,11 @@ }, "tags": [ "Connection Test" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2243,6 +2524,11 @@ }, "tags": [ "Connection Test" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2264,6 +2550,14 @@ }, "tags": [ "Pipelines" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2285,6 +2579,14 @@ }, "tags": [ "Pipelines" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2316,6 +2618,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] }, "get": { @@ -2328,6 +2635,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2342,6 +2654,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2356,6 +2673,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2370,6 +2692,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2391,6 +2718,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] }, "put": { @@ -2410,6 +2742,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] }, "patch": { @@ -2429,6 +2766,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] }, "delete": { @@ -2441,6 +2783,11 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2465,6 +2812,14 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2496,6 +2851,14 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2527,6 +2890,14 @@ }, "tags": [ "PipelinesV2" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2557,6 +2928,11 @@ }, "tags": [ "Inputs" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2591,6 +2967,11 @@ }, "tags": [ "Inputs" + ], + "security": [ + { + "access-token": [] + } ] }, "get": { @@ -2603,6 +2984,11 @@ }, "tags": [ "Inputs" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2624,6 +3010,11 @@ }, "tags": [ "Inputs" + ], + "security": [ + { + "access-token": [] + } ] }, "patch": { @@ -2643,6 +3034,11 @@ }, "tags": [ "Inputs" + ], + "security": [ + { + "access-token": [] + } ] }, "delete": { @@ -2662,6 +3058,11 @@ }, "tags": [ "Inputs" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2676,6 +3077,14 @@ }, "tags": [ "Transformations" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "get": { @@ -2688,6 +3097,14 @@ }, "tags": [ "Transformations" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2709,6 +3126,14 @@ }, "tags": [ "Transformations" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "put": { @@ -2737,6 +3162,14 @@ }, "tags": [ "Transformations" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "delete": { @@ -2756,6 +3189,14 @@ }, "tags": [ "Transformations" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2791,6 +3232,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "post": { @@ -2813,6 +3262,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2827,6 +3284,11 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -2848,6 +3310,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2871,6 +3341,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "put": { @@ -2900,6 +3378,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "delete": { @@ -2928,6 +3414,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2942,6 +3436,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2963,6 +3465,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -2984,6 +3494,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -3005,6 +3523,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "post": { @@ -3033,6 +3559,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -3056,6 +3590,11 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -3079,6 +3618,11 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -3119,6 +3663,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] }, "delete": { @@ -3157,6 +3709,14 @@ }, "tags": [ "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } ] } }, @@ -3398,6 +3958,11 @@ }, "tags": [ "Productboard" + ], + "security": [ + { + "access-token": [] + } ] } }, @@ -3409,12 +3974,17 @@ "201": { "description": "" } - } + }, + "security": [ + { + "access-token": [] + } + ] } } }, "info": { - "title": "Maestro - feat/embed-private", + "title": "Maestro - main", "description": "Documentation for Maestro gateway", "version": "1.0.0", "contact": {} @@ -3423,10 +3993,10 @@ "servers": [], "components": { "securitySchemes": { - "Authorization": { - "scheme": "bearer", - "bearerFormat": "JWT", - "type": "http" + "access-token": { + "type": "apiKey", + "name": "Authorization", + "in": "header" } }, "schemas": { diff --git a/src/authentication/authentication.decorator.ts b/src/authentication/authentication.decorator.ts index 5bebd81..2016872 100644 --- a/src/authentication/authentication.decorator.ts +++ b/src/authentication/authentication.decorator.ts @@ -1,7 +1,8 @@ import { Request } from 'express'; -import { CustomDecorator } from '@nestjs/common'; +import { applyDecorators, CustomDecorator } from '@nestjs/common'; import { PermissionsObjectPermission } from './permissions.enum'; import { RequestUser } from './user.decorator'; +import { ApiSecurity } from '@nestjs/swagger'; export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__'; @@ -10,7 +11,7 @@ export type AuthenticationFunction = (req: Request, user: any) => boolean; // implementation copied from SetMetadata, but tweaked to get existing values // of the metadataKey and accumulate it with the new metadataValue function SetMultipleMetadata(metadataKey, metadataValue): CustomDecorator { - const decoratorFactory = (target, key, descriptor) => { + const decoratorFactory: CustomDecorator = (target, key?, descriptor?) => { // .start: tweak // descriptor?.value = function decorator; target = class decorator const accumulatedVal = @@ -28,13 +29,20 @@ function SetMultipleMetadata(metadataKey, metadataValue): CustomDecorator { }; decoratorFactory.KEY = metadataKey; - return decoratorFactory as any; + return decoratorFactory; +} + +function createAuthenticatedDecorator(metadataValue: AuthenticationFunction) { + return applyDecorators( + ApiSecurity('access-token'), + SetMultipleMetadata(AUTH_FUNCTION_KEY, metadataValue), + ); } export function RequireAllPermissions( ...permissions: PermissionsObjectPermission[] ) { - return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) => + return createAuthenticatedDecorator((req, user: RequestUser) => permissions.every(({ seqid }) => user.permissions.includes(seqid)), ); } @@ -42,15 +50,15 @@ export function RequireAllPermissions( export function RequireSomePermission( ...permissions: PermissionsObjectPermission[] ) { - return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) => + return createAuthenticatedDecorator((req, user: RequestUser) => permissions.some(({ seqid }) => user.permissions.includes(seqid)), ); } export function AuthenticateCondition(func: AuthenticationFunction) { - return SetMultipleMetadata(AUTH_FUNCTION_KEY, func); + return createAuthenticatedDecorator(func); } export function Authenticated() { - return SetMultipleMetadata(AUTH_FUNCTION_KEY, () => true); + return createAuthenticatedDecorator(() => true); } diff --git a/src/authentication/authentication.guard.ts b/src/authentication/authentication.guard.ts index 44b3a75..07125b1 100644 --- a/src/authentication/authentication.guard.ts +++ b/src/authentication/authentication.guard.ts @@ -54,14 +54,11 @@ export class AuthenticationGuard >(AUTH_FUNCTION_KEY, [ctx.getClass(), ctx.getHandler()]); const mustBeAuthenticated = authFunctions.length > 0; + if (!mustBeAuthenticated) return true; + const request = ctx.switchToHttp().getRequest(); const accessToken = this.validateToken(request, mustBeAuthenticated); - if (!mustBeAuthenticated) { - // no need to be authenticated - return true; - } - if (!accessToken) { // couldn't load valid token throw new ErrorBuilder(ErrorCodes.AUTH.UNAUTHORIZED); diff --git a/src/main.ts b/src/main.ts index 7235e3b..c3bf5da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,10 +46,11 @@ function configureSwagger(app: INestApplication) { const config = new DocumentBuilder() .setTitle(swaggerTitle) .setDescription('Documentation for Maestro gateway') - .addBearerAuth( - { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }, - 'Authorization', - ) + .addSecurity('access-token', { + type: 'apiKey', + name: 'Authorization', + in: 'header', + }) .build(); const document = SwaggerModule.createDocument(app, config); diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 2bf3915..9739012 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -9,7 +9,7 @@ import { UseFilters, Get, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiSecurity, ApiTags } from '@nestjs/swagger'; import { AuthChangePasswordRequest, AuthResetPasswordRequest, @@ -203,6 +203,7 @@ export class AuthController { } @Authenticated() + @ApiSecurity('access-token') @Get('verify-access-token') @HttpCode(HttpStatus.OK) verifyAccessToken() { diff --git a/src/modules/catalog/catalog.controller.ts b/src/modules/catalog/catalog.controller.ts index 71f69f3..3a29107 100644 --- a/src/modules/catalog/catalog.controller.ts +++ b/src/modules/catalog/catalog.controller.ts @@ -6,7 +6,6 @@ import { ForbiddenException, Get, Headers, - HttpException, Inject, NotFoundException, Param, diff --git a/src/modules/connection/connection.controller.ts b/src/modules/connection/connection.controller.ts index 2aa47cf..67d5c3e 100644 --- a/src/modules/connection/connection.controller.ts +++ b/src/modules/connection/connection.controller.ts @@ -11,7 +11,7 @@ import { Query, UseFilters, } from '@nestjs/common'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { ConnectionClientService } from './client.service'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { @@ -34,7 +34,6 @@ import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filt const connectionPermissions = PERMISSIONS_GROUPS.CONNECTION.permissions; @UseFilters(new GrpcToHttpExceptionFilter()) @ApiTags('connections') -@ApiBearerAuth() @Authenticated() @Controller('connections') export class ConnectionController { diff --git a/src/modules/connector/connector.controller.ts b/src/modules/connector/connector.controller.ts index c95ff54..ba2116f 100644 --- a/src/modules/connector/connector.controller.ts +++ b/src/modules/connector/connector.controller.ts @@ -15,7 +15,7 @@ import { UploadedFiles, } from '@nestjs/common'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; -import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger'; +import { ApiConsumes, ApiTags } from '@nestjs/swagger'; import { ConnectorClientService } from './client.service'; import { AddTagDto } from './dtos/add-tag'; import { CreateConnectorDto } from './dtos/create-connector'; @@ -31,7 +31,6 @@ import { import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum'; @ApiTags('connectors') -@ApiBearerAuth() @Authenticated() @Controller('connectors') export class ConnectorController { diff --git a/src/modules/productboard/productboard.controller.ts b/src/modules/productboard/productboard.controller.ts index 55327df..16e47c3 100644 --- a/src/modules/productboard/productboard.controller.ts +++ b/src/modules/productboard/productboard.controller.ts @@ -1,12 +1,14 @@ import { Body, Controller, HttpException, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import axios from 'axios'; +import { Authenticated } from 'src/authentication/authentication.decorator'; import { User, RequestUser } from 'src/authentication/user.decorator'; import { getSecretFromSecretsManager } from 'src/utils/SecretManager'; import { INote } from './dtos'; @ApiTags('Productboard') @Controller('productboard') +@Authenticated() export class ProductboardController { @Post('notes') async sendNote(@Body() note: INote, @User() user: RequestUser) {