From 0e450cf7cc5c1e196c9e448c93b87aeead12ddd8 Mon Sep 17 00:00:00 2001 From: Frederik Christ Vestergaard Date: Thu, 14 Dec 2023 12:12:30 +0100 Subject: [PATCH] Created custom decorator for swagger ApiBearerAuth and ApiSecurity (api key) in one. Used on controllers --- src/auth/swagger-auth-decorator.ts | 6 + .../application.controller.ts | 86 ++------- .../chirpstack-gateway.controller.ts | 7 +- .../chirpstack/device-profile.controller.ts | 53 ++---- .../chirpstack/network-server.controller.ts | 21 +-- .../chirpstack/service-profile.controller.ts | 54 ++---- .../data-target.controller.ts | 111 +++--------- .../device-model.controller.ts | 30 +--- ...coder-data-target-connection.controller.ts | 73 ++------ .../iot-device-payload-decoder.controller.ts | 24 +-- .../admin-controller/iot-device.controller.ts | 170 ++++-------------- .../lorawan/lorawan-gateway.controller.ts | 17 +- .../admin-controller/multicast.controller.ts | 111 +++--------- .../payload-decoder.controller.ts | 38 ++-- .../admin-controller/search.controller.ts | 24 +-- .../sigfox/sigfox-api-contract.controller.ts | 22 +-- .../sigfox/sigfox-api-device.controller.ts | 23 +-- .../sigfox/sigfox-device-type.controller.ts | 62 ++----- .../sigfox/sigfox-group.controller.ts | 42 ++--- .../api-key/api-key-info.controller.ts | 26 +-- src/controllers/api-key/api-key.controller.ts | 62 ++----- .../user-management/auth.controller.ts | 54 ++---- .../new-kombit-creation.controller.ts | 52 ++---- .../organization.controller.ts | 44 +---- .../user-management/permission.controller.ts | 73 ++------ .../user-management/user.controller.ts | 83 ++------- src/loaders/swagger.ts | 1 + src/services/user-management/auth.service.ts | 130 ++++++-------- 28 files changed, 387 insertions(+), 1112 deletions(-) create mode 100644 src/auth/swagger-auth-decorator.ts diff --git a/src/auth/swagger-auth-decorator.ts b/src/auth/swagger-auth-decorator.ts new file mode 100644 index 00000000..cc5938fa --- /dev/null +++ b/src/auth/swagger-auth-decorator.ts @@ -0,0 +1,6 @@ +import { applyDecorators } from "@nestjs/common"; +import { ApiBearerAuth, ApiSecurity } from "@nestjs/swagger"; + +export function ApiAuth() { + return applyDecorators(ApiBearerAuth(), ApiSecurity("X-API-KEY")); +} diff --git a/src/controllers/admin-controller/application.controller.ts b/src/controllers/admin-controller/application.controller.ts index c5ac1c18..f526c175 100644 --- a/src/controllers/admin-controller/application.controller.ts +++ b/src/controllers/admin-controller/application.controller.ts @@ -1,5 +1,5 @@ -import { BadRequestException } from "@nestjs/common"; import { + BadRequestException, Body, Controller, Delete, @@ -17,17 +17,16 @@ import { } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, ApiProduces, + ApiResponse, ApiTags, ApiUnauthorizedResponse, } from "@nestjs/swagger"; -import { ApiResponse } from "@nestjs/swagger"; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreateApplicationDto } from "@dto/create-application.dto"; import { DeleteResponseDto } from "@dto/delete-application-response.dto"; @@ -49,11 +48,12 @@ import { ActionType } from "@entities/audit-log-entry"; import { ListAllEntitiesDto } from "@dto/list-all-entities.dto"; import { ListAllIoTDevicesResponseDto } from "@dto/list-all-iot-devices-response.dto"; import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Application") @Controller("application") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @@ -85,10 +85,7 @@ export class ApplicationController { return await this.getApplicationsForNonGlobalAdmin(req, query); } - private async getApplicationsForNonGlobalAdmin( - req: AuthenticatedRequest, - query: ListAllApplicationsDto - ) { + private async getApplicationsForNonGlobalAdmin(req: AuthenticatedRequest, query: ListAllApplicationsDto) { if (query?.organizationId) { checkIfUserHasAccessToOrganization(req, query.organizationId, OrganizationAccessScope.ApplicationRead); return await this.getApplicationsInOrganization(req, query); @@ -104,34 +101,22 @@ export class ApplicationController { return applications; } - private async getApplicationsInOrganization( - req: AuthenticatedRequest, - query: ListAllApplicationsDto - ) { + private async getApplicationsInOrganization(req: AuthenticatedRequest, query: ListAllApplicationsDto) { // User admins have access to all applications in the organization const allFromOrg = req.user.permissions.getAllOrganizationsWithUserAdmin(); if (allFromOrg.some(x => x === query?.organizationId)) { - return await this.applicationService.findAndCountWithPagination(query, [ - query.organizationId, - ]); + return await this.applicationService.findAndCountWithPagination(query, [query.organizationId]); } const allowedApplications = req.user.permissions.getAllApplicationsWithAtLeastRead(); - return await this.applicationService.findAndCountInList( - query, - allowedApplications, - [query.organizationId] - ); + return await this.applicationService.findAndCountInList(query, allowedApplications, [query.organizationId]); } @Read() @Get(":id") @ApiOperation({ summary: "Find one Application by id" }) @ApiNotFoundResponse() - async findOne( - @Req() req: AuthenticatedRequest, - @Param("id", new ParseIntPipe()) id: number - ): Promise { + async findOne(@Req() req: AuthenticatedRequest, @Param("id", new ParseIntPipe()) id: number): Promise { checkIfUserHasAccessToApplication(req, id, ApplicationAccessScope.Read); try { @@ -153,10 +138,7 @@ export class ApplicationController { checkIfUserHasAccessToApplication(req, applicationId, ApplicationAccessScope.Read); try { - return await this.applicationService.findDevicesForApplication( - applicationId, - query - ); + return await this.applicationService.findDevicesForApplication(applicationId, query); } catch (err) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } @@ -177,29 +159,16 @@ export class ApplicationController { OrganizationAccessScope.ApplicationWrite ); - const isValid = await this.applicationService.isNameValidAndNotUsed( - createApplicationDto?.name - ); + const isValid = await this.applicationService.isNameValidAndNotUsed(createApplicationDto?.name); if (!isValid) { - this.logger.error( - `Tried to create an application with name: '${createApplicationDto.name}'` - ); + this.logger.error(`Tried to create an application with name: '${createApplicationDto.name}'`); AuditLog.fail(ActionType.CREATE, Application.name, req.user.userId); throw new BadRequestException(ErrorCodes.NameInvalidOrAlreadyInUse); } - const application = await this.applicationService.create( - createApplicationDto, - req.user.userId - ); - AuditLog.success( - ActionType.CREATE, - Application.name, - req.user.userId, - application.id, - application.name - ); + const application = await this.applicationService.create(createApplicationDto, req.user.userId); + AuditLog.success(ActionType.CREATE, Application.name, req.user.userId, application.id, application.name); return application; } @@ -214,32 +183,15 @@ export class ApplicationController { @Body() updateApplicationDto: UpdateApplicationDto ): Promise { checkIfUserHasAccessToApplication(req, id, ApplicationAccessScope.Write); - if ( - !(await this.applicationService.isNameValidAndNotUsed( - updateApplicationDto?.name, - id - )) - ) { - this.logger.error( - `Tried to change an application with name: '${updateApplicationDto.name}'` - ); + if (!(await this.applicationService.isNameValidAndNotUsed(updateApplicationDto?.name, id))) { + this.logger.error(`Tried to change an application with name: '${updateApplicationDto.name}'`); AuditLog.fail(ActionType.UPDATE, Application.name, req.user.userId); throw new BadRequestException(ErrorCodes.NameInvalidOrAlreadyInUse); } - const application = await this.applicationService.update( - id, - updateApplicationDto, - req.user.userId - ); + const application = await this.applicationService.update(id, updateApplicationDto, req.user.userId); - AuditLog.success( - ActionType.UPDATE, - Application.name, - req.user.userId, - application.id, - application.name - ); + AuditLog.success(ActionType.UPDATE, Application.name, req.user.userId, application.id, application.name); return application; } diff --git a/src/controllers/admin-controller/chirpstack/chirpstack-gateway.controller.ts b/src/controllers/admin-controller/chirpstack/chirpstack-gateway.controller.ts index b29f5066..d3c00236 100644 --- a/src/controllers/admin-controller/chirpstack/chirpstack-gateway.controller.ts +++ b/src/controllers/admin-controller/chirpstack/chirpstack-gateway.controller.ts @@ -12,9 +12,9 @@ import { Req, UseGuards, } from "@nestjs/common"; -import { ApiBadRequestResponse, ApiBearerAuth, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; +import { ApiBadRequestResponse, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; -import { Read, GatewayAdmin } from "@auth/roles.decorator"; +import { GatewayAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { ChirpstackResponseStatus } from "@dto/chirpstack/chirpstack-response.dto"; import { CreateGatewayDto } from "@dto/chirpstack/create-gateway.dto"; @@ -29,11 +29,12 @@ import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; import { ChirpstackGetAll } from "@dto/chirpstack/chirpstack-get-all.dto"; import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Chirpstack") @Controller("chirpstack/gateway") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() export class ChirpstackGatewayController { constructor(private chirpstackGatewayService: ChirpstackGatewayService) {} diff --git a/src/controllers/admin-controller/chirpstack/device-profile.controller.ts b/src/controllers/admin-controller/chirpstack/device-profile.controller.ts index e406db01..76169a00 100644 --- a/src/controllers/admin-controller/chirpstack/device-profile.controller.ts +++ b/src/controllers/admin-controller/chirpstack/device-profile.controller.ts @@ -15,16 +15,9 @@ import { Req, UseGuards, } from "@nestjs/common"; -import { - ApiBadRequestResponse, - ApiBearerAuth, - ApiNotFoundResponse, - ApiOperation, - ApiProduces, - ApiTags, -} from "@nestjs/swagger"; +import { ApiBadRequestResponse, ApiNotFoundResponse, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreateChirpstackProfileResponseDto } from "@dto/chirpstack/create-chirpstack-profile-response.dto"; import { CreateDeviceProfileDto } from "@dto/chirpstack/create-device-profile.dto"; @@ -38,11 +31,12 @@ import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@he import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Chirpstack") @Controller("chirpstack/device-profiles") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @ApplicationAdmin() export class DeviceProfileController { constructor(private deviceProfileService: DeviceProfileService) {} @@ -59,13 +53,14 @@ export class DeviceProfileController { @Req() req: AuthenticatedRequest, @Body() createDto: CreateDeviceProfileDto ): Promise { - checkIfUserHasAccessToOrganization(req, createDto.internalOrganizationId, OrganizationAccessScope.ApplicationWrite); + checkIfUserHasAccessToOrganization( + req, + createDto.internalOrganizationId, + OrganizationAccessScope.ApplicationWrite + ); try { - const result = await this.deviceProfileService.createDeviceProfile( - createDto, - req.user.userId - ); + const result = await this.deviceProfileService.createDeviceProfile(createDto, req.user.userId); AuditLog.success( ActionType.CREATE, @@ -160,14 +155,9 @@ export class DeviceProfileController { let result = undefined; try { this.logger.debug(`Limit: '${limit}' Offset:'${offset}'`); - result = await this.deviceProfileService.findAllDeviceProfiles( - limit || 50, - offset || 0 - ); + result = await this.deviceProfileService.findAllDeviceProfiles(limit || 50, offset || 0); } catch (err) { - this.logger.error( - `Error occured during Find all: '${JSON.stringify(err?.response?.data)}'` - ); + this.logger.error(`Error occured during Find all: '${JSON.stringify(err?.response?.data)}'`); } return result; } @@ -176,30 +166,17 @@ export class DeviceProfileController { @ApiOperation({ summary: "Delete one DeviceProfile by id" }) @ApiNotFoundResponse() @ApplicationAdmin() - async deleteOne( - @Req() req: AuthenticatedRequest, - @Param("id") id: string - ): Promise { + async deleteOne(@Req() req: AuthenticatedRequest, @Param("id") id: string): Promise { try { const result = await this.deviceProfileService.deleteDeviceProfile(id, req); if (!result) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } - AuditLog.success( - ActionType.DELETE, - "ChirpstackDeviceProfile", - req.user.userId, - id - ); + AuditLog.success(ActionType.DELETE, "ChirpstackDeviceProfile", req.user.userId, id); return new DeleteResponseDto(1); } catch (err) { - AuditLog.fail( - ActionType.DELETE, - "ChirpstackDeviceProfile", - req.user.userId, - id - ); + AuditLog.fail(ActionType.DELETE, "ChirpstackDeviceProfile", req.user.userId, id); if (err?.message == this.CHIRPSTACK_IN_USE_ERROR) { throw new BadRequestException(ErrorCodes.IsUsed); } diff --git a/src/controllers/admin-controller/chirpstack/network-server.controller.ts b/src/controllers/admin-controller/chirpstack/network-server.controller.ts index 4016f3a8..97ea0a60 100644 --- a/src/controllers/admin-controller/chirpstack/network-server.controller.ts +++ b/src/controllers/admin-controller/chirpstack/network-server.controller.ts @@ -1,26 +1,17 @@ -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; -import { - Controller, - Get, - Logger, - UseGuards, -} from "@nestjs/common"; -import { - ApiBearerAuth, - ApiOperation, - ApiProduces, - ApiTags, -} from "@nestjs/swagger"; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { Controller, Get, Logger, UseGuards } from "@nestjs/common"; +import { ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { ChirpstackSetupNetworkServerService } from "@services/chirpstack/network-server.service"; import { ListAllAdrAlgorithmsResponseDto } from "@dto/chirpstack/list-all-adr-algorithms-response.dto"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Chirpstack") @Controller("chirpstack/network-server") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @ApplicationAdmin() export class NetworkServerController { constructor(private networkServerService: ChirpstackSetupNetworkServerService) {} diff --git a/src/controllers/admin-controller/chirpstack/service-profile.controller.ts b/src/controllers/admin-controller/chirpstack/service-profile.controller.ts index 2f1b1412..d2302def 100644 --- a/src/controllers/admin-controller/chirpstack/service-profile.controller.ts +++ b/src/controllers/admin-controller/chirpstack/service-profile.controller.ts @@ -1,4 +1,4 @@ -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; import { BadRequestException, Body, @@ -16,16 +16,8 @@ import { Req, UseGuards, } from "@nestjs/common"; -import { - ApiBadRequestResponse, - ApiBearerAuth, - ApiNotFoundResponse, - ApiOperation, - ApiProduces, - ApiTags, -} from "@nestjs/swagger"; - -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApiBadRequestResponse, ApiNotFoundResponse, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreateChirpstackProfileResponseDto } from "@dto/chirpstack/create-chirpstack-profile-response.dto"; import { CreateServiceProfileDto } from "@dto/chirpstack/create-service-profile.dto"; @@ -37,11 +29,12 @@ import { ServiceProfileService } from "@services/chirpstack/service-profile.serv import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Chirpstack") @Controller("chirpstack/service-profiles") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @ApplicationAdmin() export class ServiceProfileController { constructor(private serviceProfileService: ServiceProfileService) {} @@ -78,10 +71,7 @@ export class ServiceProfileController { @Param("id") id: string, @Body() updateDto: UpdateServiceProfileDto ): Promise { - const result = await this.serviceProfileService.updateServiceProfile( - updateDto, - id - ); + const result = await this.serviceProfileService.updateServiceProfile(updateDto, id); if (result.status != 200) { AuditLog.fail( @@ -121,10 +111,7 @@ export class ServiceProfileController { @Query("offset") offset: number ): Promise { this.logger.debug(`Limit: '${limit}' Offset:'${offset}'`); - const res = await this.serviceProfileService.findAllServiceProfiles( - limit || 50, - offset || 0 - ); + const res = await this.serviceProfileService.findAllServiceProfiles(limit || 50, offset || 0); return res; } @@ -133,38 +120,21 @@ export class ServiceProfileController { @ApiOperation({ summary: "Delete one ServiceProfile by id" }) @ApiNotFoundResponse() @ApplicationAdmin() - async deleteOne( - @Req() req: AuthenticatedRequest, - @Param("id") id: string - ): Promise { + async deleteOne(@Req() req: AuthenticatedRequest, @Param("id") id: string): Promise { try { const result = await this.serviceProfileService.deleteServiceProfile(id); if (!result) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } - AuditLog.success( - ActionType.DELETE, - "ChirpstackServiceProfile", - req.user.userId, - id - ); + AuditLog.success(ActionType.DELETE, "ChirpstackServiceProfile", req.user.userId, id); return new DeleteResponseDto(1); } catch (err) { - this.logger.error( - `Error occured during delete: '${JSON.stringify(err?.response?.data)}'` - ); - if ( - err?.message == "this object is used by other objects, remove them first" - ) { + this.logger.error(`Error occured during delete: '${JSON.stringify(err?.response?.data)}'`); + if (err?.message == "this object is used by other objects, remove them first") { throw new BadRequestException(ErrorCodes.IsUsed); } - AuditLog.fail( - ActionType.DELETE, - "ChirpstackServiceProfile", - req.user.userId, - id - ); + AuditLog.fail(ActionType.DELETE, "ChirpstackServiceProfile", req.user.userId, id); throw err; } } diff --git a/src/controllers/admin-controller/data-target.controller.ts b/src/controllers/admin-controller/data-target.controller.ts index 11407e66..f566226b 100644 --- a/src/controllers/admin-controller/data-target.controller.ts +++ b/src/controllers/admin-controller/data-target.controller.ts @@ -15,7 +15,6 @@ import { } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -24,7 +23,7 @@ import { } from "@nestjs/swagger"; import { ComposeAuthGuard } from "@auth/compose-auth.guard"; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreateDataTargetDto } from "@dto/create-data-target.dto"; import { DeleteResponseDto } from "@dto/delete-application-response.dto"; @@ -34,28 +33,23 @@ import { ListAllDataTargetsDto } from "@dto/list-all-data-targets.dto"; import { UpdateDataTargetDto } from "@dto/update-data-target.dto"; import { DataTarget } from "@entities/data-target.entity"; import { ErrorCodes } from "@enum/error-codes.enum"; -import { - checkIfUserHasAccessToApplication, - ApplicationAccessScope, -} from "@helpers/security-helper"; +import { ApplicationAccessScope, checkIfUserHasAccessToApplication } from "@helpers/security-helper"; import { DataTargetService } from "@services/data-targets/data-target.service"; import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; import { OrganizationService } from "@services/user-management/organization.service"; import { OddkMailInfo } from "@dto/oddk-mail-info.dto"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Data Target") @Controller("data-target") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() export class DataTargetController { - constructor( - private dataTargetService: DataTargetService, - private organizationService: OrganizationService - ) {} + constructor(private dataTargetService: DataTargetService, private organizationService: OrganizationService) {} @Get() @ApiOperation({ summary: "Find all DataTargets" }) @@ -70,33 +64,19 @@ export class DataTargetController { query.applicationId = +query.applicationId; } - checkIfUserHasAccessToApplication( - req, - query.applicationId, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, query.applicationId, ApplicationAccessScope.Read); const allowed = req.user.permissions.getAllApplicationsWithAtLeastRead(); - return await this.dataTargetService.findAndCountAllWithPagination( - query, - allowed - ); + return await this.dataTargetService.findAndCountAllWithPagination(query, allowed); } } @Get(":id") @ApiOperation({ summary: "Find DataTarget by id" }) - async findOne( - @Req() req: AuthenticatedRequest, - @Param("id", new ParseIntPipe()) id: number - ): Promise { + async findOne(@Req() req: AuthenticatedRequest, @Param("id", new ParseIntPipe()) id: number): Promise { try { const dataTarget = await this.dataTargetService.findOne(id); - checkIfUserHasAccessToApplication( - req, - dataTarget.application.id, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, dataTarget.application.id, ApplicationAccessScope.Read); return dataTarget; } catch (err) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); @@ -111,22 +91,9 @@ export class DataTargetController { @Body() createDataTargetDto: CreateDataTargetDto ): Promise { try { - checkIfUserHasAccessToApplication( - req, - createDataTargetDto.applicationId, - ApplicationAccessScope.Write - ); - const dataTarget = await this.dataTargetService.create( - createDataTargetDto, - req.user.userId - ); - AuditLog.success( - ActionType.CREATE, - DataTarget.name, - req.user.userId, - dataTarget.id, - dataTarget.name - ); + checkIfUserHasAccessToApplication(req, createDataTargetDto.applicationId, ApplicationAccessScope.Write); + const dataTarget = await this.dataTargetService.create(createDataTargetDto, req.user.userId); + AuditLog.success(ActionType.CREATE, DataTarget.name, req.user.userId, dataTarget.id, dataTarget.name); return dataTarget; } catch (err) { AuditLog.fail(ActionType.CREATE, DataTarget.name, req.user.userId); @@ -145,41 +112,17 @@ export class DataTargetController { ): Promise { const oldDataTarget = await this.dataTargetService.findOne(id); try { - checkIfUserHasAccessToApplication( - req, - oldDataTarget.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, oldDataTarget.application.id, ApplicationAccessScope.Write); if (oldDataTarget.application.id !== updateDto.applicationId) { - checkIfUserHasAccessToApplication( - req, - updateDto.applicationId, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, updateDto.applicationId, ApplicationAccessScope.Write); } } catch (err) { - AuditLog.fail( - ActionType.UPDATE, - DataTarget.name, - req.user.userId, - oldDataTarget.id, - oldDataTarget.name - ); + AuditLog.fail(ActionType.UPDATE, DataTarget.name, req.user.userId, oldDataTarget.id, oldDataTarget.name); throw err; } - const dataTarget = await this.dataTargetService.update( - id, - updateDto, - req.user.userId - ); - AuditLog.success( - ActionType.UPDATE, - DataTarget.name, - req.user.userId, - dataTarget.id, - dataTarget.name - ); + const dataTarget = await this.dataTargetService.update(id, updateDto, req.user.userId); + AuditLog.success(ActionType.UPDATE, DataTarget.name, req.user.userId, dataTarget.id, dataTarget.name); return dataTarget; } @@ -193,11 +136,7 @@ export class DataTargetController { ): Promise { try { const dt = await this.dataTargetService.findOne(id); - checkIfUserHasAccessToApplication( - req, - dt.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, dt.application.id, ApplicationAccessScope.Write); const result = await this.dataTargetService.delete(id); if (result.affected === 0) { @@ -218,7 +157,9 @@ export class DataTargetController { @ApiOperation({ summary: "Get OpenDataDkRegistered-status for given OrganizationId" }) @ApiNotFoundResponse() @Read() - async getOpenDataDkRegistered(@Param("organizationId", new ParseIntPipe()) organizationId: number): Promise { + async getOpenDataDkRegistered( + @Param("organizationId", new ParseIntPipe()) organizationId: number + ): Promise { try { return (await this.organizationService.findById(organizationId))?.openDataDkRegistered; } catch (err) { @@ -228,7 +169,8 @@ export class DataTargetController { @Put("updateOpenDataDkRegistered/:organizationId") @ApiOperation({ - summary: "Update the OpenDataDkRegistered to true, for the given OrganizationId - to stop showing the dialog for sending ODDK-mail on creation of new datatargets", + summary: + "Update the OpenDataDkRegistered to true, for the given OrganizationId - to stop showing the dialog for sending ODDK-mail on creation of new datatargets", }) @ApiNotFoundResponse() async updateOpenDataDkRegistered( @@ -247,11 +189,8 @@ export class DataTargetController { } @Post("sendOpenDataDkMail") - @ApiOperation({summary: "Send mail for registering datatargets to Open Data DK, for the given OrganizationId"}) - async sendOpenDataDkMail( - @Req() req: AuthenticatedRequest, - @Body() mailInfoDto: OddkMailInfo - ): Promise { + @ApiOperation({ summary: "Send mail for registering datatargets to Open Data DK, for the given OrganizationId" }) + async sendOpenDataDkMail(@Req() req: AuthenticatedRequest, @Body() mailInfoDto: OddkMailInfo): Promise { await this.dataTargetService.sendOpenDataDkMail(mailInfoDto, req.user.userId); await this.organizationService.updateOpenDataDkRegistered(mailInfoDto.organizationId, req.user.userId); return true; diff --git a/src/controllers/admin-controller/device-model.controller.ts b/src/controllers/admin-controller/device-model.controller.ts index 782b1c4e..b71ceaa2 100644 --- a/src/controllers/admin-controller/device-model.controller.ts +++ b/src/controllers/admin-controller/device-model.controller.ts @@ -1,4 +1,4 @@ -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; import { Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreateDeviceModelDto } from "@dto/create-device-model.dto"; @@ -29,7 +29,6 @@ import { } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiOperation, ApiTags, @@ -37,11 +36,12 @@ import { } from "@nestjs/swagger"; import { AuditLog } from "@services/audit-log.service"; import { DeviceModelService } from "@services/device-management/device-model.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Device Model") @Controller("device-model") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @@ -54,12 +54,9 @@ export class DeviceModelController { @Req() req: AuthenticatedRequest, @Query() query?: ListAllDeviceModelsDto ): Promise { - if (query?.organizationId != null) { + if (query?.organizationId != null) { checkIfUserHasAccessToOrganization(req, query?.organizationId, OrganizationAccessScope.ApplicationRead); - return this.service.getAllDeviceModelsByOrgIds( - [query?.organizationId], - query - ); + return this.service.getAllDeviceModelsByOrgIds([query?.organizationId], query); } const orgIds = req.user.permissions.getAllOrganizationsWithAtLeastApplicationRead(); @@ -68,10 +65,7 @@ export class DeviceModelController { @Get(":id") @ApiOperation({ summary: "Get one device model" }) - async findOne( - @Req() req: AuthenticatedRequest, - @Param("id", new ParseIntPipe()) id: number - ): Promise { + async findOne(@Req() req: AuthenticatedRequest, @Param("id", new ParseIntPipe()) id: number): Promise { const deviceModel = await this.service.getById(id); if (!deviceModel) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); @@ -85,20 +79,12 @@ export class DeviceModelController { @Header("Cache-Control", "none") @ApiOperation({ summary: "Create a new device model" }) @ApiBadRequestResponse() - async create( - @Req() req: AuthenticatedRequest, - @Body() dto: CreateDeviceModelDto - ): Promise { + async create(@Req() req: AuthenticatedRequest, @Body() dto: CreateDeviceModelDto): Promise { try { checkIfUserHasAccessToOrganization(req, dto.belongsToId, OrganizationAccessScope.ApplicationWrite); const res = await this.service.create(dto, req.user.userId); - AuditLog.success( - ActionType.CREATE, - DeviceModel.name, - req.user.userId, - res.id - ); + AuditLog.success(ActionType.CREATE, DeviceModel.name, req.user.userId, res.id); return res; } catch (err) { AuditLog.fail(ActionType.CREATE, DeviceModel.name, req.user.userId); diff --git a/src/controllers/admin-controller/iot-device-payload-decoder-data-target-connection.controller.ts b/src/controllers/admin-controller/iot-device-payload-decoder-data-target-connection.controller.ts index 4554cc27..afa41bec 100644 --- a/src/controllers/admin-controller/iot-device-payload-decoder-data-target-connection.controller.ts +++ b/src/controllers/admin-controller/iot-device-payload-decoder-data-target-connection.controller.ts @@ -14,7 +14,6 @@ import { } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -25,7 +24,7 @@ import { } from "@nestjs/swagger"; import { ComposeAuthGuard } from "@auth/compose-auth.guard"; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreateIoTDevicePayloadDecoderDataTargetConnectionDto } from "@dto/create-iot-device-payload-decoder-data-target-connection.dto"; import { DeleteResponseDto } from "@dto/delete-application-response.dto"; @@ -36,19 +35,17 @@ import { ListAllEntitiesDto } from "@dto/list-all-entities.dto"; import { UpdateIoTDevicePayloadDecoderDataTargetConnectionDto as UpdateConnectionDto } from "@dto/update-iot-device-payload-decoder-data-target-connection.dto"; import { IoTDevicePayloadDecoderDataTargetConnection } from "@entities/iot-device-payload-decoder-data-target-connection.entity"; import { ErrorCodes } from "@enum/error-codes.enum"; -import { - checkIfUserHasAccessToApplication, - ApplicationAccessScope, -} from "@helpers/security-helper"; +import { ApplicationAccessScope, checkIfUserHasAccessToApplication } from "@helpers/security-helper"; import { IoTDevicePayloadDecoderDataTargetConnectionService } from "@services/device-management/iot-device-payload-decoder-data-target-connection.service"; import { IoTDeviceService } from "@services/device-management/iot-device.service"; import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("IoT-Device, PayloadDecoder and DataTarget Connection") @Controller("iot-device-payload-decoder-data-target-connection") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @@ -61,8 +58,7 @@ export class IoTDevicePayloadDecoderDataTargetConnectionController { @Get() @ApiProduces("application/json") @ApiOperation({ - summary: - "Find all connections between IoT-Devices, PayloadDecoders and DataTargets (paginated)", + summary: "Find all connections between IoT-Devices, PayloadDecoders and DataTargets (paginated)", }) @ApiResponse({ status: 200, @@ -158,10 +154,7 @@ export class IoTDevicePayloadDecoderDataTargetConnectionController { createDto: CreateIoTDevicePayloadDecoderDataTargetConnectionDto ): Promise { try { - await this.checkUserHasWriteAccessToAllIotDevices( - createDto.iotDeviceIds, - req - ); + await this.checkUserHasWriteAccessToAllIotDevices(createDto.iotDeviceIds, req); const result = await this.service.create(createDto, req.user.userId); AuditLog.success( @@ -172,26 +165,15 @@ export class IoTDevicePayloadDecoderDataTargetConnectionController { ); return result; } catch (err) { - AuditLog.fail( - ActionType.CREATE, - IoTDevicePayloadDecoderDataTargetConnection.name, - req.user.userId - ); + AuditLog.fail(ActionType.CREATE, IoTDevicePayloadDecoderDataTargetConnection.name, req.user.userId); throw err; } } - private async checkUserHasWriteAccessToAllIotDevices( - ids: number[], - req: AuthenticatedRequest - ) { + private async checkUserHasWriteAccessToAllIotDevices(ids: number[], req: AuthenticatedRequest) { const iotDevices = await this.iotDeviceService.findManyByIds(ids); iotDevices.forEach(x => { - checkIfUserHasAccessToApplication( - req, - x.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, x.application.id, ApplicationAccessScope.Write); }); } @@ -220,29 +202,14 @@ export class IoTDevicePayloadDecoderDataTargetConnectionController { ); return result; } catch (err) { - AuditLog.fail( - ActionType.UPDATE, - IoTDevicePayloadDecoderDataTargetConnection.name, - req.user.userId, - id - ); + AuditLog.fail(ActionType.UPDATE, IoTDevicePayloadDecoderDataTargetConnection.name, req.user.userId, id); throw err; } } - private async checkIfUpdateIsAllowed( - updateDto: UpdateConnectionDto, - req: AuthenticatedRequest, - id: number - ) { - const newIotDevice = await this.iotDeviceService.findOne( - updateDto.iotDeviceIds[0] - ); - checkIfUserHasAccessToApplication( - req, - newIotDevice.application.id, - ApplicationAccessScope.Write - ); + private async checkIfUpdateIsAllowed(updateDto: UpdateConnectionDto, req: AuthenticatedRequest, id: number) { + const newIotDevice = await this.iotDeviceService.findOne(updateDto.iotDeviceIds[0]); + checkIfUserHasAccessToApplication(req, newIotDevice.application.id, ApplicationAccessScope.Write); const oldConnection = await this.service.findOne(id); await this.checkUserHasWriteAccessToAllIotDevices(updateDto.iotDeviceIds, req); const oldIds = oldConnection.iotDevices.map(x => x.id); @@ -270,20 +237,10 @@ export class IoTDevicePayloadDecoderDataTargetConnectionController { if (result.affected === 0) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } - AuditLog.success( - ActionType.DELETE, - IoTDevicePayloadDecoderDataTargetConnection.name, - req.user.userId, - id - ); + AuditLog.success(ActionType.DELETE, IoTDevicePayloadDecoderDataTargetConnection.name, req.user.userId, id); return new DeleteResponseDto(result.affected); } catch (err) { - AuditLog.fail( - ActionType.DELETE, - IoTDevicePayloadDecoderDataTargetConnection.name, - req.user.userId, - id - ); + AuditLog.fail(ActionType.DELETE, IoTDevicePayloadDecoderDataTargetConnection.name, req.user.userId, id); throw err; } } diff --git a/src/controllers/admin-controller/iot-device-payload-decoder.controller.ts b/src/controllers/admin-controller/iot-device-payload-decoder.controller.ts index 51400a96..80bf0f82 100644 --- a/src/controllers/admin-controller/iot-device-payload-decoder.controller.ts +++ b/src/controllers/admin-controller/iot-device-payload-decoder.controller.ts @@ -1,22 +1,7 @@ -import { - Controller, - Get, - NotFoundException, - Param, - ParseIntPipe, - Query, - Req, - UseGuards, -} from "@nestjs/common"; -import { - ApiBearerAuth, - ApiForbiddenResponse, - ApiOperation, - ApiTags, - ApiUnauthorizedResponse, -} from "@nestjs/swagger"; +import { Controller, Get, NotFoundException, Param, ParseIntPipe, Query, Req, UseGuards } from "@nestjs/common"; +import { ApiForbiddenResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from "@nestjs/swagger"; -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; import { Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { IoTDeviceService } from "@services/device-management/iot-device.service"; @@ -26,11 +11,12 @@ import { } from "@dto/list-all-iot-devices-minimal-response.dto"; import { ErrorCodes } from "@enum/error-codes.enum"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("IoT Device") @Controller("iot-device/minimalByPayloadDecoder") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() diff --git a/src/controllers/admin-controller/iot-device.controller.ts b/src/controllers/admin-controller/iot-device.controller.ts index 7069a32f..c4b5366c 100644 --- a/src/controllers/admin-controller/iot-device.controller.ts +++ b/src/controllers/admin-controller/iot-device.controller.ts @@ -17,7 +17,6 @@ import { } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -35,10 +34,7 @@ import { LoRaWANDeviceWithChirpstackDataDto } from "@dto/lorawan-device-with-chi import { UpdateIoTDeviceDto } from "@dto/update-iot-device.dto"; import { IoTDevice } from "@entities/iot-device.entity"; import { ErrorCodes } from "@enum/error-codes.enum"; -import { - ApplicationAccessScope, - checkIfUserHasAccessToApplication, -} from "@helpers/security-helper"; +import { ApplicationAccessScope, checkIfUserHasAccessToApplication } from "@helpers/security-helper"; import { IoTDeviceService } from "@services/device-management/iot-device.service"; import { SigFoxDeviceWithBackendDataDto } from "@dto/sigfox-device-with-backend-data.dto"; import { CreateIoTDeviceDownlinkDto } from "@dto/create-iot-device-downlink.dto"; @@ -62,11 +58,12 @@ import { DeviceStatsResponseDto } from "@dto/chirpstack/device/device-stats.resp import { GenericHTTPDevice } from "@entities/generic-http-device.entity"; import { MQTTInternalBrokerDeviceDTO } from "@dto/mqtt-internal-broker-device.dto"; import { MQTTExternalBrokerDeviceDTO } from "@dto/mqtt-external-broker-device.dto"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("IoT Device") @Controller("iot-device") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @@ -94,10 +91,7 @@ export class IoTDeviceController { > { let result = undefined; try { - result = await this.iotDeviceService.findOneWithApplicationAndMetadata( - id, - true - ); + result = await this.iotDeviceService.findOneWithApplicationAndMetadata(id, true); } catch (err) { this.logger.error(`Error occured during findOne: '${JSON.stringify(err)}'`); } @@ -106,11 +100,7 @@ export class IoTDeviceController { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } - checkIfUserHasAccessToApplication( - req, - result.application.id, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, result.application.id, ApplicationAccessScope.Read); return result; } @@ -123,10 +113,7 @@ export class IoTDeviceController { ): Promise { let device = undefined; try { - device = await this.iotDeviceService.findOneWithApplicationAndMetadata( - id, - true - ); + device = await this.iotDeviceService.findOneWithApplicationAndMetadata(id, true); } catch (err) { this.logger.error(`Error occured during findOne: '${JSON.stringify(err)}'`); } @@ -134,15 +121,9 @@ export class IoTDeviceController { if (!device) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } - checkIfUserHasAccessToApplication( - req, - device.application.id, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, device.application.id, ApplicationAccessScope.Read); if (device.type == IoTDeviceType.LoRaWAN) { - return this.chirpstackDeviceService.getDownlinkQueue( - (device as LoRaWANDevice).deviceEUI - ); + return this.chirpstackDeviceService.getDownlinkQueue((device as LoRaWANDevice).deviceEUI); } else if (device.type == IoTDeviceType.SigFox) { return this.iotDeviceService.getDownlinkForSigfox(device as SigFoxDevice); } else { @@ -159,11 +140,7 @@ export class IoTDeviceController { @Param("id", new ParseIntPipe()) id: number ): Promise { const device = await this.iotDeviceService.findOne(id); - checkIfUserHasAccessToApplication( - req, - device.application.id, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, device.application.id, ApplicationAccessScope.Read); return this.iotDeviceService.findStats(device); } @@ -172,32 +149,15 @@ export class IoTDeviceController { @Header("Cache-Control", "none") @ApiOperation({ summary: "Create a new IoTDevice" }) @ApiBadRequestResponse() - async create( - @Req() req: AuthenticatedRequest, - @Body() createDto: CreateIoTDeviceDto - ): Promise { + async create(@Req() req: AuthenticatedRequest, @Body() createDto: CreateIoTDeviceDto): Promise { try { - checkIfUserHasAccessToApplication( - req, - createDto.applicationId, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, createDto.applicationId, ApplicationAccessScope.Write); const device = await this.iotDeviceService.create(createDto, req.user.userId); - AuditLog.success( - ActionType.CREATE, - IoTDevice.name, - req.user.userId, - device.id, - device.name - ); + AuditLog.success(ActionType.CREATE, IoTDevice.name, req.user.userId, device.id, device.name); return device; } catch (err) { AuditLog.fail(ActionType.CREATE, IoTDevice.name, req.user.userId); - this.logger.error( - `Failed to create IoTDevice from dto: ${JSON.stringify( - createDto - )}. Error: ${err}` - ); + this.logger.error(`Failed to create IoTDevice from dto: ${JSON.stringify(createDto)}. Error: ${err}`); throw err; } } @@ -212,17 +172,11 @@ export class IoTDeviceController { @Body() dto: CreateIoTDeviceDownlinkDto ): Promise { try { - const device = await this.iotDeviceService.findOneWithApplicationAndMetadata( - id - ); + const device = await this.iotDeviceService.findOneWithApplicationAndMetadata(id); if (!device) { throw new NotFoundException(); } - checkIfUserHasAccessToApplication( - req, - device?.application?.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, device?.application?.id, ApplicationAccessScope.Write); const result = await this.downlinkService.createDownlink(dto, device); AuditLog.success(ActionType.CREATE, "Downlink", req.user.userId); return result; @@ -242,41 +196,20 @@ export class IoTDeviceController { @Body() updateDto: UpdateIoTDeviceDto ): Promise { // Old application - const oldIotDevice = await this.iotDeviceService.findOneWithApplicationAndMetadata( - id, - false - ); + const oldIotDevice = await this.iotDeviceService.findOneWithApplicationAndMetadata(id, false); try { - checkIfUserHasAccessToApplication( - req, - oldIotDevice.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, oldIotDevice.application.id, ApplicationAccessScope.Write); if (updateDto.applicationId !== oldIotDevice.application.id) { // New application - checkIfUserHasAccessToApplication( - req, - updateDto.applicationId, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, updateDto.applicationId, ApplicationAccessScope.Write); } } catch (err) { AuditLog.fail(ActionType.UPDATE, IoTDevice.name, req.user.userId, id); throw err; } - const iotDevice = await this.iotDeviceService.update( - id, - updateDto, - req.user.userId - ); - AuditLog.success( - ActionType.UPDATE, - IoTDevice.name, - req.user.userId, - iotDevice.id, - iotDevice.name - ); + const iotDevice = await this.iotDeviceService.update(id, updateDto, req.user.userId); + AuditLog.success(ActionType.UPDATE, IoTDevice.name, req.user.userId, iotDevice.id, iotDevice.name); return iotDevice; } @@ -290,22 +223,13 @@ export class IoTDeviceController { ): Promise { try { createDto.data.forEach(createDto => - checkIfUserHasAccessToApplication( - req, - createDto.applicationId, - ApplicationAccessScope.Write - ) + checkIfUserHasAccessToApplication(req, createDto.applicationId, ApplicationAccessScope.Write) ); - const devices = await this.iotDeviceService.createMany( - createDto.data, - req.user.userId - ); + const devices = await this.iotDeviceService.createMany(createDto.data, req.user.userId); // Iterate through the devices once, splitting it into a tuple with the data we want to log - const { deviceIds, deviceNames } = buildIoTDeviceCreateUpdateAuditData( - devices - ); + const { deviceIds, deviceNames } = buildIoTDeviceCreateUpdateAuditData(devices); if (!deviceIds.length) { AuditLog.fail(ActionType.CREATE, IoTDevice.name, req.user.userId); @@ -321,11 +245,7 @@ export class IoTDeviceController { return devices; } catch (err) { AuditLog.fail(ActionType.CREATE, IoTDevice.name, req.user.userId); - this.logger.error( - `Failed to create IoTDevice from dto: ${JSON.stringify( - createDto - )}. Error: ${err}` - ); + this.logger.error(`Failed to create IoTDevice from dto: ${JSON.stringify(createDto)}. Error: ${err}`); throw err; } } @@ -346,12 +266,7 @@ export class IoTDeviceController { try { validDevices.data = updateDto.data.reduce( - ensureIoTDeviceUpdatePayload( - validDevices, - oldIotDevices, - devicesNotFound, - req - ), + ensureIoTDeviceUpdatePayload(validDevices, oldIotDevices, devicesNotFound, req), [] ); } catch (err) { @@ -388,15 +303,8 @@ export class IoTDeviceController { @Param("id", new ParseIntPipe()) id: number ): Promise { try { - const oldIotDevice = await this.iotDeviceService.findOneWithApplicationAndMetadata( - id, - false - ); - checkIfUserHasAccessToApplication( - req, - oldIotDevice?.application?.id, - ApplicationAccessScope.Write - ); + const oldIotDevice = await this.iotDeviceService.findOneWithApplicationAndMetadata(id, false); + checkIfUserHasAccessToApplication(req, oldIotDevice?.application?.id, ApplicationAccessScope.Write); const result = await this.iotDeviceService.delete(oldIotDevice); AuditLog.success(ActionType.DELETE, IoTDevice.name, req.user.userId, id); return new DeleteResponseDto(result.affected); @@ -415,21 +323,13 @@ export class IoTDeviceController { ): Promise> { try { const oldIotDevice = await this.iotDeviceService.findOne(id); - checkIfUserHasAccessToApplication( - req, - oldIotDevice?.application?.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, oldIotDevice?.application?.id, ApplicationAccessScope.Write); if (oldIotDevice.type !== IoTDeviceType.GenericHttp) { - throw new BadRequestException( - "The requested device is not a generic HTTP device" - ); + throw new BadRequestException("The requested device is not a generic HTTP device"); } - const result = await this.iotDeviceService.resetHttpDeviceApiKey( - oldIotDevice as GenericHTTPDevice - ); + const result = await this.iotDeviceService.resetHttpDeviceApiKey(oldIotDevice as GenericHTTPDevice); AuditLog.success(ActionType.UPDATE, IoTDevice.name, req.user.userId, id); return { apiKey: result.apiKey, @@ -450,14 +350,8 @@ export class IoTDeviceController { @Param("applicationId", new ParseIntPipe()) applicationId: number ): Promise { try { - checkIfUserHasAccessToApplication( - req, - applicationId, - ApplicationAccessScope.Read - ); - const csvFile = await this.iotDeviceService.getDevicesMetadataCsv( - applicationId - ); + checkIfUserHasAccessToApplication(req, applicationId, ApplicationAccessScope.Read); + const csvFile = await this.iotDeviceService.getDevicesMetadataCsv(applicationId); return new StreamableFile(csvFile); } catch (err) { this.logger.error(err); diff --git a/src/controllers/admin-controller/lorawan/lorawan-gateway.controller.ts b/src/controllers/admin-controller/lorawan/lorawan-gateway.controller.ts index 8e0ad6e6..3e3cefe7 100644 --- a/src/controllers/admin-controller/lorawan/lorawan-gateway.controller.ts +++ b/src/controllers/admin-controller/lorawan/lorawan-gateway.controller.ts @@ -6,16 +6,16 @@ import { ListAllGatewayStatusDto, } from "@dto/chirpstack/backend/gateway-all-status.dto"; import { GatewayStatus, GetGatewayStatusQuery } from "@dto/chirpstack/backend/gateway-status.dto"; -import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; -import { Controller, Get, Param, Query, Req, UseGuards } from "@nestjs/common"; -import { ApiBearerAuth, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; +import { Controller, Get, Param, Query, UseGuards } from "@nestjs/common"; +import { ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; import { ChirpstackGatewayService } from "@services/chirpstack/chirpstack-gateway.service"; import { GatewayStatusHistoryService } from "@services/chirpstack/gateway-status-history.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("LoRaWAN gateway") @Controller("lorawan/gateway") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() export class LoRaWANGatewayController { constructor( private statusHistoryService: GatewayStatusHistoryService, @@ -26,9 +26,7 @@ export class LoRaWANGatewayController { @ApiProduces("application/json") @ApiOperation({ summary: "Get the status for all LoRaWAN gateways" }) @Read() - async getAllStatus( - @Query() query: ListAllGatewayStatusDto - ): Promise { + async getAllStatus(@Query() query: ListAllGatewayStatusDto): Promise { // Currently, everyone is allowed to get the status return this.statusHistoryService.findAllWithChirpstack(query); } @@ -36,10 +34,7 @@ export class LoRaWANGatewayController { @Get("/status/:id") @ApiProduces("application/json") @ApiOperation({ summary: "Get the status for a LoRaWAN gateway" }) - async getStatus( - @Param("id") id: string, - @Query() query: GetGatewayStatusQuery - ): Promise { + async getStatus(@Param("id") id: string, @Query() query: GetGatewayStatusQuery): Promise { // Currently, everyone is allowed to get the status const gatewayDto = await this.chirpstackGatewayService.getOne(id); return this.statusHistoryService.findOne(gatewayDto.gateway, query.timeInterval); diff --git a/src/controllers/admin-controller/multicast.controller.ts b/src/controllers/admin-controller/multicast.controller.ts index 59c94b45..ec54d97f 100644 --- a/src/controllers/admin-controller/multicast.controller.ts +++ b/src/controllers/admin-controller/multicast.controller.ts @@ -1,26 +1,25 @@ import { + Body, Controller, + Delete, Get, + Header, + Logger, + NotFoundException, + Param, + ParseIntPipe, Post, - Body, Put, - Param, - Delete, - Req, - UseGuards, Query, + Req, UnauthorizedException, - NotFoundException, - Header, - ParseIntPipe, - Logger, + UseGuards, } from "@nestjs/common"; import { MulticastService } from "../../services/device-management/multicast.service"; import { CreateMulticastDto } from "../../entities/dto/create-multicast.dto"; import { UpdateMulticastDto } from "../../entities/dto/update-multicast.dto"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiOperation, ApiTags, @@ -28,15 +27,12 @@ import { } from "@nestjs/swagger"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; import { Multicast } from "@entities/multicast.entity"; -import { - checkIfUserHasAccessToApplication, - ApplicationAccessScope, -} from "@helpers/security-helper"; +import { ApplicationAccessScope, checkIfUserHasAccessToApplication } from "@helpers/security-helper"; import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; import { RolesGuard } from "@auth/roles.guard"; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { ListAllMulticastsDto } from "@dto/list-all-multicasts.dto"; import { ListAllMulticastsResponseDto } from "@dto/list-all-multicasts-response.dto"; import { ErrorCodes } from "@enum/error-codes.enum"; @@ -44,10 +40,11 @@ import { DeleteResponseDto } from "@dto/delete-application-response.dto"; import { MulticastDownlinkQueueResponseDto } from "@dto/chirpstack/chirpstack-multicast-downlink-queue-response.dto"; import { CreateMulticastDownlinkDto } from "@dto/create-multicast-downlink.dto"; import { CreateChirpstackMulticastQueueItemResponse } from "@dto/chirpstack/create-chirpstack-multicast-queue-item.dto"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Multicast") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @@ -59,27 +56,11 @@ export class MulticastController { @Post() @ApiOperation({ summary: "Create a new multicast" }) @ApiBadRequestResponse() - async create( - @Req() req: AuthenticatedRequest, - @Body() createMulticastDto: CreateMulticastDto - ): Promise { + async create(@Req() req: AuthenticatedRequest, @Body() createMulticastDto: CreateMulticastDto): Promise { try { - checkIfUserHasAccessToApplication( - req, - createMulticastDto.applicationID, - ApplicationAccessScope.Write - ); - const multicast = await this.multicastService.create( - createMulticastDto, - req.user.userId - ); - AuditLog.success( - ActionType.CREATE, - Multicast.name, - req.user.userId, - multicast.id, - multicast.groupName - ); + checkIfUserHasAccessToApplication(req, createMulticastDto.applicationID, ApplicationAccessScope.Write); + const multicast = await this.multicastService.create(createMulticastDto, req.user.userId); + AuditLog.success(ActionType.CREATE, Multicast.name, req.user.userId, multicast.id, multicast.groupName); return multicast; } catch (err) { AuditLog.fail(ActionType.CREATE, Multicast.name, req.user.userId); @@ -106,26 +87,16 @@ export class MulticastController { throw new UnauthorizedException(); } - return await this.multicastService.findAndCountAllWithPagination( - query, - allowed - ); + return await this.multicastService.findAndCountAllWithPagination(query, allowed); } } @Get(":id") @ApiOperation({ summary: "Find Multicast by id" }) - async findOne( - @Req() req: AuthenticatedRequest, - @Param("id", new ParseIntPipe()) id: number - ): Promise { + async findOne(@Req() req: AuthenticatedRequest, @Param("id", new ParseIntPipe()) id: number): Promise { try { const multicast = await this.multicastService.findOne(id); - checkIfUserHasAccessToApplication( - req, - multicast.application.id, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, multicast.application.id, ApplicationAccessScope.Read); return multicast; } catch (err) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); @@ -148,15 +119,9 @@ export class MulticastController { if (!multicast) { throw new NotFoundException(ErrorCodes.IdDoesNotExists); } - checkIfUserHasAccessToApplication( - req, - multicast.application.id, - ApplicationAccessScope.Read - ); + checkIfUserHasAccessToApplication(req, multicast.application.id, ApplicationAccessScope.Read); - return this.multicastService.getDownlinkQueue( - multicast.lorawanMulticastDefinition.chirpstackGroupId - ); + return this.multicastService.getDownlinkQueue(multicast.lorawanMulticastDefinition.chirpstackGroupId); } @Post(":id/downlink-multicast") @@ -173,11 +138,7 @@ export class MulticastController { if (!multicast) { throw new NotFoundException(); } - checkIfUserHasAccessToApplication( - req, - multicast.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, multicast.application.id, ApplicationAccessScope.Write); const result = await this.multicastService.createDownlink(dto, multicast); AuditLog.success(ActionType.CREATE, "Downlink", req.user.userId); return result; @@ -198,17 +159,9 @@ export class MulticastController { ): Promise { const oldMulticast = await this.multicastService.findOne(id); try { - checkIfUserHasAccessToApplication( - req, - oldMulticast.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, oldMulticast.application.id, ApplicationAccessScope.Write); if (oldMulticast.application.id !== updateDto.applicationID) { - checkIfUserHasAccessToApplication( - req, - updateDto.applicationID, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, updateDto.applicationID, ApplicationAccessScope.Write); } } catch (err) { AuditLog.fail( @@ -221,11 +174,7 @@ export class MulticastController { throw err; } - const multicast = await this.multicastService.update( - oldMulticast, - updateDto, - req.user.userId - ); + const multicast = await this.multicastService.update(oldMulticast, updateDto, req.user.userId); AuditLog.success( ActionType.UPDATE, Multicast.name, @@ -246,11 +195,7 @@ export class MulticastController { ): Promise { try { const multicast = await this.multicastService.findOne(id); - checkIfUserHasAccessToApplication( - req, - multicast.application.id, - ApplicationAccessScope.Write - ); + checkIfUserHasAccessToApplication(req, multicast.application.id, ApplicationAccessScope.Write); const result = await this.multicastService.multicastDelete(id, multicast); if (result.affected === 0) { diff --git a/src/controllers/admin-controller/payload-decoder.controller.ts b/src/controllers/admin-controller/payload-decoder.controller.ts index 454cf4a9..a82e013e 100644 --- a/src/controllers/admin-controller/payload-decoder.controller.ts +++ b/src/controllers/admin-controller/payload-decoder.controller.ts @@ -17,7 +17,6 @@ import { } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -25,8 +24,8 @@ import { ApiUnauthorizedResponse, } from "@nestjs/swagger"; -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { CreatePayloadDecoderDto } from "@dto/create-payload-decoder.dto"; import { DeleteResponseDto } from "@dto/delete-application-response.dto"; @@ -36,15 +35,16 @@ import { ListAllPayloadDecoderResponseDto } from "@dto/list-all-payload-decoders import { UpdatePayloadDecoderDto } from "@dto/update-payload-decoder.dto"; import { PayloadDecoder } from "@entities/payload-decoder.entity"; import { ErrorCodes } from "@enum/error-codes.enum"; -import { OrganizationAccessScope, checkIfUserHasAccessToOrganization } from "@helpers/security-helper"; +import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper"; import { PayloadDecoderService } from "@services/data-management/payload-decoder.service"; import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("Payload Decoder") @Controller("payload-decoder") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @@ -80,10 +80,7 @@ export class PayloadDecoderController { @Req() req: AuthenticatedRequest, @Query() query?: ListAllPayloadDecoderDto ): Promise { - return await this.payloadDecoderService.findAndCountWithPagination( - query, - query.organizationId - ); + return await this.payloadDecoderService.findAndCountWithPagination(query, query.organizationId); } @Post() @@ -99,10 +96,7 @@ export class PayloadDecoderController { checkIfUserHasAccessToOrganization(req, createDto.organizationId, OrganizationAccessScope.ApplicationWrite); // TODO: Valider at funktionen er gyldig - const payloadDecoder = await this.payloadDecoderService.create( - createDto, - req.user.userId - ); + const payloadDecoder = await this.payloadDecoderService.create(createDto, req.user.userId); AuditLog.success( ActionType.CREATE, PayloadDecoder.name, @@ -131,14 +125,14 @@ export class PayloadDecoderController { checkIfUserHasAccessToOrganization(req, updateDto.organizationId, OrganizationAccessScope.ApplicationWrite); const oldDecoder = await this.payloadDecoderService.findOne(id); if (oldDecoder?.organization?.id) { - checkIfUserHasAccessToOrganization(req, oldDecoder.organization.id, OrganizationAccessScope.ApplicationWrite); + checkIfUserHasAccessToOrganization( + req, + oldDecoder.organization.id, + OrganizationAccessScope.ApplicationWrite + ); } // TODO: Valider at funktionen er gyldig - const payloadDecoder = await this.payloadDecoderService.update( - id, - updateDto, - req.user.userId - ); + const payloadDecoder = await this.payloadDecoderService.update(id, updateDto, req.user.userId); AuditLog.success( ActionType.UPDATE, PayloadDecoder.name, @@ -165,7 +159,11 @@ export class PayloadDecoderController { try { const oldDecoder = await this.payloadDecoderService.findOne(id); if (oldDecoder?.organization?.id) { - checkIfUserHasAccessToOrganization(req, oldDecoder.organization.id, OrganizationAccessScope.ApplicationWrite); + checkIfUserHasAccessToOrganization( + req, + oldDecoder.organization.id, + OrganizationAccessScope.ApplicationWrite + ); } const result = await this.payloadDecoderService.delete(id); diff --git a/src/controllers/admin-controller/search.controller.ts b/src/controllers/admin-controller/search.controller.ts index ea1f5a5d..04433928 100644 --- a/src/controllers/admin-controller/search.controller.ts +++ b/src/controllers/admin-controller/search.controller.ts @@ -1,34 +1,20 @@ -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; import { Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; import { ListAllSearchResultsResponseDto } from "@dto/list-all-search-results-response.dto"; import { SearchResultType } from "@dto/search-result.dto"; import { ErrorCodes } from "@enum/error-codes.enum"; -import { - BadRequestException, - Controller, - Get, - Logger, - ParseIntPipe, - Query, - Req, - UseGuards, -} from "@nestjs/common"; -import { - ApiTags, - ApiBearerAuth, - ApiForbiddenResponse, - ApiUnauthorizedResponse, - ApiOperation, -} from "@nestjs/swagger"; +import { BadRequestException, Controller, Get, Logger, ParseIntPipe, Query, Req, UseGuards } from "@nestjs/common"; +import { ApiForbiddenResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from "@nestjs/swagger"; import { SearchService } from "@services/data-management/search.service"; import { isNumber } from "lodash"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @Controller("search") @ApiTags("Search") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() @ApiUnauthorizedResponse() diff --git a/src/controllers/admin-controller/sigfox/sigfox-api-contract.controller.ts b/src/controllers/admin-controller/sigfox/sigfox-api-contract.controller.ts index 77273e7e..faa4a972 100644 --- a/src/controllers/admin-controller/sigfox/sigfox-api-contract.controller.ts +++ b/src/controllers/admin-controller/sigfox/sigfox-api-contract.controller.ts @@ -1,4 +1,4 @@ -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; import { Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; @@ -6,27 +6,19 @@ import { SigFoxApiContractInfosContent } from "@dto/sigfox/external/sigfox-api-c import { SigFoxGroup } from "@entities/sigfox-group.entity"; import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper"; import { Controller, Get, ParseIntPipe, Query, Req, UseGuards } from "@nestjs/common"; -import { - ApiTags, - ApiBearerAuth, - ApiForbiddenResponse, - ApiProduces, - ApiOperation, -} from "@nestjs/swagger"; +import { ApiForbiddenResponse, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; import { SigFoxApiContractService } from "@services/sigfox/sigfox-api-contract.service"; import { SigFoxGroupService } from "@services/sigfox/sigfox-group.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("SigFox") @Controller("sigfox-contract") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() export class SigFoxApiContractController { - constructor( - private service: SigFoxApiContractService, - private sigfoxGroupService: SigFoxGroupService - ) {} + constructor(private service: SigFoxApiContractService, private sigfoxGroupService: SigFoxGroupService) {} @Get() @ApiProduces("application/json") @@ -35,9 +27,7 @@ export class SigFoxApiContractController { @Req() req: AuthenticatedRequest, @Query("groupId", new ParseIntPipe()) groupId: number ): Promise { - const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword( - groupId - ); + const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword(groupId); checkIfUserHasAccessToOrganization(req, group?.belongsTo?.id, OrganizationAccessScope.ApplicationRead); return await this.service.getContractInfos(group); diff --git a/src/controllers/admin-controller/sigfox/sigfox-api-device.controller.ts b/src/controllers/admin-controller/sigfox/sigfox-api-device.controller.ts index 130a4d9d..78bc28a3 100644 --- a/src/controllers/admin-controller/sigfox/sigfox-api-device.controller.ts +++ b/src/controllers/admin-controller/sigfox/sigfox-api-device.controller.ts @@ -6,41 +6,30 @@ import { SigFoxApiDeviceResponse } from "@dto/sigfox/external/sigfox-api-device- import { SigFoxGroup } from "@entities/sigfox-group.entity"; import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper"; import { Controller, Get, ParseIntPipe, Query, Req, UseGuards } from "@nestjs/common"; -import { - ApiBearerAuth, - ApiForbiddenResponse, - ApiOperation, - ApiProduces, - ApiTags, -} from "@nestjs/swagger"; +import { ApiForbiddenResponse, ApiOperation, ApiProduces, ApiTags } from "@nestjs/swagger"; import { IoTDeviceService } from "@services/device-management/iot-device.service"; import { SigFoxGroupService } from "@services/sigfox/sigfox-group.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("SigFox") @Controller("sigfox-api-device") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() export class SigFoxApiDeviceController { - constructor( - private sigfoxGroupService: SigFoxGroupService, - private iotDeviceService: IoTDeviceService - ) {} + constructor(private sigfoxGroupService: SigFoxGroupService, private iotDeviceService: IoTDeviceService) {} @Get() @ApiProduces("application/json") @ApiOperation({ - summary: - "List all SigFox Devices for a SigFox Group, that are not already created in OS2IoT", + summary: "List all SigFox Devices for a SigFox Group, that are not already created in OS2IoT", }) async getAll( @Req() req: AuthenticatedRequest, @Query("groupId", new ParseIntPipe()) groupId: number ): Promise { - const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword( - groupId - ); + const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword(groupId); checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationRead); return await this.iotDeviceService.getAllSigfoxDevicesByGroup(group, true); diff --git a/src/controllers/admin-controller/sigfox/sigfox-device-type.controller.ts b/src/controllers/admin-controller/sigfox/sigfox-device-type.controller.ts index a0c8e04e..3a5578cd 100644 --- a/src/controllers/admin-controller/sigfox/sigfox-device-type.controller.ts +++ b/src/controllers/admin-controller/sigfox/sigfox-device-type.controller.ts @@ -1,5 +1,5 @@ -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; import { CreateSigFoxApiDeviceTypeRequestDto } from "@dto/sigfox/external/create-sigfox-api-device-type-request.dto"; @@ -12,22 +12,9 @@ import { UpdateSigFoxApiDeviceTypeRequestDto } from "@dto/sigfox/external/update import { ActionType } from "@entities/audit-log-entry"; import { SigFoxGroup } from "@entities/sigfox-group.entity"; import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper"; -import { - Body, - Controller, - Get, - HttpCode, - Param, - ParseIntPipe, - Post, - Put, - Query, - Req, - UseGuards, -} from "@nestjs/common"; +import { Body, Controller, Get, HttpCode, Param, ParseIntPipe, Post, Put, Query, Req, UseGuards } from "@nestjs/common"; import { ApiBadRequestResponse, - ApiBearerAuth, ApiCreatedResponse, ApiForbiddenResponse, ApiNoContentResponse, @@ -39,11 +26,12 @@ import { AuditLog } from "@services/audit-log.service"; import { SigFoxApiDeviceTypeService } from "@services/sigfox/sigfox-api-device-type.service"; import { SigfoxApiUsersService } from "@services/sigfox/sigfox-api-users.service"; import { SigFoxGroupService } from "@services/sigfox/sigfox-group.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("SigFox") @Controller("sigfox-device-type") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() export class SigfoxDeviceTypeController { @@ -60,9 +48,7 @@ export class SigfoxDeviceTypeController { @Req() req: AuthenticatedRequest, @Query("groupId", new ParseIntPipe()) groupId: number ): Promise { - const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword( - groupId - ); + const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword(groupId); checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationRead); const sigfoxApiGroup = await this.usersService.getByUserId(group.username, group); @@ -77,9 +63,7 @@ export class SigfoxDeviceTypeController { @Param("id") id: string, @Query("groupId", new ParseIntPipe()) groupId: number ): Promise { - const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword( - groupId - ); + const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword(groupId); checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationRead); return await this.service.getById(group, id); @@ -95,18 +79,10 @@ export class SigfoxDeviceTypeController { @Query("groupId", new ParseIntPipe()) groupId: number ): Promise { try { - const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword( - groupId - ); + const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword(groupId); checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationWrite); const res = await this.service.create(group, dto); - AuditLog.success( - ActionType.CREATE, - "SigfoxDeviceType", - req.user.userId, - res.id, - dto.name - ); + AuditLog.success(ActionType.CREATE, "SigfoxDeviceType", req.user.userId, res.id, dto.name); return res; } catch (err) { AuditLog.fail(ActionType.CREATE, "SigfoxDeviceType", req.user.userId); @@ -126,26 +102,12 @@ export class SigfoxDeviceTypeController { @Query("groupId", new ParseIntPipe()) groupId: number ): Promise { try { - const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword( - groupId - ); + const group: SigFoxGroup = await this.sigfoxGroupService.findOneWithPassword(groupId); checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationWrite); await this.service.update(group, id, dto); - AuditLog.success( - ActionType.UPDATE, - "SigfoxDeviceType", - req.user.userId, - id, - dto.name - ); + AuditLog.success(ActionType.UPDATE, "SigfoxDeviceType", req.user.userId, id, dto.name); } catch (err) { - AuditLog.fail( - ActionType.UPDATE, - "SigfoxDeviceType", - req.user.userId, - id, - dto.name - ); + AuditLog.fail(ActionType.UPDATE, "SigfoxDeviceType", req.user.userId, id, dto.name); throw err; } } diff --git a/src/controllers/admin-controller/sigfox/sigfox-group.controller.ts b/src/controllers/admin-controller/sigfox/sigfox-group.controller.ts index fa291927..7fdf6e90 100644 --- a/src/controllers/admin-controller/sigfox/sigfox-group.controller.ts +++ b/src/controllers/admin-controller/sigfox/sigfox-group.controller.ts @@ -14,7 +14,6 @@ import { UseGuards, } from "@nestjs/common"; import { - ApiBearerAuth, ApiCreatedResponse, ApiForbiddenResponse, ApiOkResponse, @@ -23,8 +22,8 @@ import { ApiTags, } from "@nestjs/swagger"; -import { ComposeAuthGuard } from '@auth/compose-auth.guard'; -import { Read, ApplicationAdmin } from "@auth/roles.decorator"; +import { ComposeAuthGuard } from "@auth/compose-auth.guard"; +import { ApplicationAdmin, Read } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; import { CreateSigFoxGroupRequestDto } from "@dto/sigfox/internal/create-sigfox-group-request.dto"; @@ -39,11 +38,12 @@ import { ErrorCodes } from "@enum/error-codes.enum"; import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @ApiTags("SigFox") @Controller("sigfox-group") @UseGuards(ComposeAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @Read() @ApiForbiddenResponse() export class SigfoxGroupController { @@ -70,15 +70,12 @@ export class SigfoxGroupController { @ApiProduces("application/json") @ApiOperation({ summary: "Get one group by ID" }) @Read() - async getOne( - @Req() req: AuthenticatedRequest, - @Param("id", new ParseIntPipe()) id: number - ): Promise { + async getOne(@Req() req: AuthenticatedRequest, @Param("id", new ParseIntPipe()) id: number): Promise { let group: SigFoxGroup; try { group = await this.service.findOne(id); } catch (err) { - return null; + return null; } checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationRead); return group; @@ -89,27 +86,17 @@ export class SigfoxGroupController { @ApiOperation({ summary: "Create a SigFox Group connection" }) @ApiCreatedResponse() @ApplicationAdmin() - async create( - @Req() req: AuthenticatedRequest, - @Body() query: CreateSigFoxGroupRequestDto - ): Promise { + async create(@Req() req: AuthenticatedRequest, @Body() query: CreateSigFoxGroupRequestDto): Promise { try { checkIfUserHasAccessToOrganization(req, query.organizationId, OrganizationAccessScope.ApplicationWrite); const group = await this.service.create(query, req.user.userId); - AuditLog.success( - ActionType.CREATE, - SigFoxGroup.name, - req.user.userId, - group.id - ); + AuditLog.success(ActionType.CREATE, SigFoxGroup.name, req.user.userId, group.id); return group; } catch (err) { AuditLog.fail(ActionType.CREATE, SigFoxGroup.name, req.user.userId); if (err.message.startsWith(this.DUPLICATE_KEY_ERROR)) { - throw new BadRequestException( - ErrorCodes.GroupCanOnlyBeCreatedOncePrOrganization - ); + throw new BadRequestException(ErrorCodes.GroupCanOnlyBeCreatedOncePrOrganization); } throw err; } @@ -134,19 +121,12 @@ export class SigfoxGroupController { checkIfUserHasAccessToOrganization(req, group.belongsTo.id, OrganizationAccessScope.ApplicationWrite); try { const changeGroup = await this.service.update(group, dto, req.user.userId); - AuditLog.success( - ActionType.UPDATE, - SigFoxGroup.name, - req.user.userId, - group.id - ); + AuditLog.success(ActionType.UPDATE, SigFoxGroup.name, req.user.userId, group.id); return changeGroup; } catch (err) { AuditLog.fail(ActionType.CREATE, SigFoxGroup.name, req.user.userId, id); if (err.message.startsWith(this.DUPLICATE_KEY_ERROR)) { - throw new BadRequestException( - ErrorCodes.GroupCanOnlyBeCreatedOncePrOrganization - ); + throw new BadRequestException(ErrorCodes.GroupCanOnlyBeCreatedOncePrOrganization); } throw err; } diff --git a/src/controllers/api-key/api-key-info.controller.ts b/src/controllers/api-key/api-key-info.controller.ts index 1a0b4ce0..4b6ffc75 100644 --- a/src/controllers/api-key/api-key-info.controller.ts +++ b/src/controllers/api-key/api-key-info.controller.ts @@ -3,26 +3,13 @@ import { RolesGuard } from "@auth/roles.guard"; import { AuthenticatedRequest } from "@dto/internal/authenticated-request"; import { ListAllEntitiesDto } from "@dto/list-all-entities.dto"; import { Organization } from "@entities/organization.entity"; -import { - BadRequestException, - Controller, - Get, - Logger, - Query, - Req, - UseGuards, -} from "@nestjs/common"; -import { - ApiBearerAuth, - ApiForbiddenResponse, - ApiOperation, - ApiTags, - ApiUnauthorizedResponse, -} from "@nestjs/swagger"; +import { BadRequestException, Controller, Get, Logger, Query, Req, UseGuards } from "@nestjs/common"; +import { ApiForbiddenResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from "@nestjs/swagger"; import { ApiKeyInfoService } from "@services/api-key-info/api-key-info.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseGuards(ApiKeyAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @ApiTags("API key info") @@ -37,13 +24,12 @@ export class ApiKeyInfoController { @Req() req: AuthenticatedRequest, @Query() query?: ListAllEntitiesDto ): Promise { - // The API Key will have access to at least read from a specific + // The API Key will have access to at least read from a specific const allowedOrganizations = req.user.permissions.getAllOrganizationsWithAtLeastApplicationRead(); if (allowedOrganizations.length !== 1) { this.logger.error( - "API key is possibly tied to more than one organization. API key system user id: " + - req.user.userId + "API key is possibly tied to more than one organization. API key system user id: " + req.user.userId ); throw new BadRequestException(); } diff --git a/src/controllers/api-key/api-key.controller.ts b/src/controllers/api-key/api-key.controller.ts index 91d429b1..66ec62ea 100644 --- a/src/controllers/api-key/api-key.controller.ts +++ b/src/controllers/api-key/api-key.controller.ts @@ -1,5 +1,5 @@ import { JwtAuthGuard } from "@auth/jwt-auth.guard"; -import { Read, UserAdmin } from "@auth/roles.decorator"; +import { UserAdmin } from "@auth/roles.decorator"; import { RolesGuard } from "@auth/roles.guard"; import { ApiKeyResponseDto } from "@dto/api-key/api-key-response.dto"; import { CreateApiKeyDto } from "@dto/api-key/create-api-key.dto"; @@ -20,60 +20,45 @@ import { Param, ParseIntPipe, Post, + Put, Query, Req, UseGuards, - Put, } from "@nestjs/common"; import { - ApiBearerAuth, + ApiBadRequestResponse, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse, - ApiBadRequestResponse, } from "@nestjs/swagger"; import { ApiKeyService } from "@services/api-key-management/api-key.service"; import { AuditLog } from "@services/audit-log.service"; import { OrganizationService } from "@services/user-management/organization.service"; import { UpdateApiKeyDto } from "@dto/api-key/update-api-key.dto"; -import { - checkIfUserHasAccessToOrganization, - OrganizationAccessScope, -} from "@helpers/security-helper"; +import { checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseGuards(JwtAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @UserAdmin() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @ApiTags("API Key Management") @Controller("api-key") export class ApiKeyController { - constructor( - private apiKeyService: ApiKeyService, - private organizationService: OrganizationService - ) {} + constructor(private apiKeyService: ApiKeyService, private organizationService: OrganizationService) {} @Post() @ApiOperation({ summary: "Create new API key" }) - async createApiKey( - @Req() req: AuthenticatedRequest, - @Body() dto: CreateApiKeyDto - ): Promise { + async createApiKey(@Req() req: AuthenticatedRequest, @Body() dto: CreateApiKeyDto): Promise { await this.checkIfUserHasAccessToPermissions(req, dto.permissionIds); try { const result = await this.apiKeyService.create(dto, req.user.userId); - AuditLog.success( - ActionType.CREATE, - ApiKey.name, - req.user.userId, - result.id, - result.name - ); + AuditLog.success(ActionType.CREATE, ApiKey.name, req.user.userId, result.id, result.name); return result; } catch (err) { AuditLog.fail(ActionType.CREATE, ApiKey.name, req.user.userId); @@ -94,13 +79,7 @@ export class ApiKeyController { try { const result = await this.apiKeyService.update(id, dto, req.user.userId); - AuditLog.success( - ActionType.UPDATE, - ApiKey.name, - req.user.userId, - result.id, - result.name - ); + AuditLog.success(ActionType.UPDATE, ApiKey.name, req.user.userId, result.id, result.name); return result; } catch (err) { AuditLog.fail(ActionType.UPDATE, ApiKey.name, req.user.userId, id); @@ -133,11 +112,7 @@ export class ApiKeyController { @Req() req: AuthenticatedRequest, @Query() query: ListAllApiKeysDto ): Promise { - checkIfUserHasAccessToOrganization( - req, - query.organizationId, - OrganizationAccessScope.UserAdministrationWrite - ); + checkIfUserHasAccessToOrganization(req, query.organizationId, OrganizationAccessScope.UserAdministrationWrite); try { return this.apiKeyService.findAllByOrganizationId(query); @@ -170,22 +145,13 @@ export class ApiKeyController { ); } - private async checkIfUserHasAccessToPermissions( - req: AuthenticatedRequest, - permissionIds: number[] - ) { + private async checkIfUserHasAccessToPermissions(req: AuthenticatedRequest, permissionIds: number[]) { if (!permissionIds?.length) throw new ForbiddenException(); - const apiKeyOrganizations = await this.organizationService.findByPermissionIds( - permissionIds - ); + const apiKeyOrganizations = await this.organizationService.findByPermissionIds(permissionIds); for (const id of apiKeyOrganizations.map(org => org.id)) { - checkIfUserHasAccessToOrganization( - req, - id, - OrganizationAccessScope.UserAdministrationWrite - ); + checkIfUserHasAccessToOrganization(req, id, OrganizationAccessScope.UserAdministrationWrite); } } } diff --git a/src/controllers/user-management/auth.controller.ts b/src/controllers/user-management/auth.controller.ts index 0b31bb27..e300d544 100644 --- a/src/controllers/user-management/auth.controller.ts +++ b/src/controllers/user-management/auth.controller.ts @@ -11,12 +11,7 @@ import { UseFilters, UseGuards, } from "@nestjs/common"; -import { - ApiBearerAuth, - ApiOperation, - ApiTags, - ApiUnauthorizedResponse, -} from "@nestjs/swagger"; +import { ApiOperation, ApiTags, ApiUnauthorizedResponse } from "@nestjs/swagger"; import * as _ from "lodash"; import * as fs from "fs"; @@ -43,6 +38,7 @@ import { CustomExceptionFilter } from "@auth/custom-exception-filter"; import { isOrganizationPermission } from "@helpers/security-helper"; import { RequestWithUser } from "passport-saml/lib/passport-saml/types"; import Configuration from "@config/configuration"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseFilters(new CustomExceptionFilter()) @ApiTags("Auth") @@ -67,10 +63,7 @@ export class AuthController { @Post("kombit/login/callback") @ApiOperation({ summary: "Login callback from Kombit adgangsstyring" }) @UseGuards(KombitAuthGuard) - async kombitLoginCallback( - @Req() req: AuthenticatedRequestKombitStrategy, - @Res() res: Response - ): Promise { + async kombitLoginCallback(@Req() req: AuthenticatedRequestKombitStrategy, @Res() res: Response): Promise { const redirectTarget = req.cookies["redirect"]; // Login without proper roles @@ -78,9 +71,7 @@ export class AuthController { if (req.user == ErrorCodes.MissingRole) { // Send back to frontend with an error if (redirectTarget) { - return res.redirect( - `${redirectTarget}?error=${ErrorCodes.MissingRole}` - ); + return res.redirect(`${redirectTarget}?error=${ErrorCodes.MissingRole}`); } else { throw new UnauthorizedException(ErrorCodes.MissingRole); } @@ -90,9 +81,7 @@ export class AuthController { const { nameId, id } = req.user; const jwt = await this.authService.issueJwt(nameId, id, true); - const baseUrl = redirectTarget - ? redirectTarget - : Configuration()["frontend"]["baseurl"]; + const baseUrl = redirectTarget ? redirectTarget : Configuration()["frontend"]["baseurl"]; return res.redirect(`${baseUrl}?jwt=${jwt.accessToken}`); } @@ -108,8 +97,7 @@ export class AuthController { // - nameID is used. Corresponds to user.nameId in DB // - nameIDFormat is used. Correspond to in the public certificate reqConverted.samlLogoutRequest = null; // Property must be set, but it is unused in the source code - reqConverted.user.nameIDFormat = - "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"; + reqConverted.user.nameIDFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"; this.strategy.logout(reqConverted, (err: Error, url: string): void => { req.logout(err1 => { @@ -119,9 +107,7 @@ export class AuthController { res.redirect(url); } else { this.logger.error( - `Logout failed with error: ${JSON.stringify( - err - )} and inner Err: ${JSON.stringify(err1)}` + `Logout failed with error: ${JSON.stringify(err)} and inner Err: ${JSON.stringify(err1)}` ); } }); @@ -131,10 +117,7 @@ export class AuthController { @Get("kombit/logout/callback") // @UseGuards(KombitAuthGuard) @ApiOperation({ summary: "Handles the SAML logout" }) - public async logoutCallback( - @Req() req: expressRequest, - @Res() res: Response - ): Promise { + public async logoutCallback(@Req() req: expressRequest, @Res() res: Response): Promise { this.logger.debug("Get callback Logging out ..."); // This callback openes in a new window for some reason, without sending something to it a timout error happens res.send("Logged out ..."); @@ -155,10 +138,7 @@ export class AuthController { @ApiOperation({ summary: "Login using username and password" }) @ApiUnauthorizedResponse() @UseGuards(LocalAuthGuard) - async login( - @Request() req: AuthenticatedRequestLocalStrategy, - @Body() _: LoginDto - ): Promise { + async login(@Request() req: AuthenticatedRequestLocalStrategy, @Body() _: LoginDto): Promise { const { email, id } = req.user; return this.authService.issueJwt(email, id, false); } @@ -167,7 +147,7 @@ export class AuthController { @ApiOperation({ summary: "Return id and username (email) of the user logged in", }) - @ApiBearerAuth() + @ApiAuth() @UseGuards(JwtAuthGuard) async getProfile(@Request() req: AuthenticatedRequest): Promise { return { @@ -178,14 +158,11 @@ export class AuthController { @Get("me") @ApiOperation({ - summary: - "Get basic info on the current user and the organizations it has some permissions to.", + summary: "Get basic info on the current user and the organizations it has some permissions to.", }) - @ApiBearerAuth() + @ApiAuth() @UseGuards(JwtAuthGuard) - async getInfoAboutCurrentUser( - @Request() req: AuthenticatedRequest - ): Promise { + async getInfoAboutCurrentUser(@Request() req: AuthenticatedRequest): Promise { const user = await this.userService.findOneWithOrganizations(req.user.userId); const orgs = await this.getAllowedOrganisations(req, user); return { @@ -194,10 +171,7 @@ export class AuthController { }; } - private async getAllowedOrganisations( - req: AuthenticatedRequest, - user: User - ): Promise { + private async getAllowedOrganisations(req: AuthenticatedRequest, user: User): Promise { if (req.user.permissions.isGlobalAdmin) { return (await this.organisationService.findAll()).data; } diff --git a/src/controllers/user-management/new-kombit-creation.controller.ts b/src/controllers/user-management/new-kombit-creation.controller.ts index f3d62adf..cd61b685 100644 --- a/src/controllers/user-management/new-kombit-creation.controller.ts +++ b/src/controllers/user-management/new-kombit-creation.controller.ts @@ -24,7 +24,6 @@ import { UseGuards, } from "@nestjs/common"; import { - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -35,9 +34,10 @@ import { AuditLog } from "@services/audit-log.service"; import { OrganizationService } from "@services/user-management/organization.service"; import { PermissionService } from "@services/user-management/permission.service"; import { UserService } from "@services/user-management/user.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseGuards(JwtAuthGuard) -@ApiBearerAuth() +@ApiAuth() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @ApiTags("KombitEmailCreation") @@ -47,19 +47,14 @@ export class NewKombitCreationController { private organizationService: OrganizationService, private userService: UserService, private permissionService: PermissionService - ) { } + ) {} @Put("createNewKombitUser") @ApiOperation({ summary: "Create kombit-user Email" }) - async newKombitUser( - @Req() req: AuthenticatedRequest, - @Body() dto: CreateNewKombitUserDto - ): Promise { + async newKombitUser(@Req() req: AuthenticatedRequest, @Body() dto: CreateNewKombitUserDto): Promise { try { const dbUser: User = await this.userService.findOne(req.user.userId); - const permissions = await this.permissionService.findManyWithRelations( - dto.requestedOrganizationIds - ); + const permissions = await this.permissionService.findManyWithRelations(dto.requestedOrganizationIds); const requestedOrganizations: Organization[] = await this.organizationService.mapPermissionsToOrganizations( permissions @@ -71,25 +66,12 @@ export class NewKombitCreationController { throw new BadRequestException(ErrorCodes.EmailAlreadyInUse); } - const updatedUser: User = await this.userService.newKombitUser( - dto, - requestedOrganizations, - dbUser - ); - - for ( - let index = 0; - index < dto.requestedOrganizationIds.length; - index++ - ) { - const dbOrg = await this.organizationService.findByIdWithUsers( - requestedOrganizations[index].id - ); - - await this.organizationService.updateAwaitingUsers( - dbOrg, - updatedUser - ); + const updatedUser: User = await this.userService.newKombitUser(dto, requestedOrganizations, dbUser); + + for (let index = 0; index < dto.requestedOrganizationIds.length; index++) { + const dbOrg = await this.organizationService.findByIdWithUsers(requestedOrganizations[index].id); + + await this.organizationService.updateAwaitingUsers(dbOrg, updatedUser); } AuditLog.success(ActionType.UPDATE, User.name, req.user.userId); @@ -105,8 +87,7 @@ export class NewKombitCreationController { @Get("minimal") @ApiOperation({ - summary: - "Get list of the minimal representation of organizations, i.e. id and name.", + summary: "Get list of the minimal representation of organizations, i.e. id and name.", }) async findAllMinimal(): Promise { return await this.organizationService.findAllMinimal(); @@ -131,15 +112,10 @@ export class NewKombitCreationController { updateUserOrgsDto.requestedOrganizationIds ); - const requestedOrganizations = this.organizationService.mapPermissionsToOrganizations( - permissions - ); + const requestedOrganizations = this.organizationService.mapPermissionsToOrganizations(permissions); for (let index = 0; index < requestedOrganizations.length; index++) { - await this.userService.sendOrganizationRequestMail( - user, - requestedOrganizations[index] - ); + await this.userService.sendOrganizationRequestMail(user, requestedOrganizations[index]); } for (const org of requestedOrganizations) { diff --git a/src/controllers/user-management/organization.controller.ts b/src/controllers/user-management/organization.controller.ts index b8a0a05a..4900413b 100644 --- a/src/controllers/user-management/organization.controller.ts +++ b/src/controllers/user-management/organization.controller.ts @@ -14,7 +14,6 @@ import { UseGuards, } from "@nestjs/common"; import { - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -39,19 +38,17 @@ import { OrganizationService } from "@services/user-management/organization.serv import { AuditLog } from "@services/audit-log.service"; import { ActionType } from "@entities/audit-log-entry"; import { ListAllEntitiesDto } from "@dto/list-all-entities.dto"; -import { UserService } from "@services/user-management/user.service"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseGuards(JwtAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @ApiTags("User Management") @Controller("organization") @GlobalAdmin() export class OrganizationController { - constructor( - private organizationService: OrganizationService, - ) {} + constructor(private organizationService: OrganizationService) {} private readonly logger = new Logger(OrganizationController.name); @Post() @@ -61,17 +58,8 @@ export class OrganizationController { @Body() createOrganizationDto: CreateOrganizationDto ): Promise { try { - const organization = await this.organizationService.create( - createOrganizationDto, - req.user.userId - ); - AuditLog.success( - ActionType.CREATE, - Organization.name, - req.user.userId, - organization.id, - organization.name - ); + const organization = await this.organizationService.create(createOrganizationDto, req.user.userId); + AuditLog.success(ActionType.CREATE, Organization.name, req.user.userId, organization.id, organization.name); return organization; } catch (err) { AuditLog.fail(ActionType.CREATE, Organization.name, req.user.userId); @@ -88,18 +76,8 @@ export class OrganizationController { @Body() updateOrganizationDto: UpdateOrganizationDto ): Promise { try { - const organization = await this.organizationService.update( - id, - updateOrganizationDto, - req.user.userId - ); - AuditLog.success( - ActionType.UPDATE, - Organization.name, - req.user.userId, - organization.id, - organization.name - ); + const organization = await this.organizationService.update(id, updateOrganizationDto, req.user.userId); + AuditLog.success(ActionType.UPDATE, Organization.name, req.user.userId, organization.id, organization.name); return organization; } catch (err) { AuditLog.fail(ActionType.UPDATE, Organization.name, req.user.userId, id); @@ -112,8 +90,7 @@ export class OrganizationController { @Get("minimal") @ApiOperation({ - summary: - "Get list of the minimal representation of organizations, i.e. id and name.", + summary: "Get list of the minimal representation of organizations, i.e. id and name.", }) @Read() async findAllMinimal(): Promise { @@ -131,10 +108,7 @@ export class OrganizationController { return this.organizationService.findAllPaginated(query); } else { const allowedOrganizations = req.user.permissions.getAllOrganizationsWithUserAdmin(); - return this.organizationService.findAllInOrganizationList( - allowedOrganizations, - query - ); + return this.organizationService.findAllInOrganizationList(allowedOrganizations, query); } } diff --git a/src/controllers/user-management/permission.controller.ts b/src/controllers/user-management/permission.controller.ts index 3225dd12..46707994 100644 --- a/src/controllers/user-management/permission.controller.ts +++ b/src/controllers/user-management/permission.controller.ts @@ -1,5 +1,5 @@ -import { BadRequestException, Query } from "@nestjs/common"; import { + BadRequestException, Body, Controller, Delete, @@ -9,11 +9,11 @@ import { ParseIntPipe, Post, Put, + Query, Req, UseGuards, } from "@nestjs/common"; import { - ApiBearerAuth, ApiForbiddenResponse, ApiNotFoundResponse, ApiOperation, @@ -31,8 +31,8 @@ import { AuthenticatedRequest } from "@entities/dto/internal/authenticated-reque import { Permission } from "@entities/permissions/permission.entity"; import { PermissionType } from "@enum/permission-type.enum"; import { - checkIfUserIsGlobalAdmin, checkIfUserHasAccessToOrganization, + checkIfUserIsGlobalAdmin, OrganizationAccessScope, } from "@helpers/security-helper"; import { PermissionService } from "@services/user-management/permission.service"; @@ -49,9 +49,10 @@ import { PermissionRequestAcceptUser } from "@dto/user-management/add-user-to-pe import { OrganizationService } from "@services/user-management/organization.service"; import { Organization } from "@entities/organization.entity"; import { User } from "@entities/user.entity"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseGuards(JwtAuthGuard, RolesGuard) -@ApiBearerAuth() +@ApiAuth() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @ApiTags("User Management") @@ -67,10 +68,7 @@ export class PermissionController { @Post() @ApiOperation({ summary: "Create new permission entity" }) @UserAdmin() - async createPermission( - @Req() req: AuthenticatedRequest, - @Body() dto: CreatePermissionDto - ): Promise { + async createPermission(@Req() req: AuthenticatedRequest, @Body() dto: CreatePermissionDto): Promise { try { checkIfUserHasAccessToOrganization( req, @@ -78,18 +76,9 @@ export class PermissionController { OrganizationAccessScope.UserAdministrationWrite ); - const result = await this.permissionService.createNewPermission( - dto, - req.user.userId - ); + const result = await this.permissionService.createNewPermission(dto, req.user.userId); - AuditLog.success( - ActionType.CREATE, - Permission.name, - req.user.userId, - result.id, - result.name - ); + AuditLog.success(ActionType.CREATE, Permission.name, req.user.userId, result.id, result.name); return result; } catch (err) { AuditLog.fail(ActionType.CREATE, Permission.name, req.user.userId); @@ -110,13 +99,9 @@ export class PermissionController { OrganizationAccessScope.UserAdministrationWrite ); - const permissions = await this.permissionService.findOneWithRelations( - dto.organizationId - ); + const permissions = await this.permissionService.findOneWithRelations(dto.organizationId); - const org: Organization = this.organizationService.mapPermissionsToOneOrganization( - permissions - ); + const org: Organization = this.organizationService.mapPermissionsToOneOrganization(permissions); const user: User = await this.userService.findOne(dto.userId); const newUserPermissions: Permission[] = []; @@ -127,19 +112,9 @@ export class PermissionController { } } - const resultUser = await this.userService.acceptUser( - user, - org, - newUserPermissions - ); + const resultUser = await this.userService.acceptUser(user, org, newUserPermissions); - AuditLog.success( - ActionType.UPDATE, - Permission.name, - req.user.userId, - resultUser.id, - resultUser.name - ); + AuditLog.success(ActionType.UPDATE, Permission.name, req.user.userId, resultUser.id, resultUser.name); return resultUser; } catch (err) { AuditLog.fail(ActionType.UPDATE, Permission.name, req.user.userId); @@ -167,19 +142,9 @@ export class PermissionController { ); } - const result = await this.permissionService.updatePermission( - id, - dto, - req.user.userId - ); + const result = await this.permissionService.updatePermission(id, dto, req.user.userId); - AuditLog.success( - ActionType.UPDATE, - Permission.name, - req.user.userId, - result.id, - result.name - ); + AuditLog.success(ActionType.UPDATE, Permission.name, req.user.userId, result.id, result.name); return result; } catch (err) { AuditLog.fail(ActionType.UPDATE, Permission.name, req.user.userId, id); @@ -224,10 +189,7 @@ export class PermissionController { ): Promise { if (!req.user.permissions.isGlobalAdmin && query.organisationId === undefined) { const allowedOrganizations = req.user.permissions.getAllOrganizationsWithUserAdmin(); - return this.permissionService.getAllPermissionsInOrganizations( - allowedOrganizations, - query - ); + return this.permissionService.getAllPermissionsInOrganizations(allowedOrganizations, query); } return this.permissionService.getAllPermissions(query); } @@ -273,10 +235,7 @@ export class PermissionController { let applicationsPromise; let permission; try { - applicationsPromise = this.applicationService.getApplicationsOnPermissionId( - id, - query - ); + applicationsPromise = this.applicationService.getApplicationsOnPermissionId(id, query); permission = await this.permissionService.getPermission(id); } catch (err) { throw new NotFoundException(); diff --git a/src/controllers/user-management/user.controller.ts b/src/controllers/user-management/user.controller.ts index 0eff0a94..6b31ef63 100644 --- a/src/controllers/user-management/user.controller.ts +++ b/src/controllers/user-management/user.controller.ts @@ -15,13 +15,7 @@ import { Req, UseGuards, } from "@nestjs/common"; -import { - ApiBearerAuth, - ApiForbiddenResponse, - ApiOperation, - ApiTags, - ApiUnauthorizedResponse, -} from "@nestjs/swagger"; +import { ApiForbiddenResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from "@nestjs/swagger"; import { QueryFailedError } from "typeorm"; import { JwtAuthGuard } from "@auth/jwt-auth.guard"; @@ -47,19 +41,17 @@ import { ListAllEntitiesDto } from "@dto/list-all-entities.dto"; import { OrganizationService } from "@services/user-management/organization.service"; import { Organization } from "@entities/organization.entity"; import { RejectUserDto } from "@dto/user-management/reject-user.dto"; +import { ApiAuth } from "@auth/swagger-auth-decorator"; @UseGuards(JwtAuthGuard, RolesGuard) @UserAdmin() -@ApiBearerAuth() +@ApiAuth() @ApiForbiddenResponse() @ApiUnauthorizedResponse() @ApiTags("User Management") @Controller("user") export class UserController { - constructor( - private userService: UserService, - private organizationService: OrganizationService - ) {} + constructor(private userService: UserService, private organizationService: OrganizationService) {} private readonly logger = new Logger(UserController.name); @@ -71,27 +63,15 @@ export class UserController { @Post() @ApiOperation({ summary: "Create a new User" }) - async create( - @Req() req: AuthenticatedRequest, - @Body() createUserDto: CreateUserDto - ): Promise { + async create(@Req() req: AuthenticatedRequest, @Body() createUserDto: CreateUserDto): Promise { if (createUserDto.globalAdmin) { checkIfUserIsGlobalAdmin(req); } try { // Don't leak the passwordHash // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { passwordHash, ...user } = await this.userService.createUser( - createUserDto, - req.user.userId - ); - AuditLog.success( - ActionType.CREATE, - User.name, - req.user.userId, - user.id, - user.name - ); + const { passwordHash, ...user } = await this.userService.createUser(createUserDto, req.user.userId); + AuditLog.success(ActionType.CREATE, User.name, req.user.userId, user.id, user.name); return user; } catch (err) { @@ -110,15 +90,8 @@ export class UserController { @Put("/rejectUser") @ApiOperation({ summary: "Rejects user and removes from awaiting users" }) - async rejectUser( - @Req() req: AuthenticatedRequest, - @Body() body: RejectUserDto - ): Promise { - checkIfUserHasAccessToOrganization( - req, - body.orgId, - OrganizationAccessScope.UserAdministrationWrite - ); + async rejectUser(@Req() req: AuthenticatedRequest, @Body() body: RejectUserDto): Promise { + checkIfUserHasAccessToOrganization(req, body.orgId, OrganizationAccessScope.UserAdministrationWrite); const user = await this.userService.findOne(body.userIdToReject); const organization = await this.organizationService.findByIdWithUsers(body.orgId); @@ -141,9 +114,7 @@ export class UserController { // _OR_ be global admin if ( !req.user.permissions.isGlobalAdmin && - !dbUser.permissions.some(perm => - req.user.permissions.hasUserAdminOnOrganization(perm.organization.id) - ) + !dbUser.permissions.some(perm => req.user.permissions.hasUserAdminOnOrganization(perm.organization.id)) ) { throw new ForbiddenException(); } @@ -154,18 +125,8 @@ export class UserController { } // Don't leak the passwordHash - const { passwordHash: _, ...user } = await this.userService.updateUser( - id, - dto, - req.user.userId - ); - AuditLog.success( - ActionType.UPDATE, - User.name, - req.user.userId, - user.id, - user.name - ); + const { passwordHash: _, ...user } = await this.userService.updateUser(id, dto, req.user.userId); + AuditLog.success(ActionType.UPDATE, User.name, req.user.userId, user.id, user.name); return user; } catch (err) { @@ -186,13 +147,7 @@ export class UserController { async hideWelcome(@Req() req: AuthenticatedRequest): Promise { const wasOk = await this.userService.hideWelcome(req.user.userId); - AuditLog.success( - ActionType.UPDATE, - User.name, - req.user.userId, - req.user.userId, - req.user.username - ); + AuditLog.success(ActionType.UPDATE, User.name, req.user.userId, req.user.userId, req.user.username); return wasOk; } @@ -248,10 +203,7 @@ export class UserController { const getExtendedInfo = extendedInfo != null ? extendedInfo : false; try { // Don't leak the passwordHash - const { passwordHash: _, ...user } = await this.userService.findOne( - id, - getExtendedInfo - ); + const { passwordHash: _, ...user } = await this.userService.findOne(id, getExtendedInfo); return user; } catch (err) { @@ -261,8 +213,7 @@ export class UserController { @Get("organizationUsers/:organizationId") @ApiOperation({ - summary: - "Get all users for an organization. Requires UserAdmin priviledges for the specified organization", + summary: "Get all users for an organization. Requires UserAdmin priviledges for the specified organization", }) async findByOrganizationId( @Req() req: AuthenticatedRequest, @@ -272,9 +223,7 @@ export class UserController { try { // Check if user has access to organization if (!req.user.permissions.hasUserAdminOnOrganization(organizationId)) { - throw new ForbiddenException( - "User does not have org admin permissions for this organization" - ); + throw new ForbiddenException("User does not have org admin permissions for this organization"); } // Get user objects diff --git a/src/loaders/swagger.ts b/src/loaders/swagger.ts index 5631bde3..336b56e6 100644 --- a/src/loaders/swagger.ts +++ b/src/loaders/swagger.ts @@ -7,6 +7,7 @@ export function setupSwagger(app: INestApplication, SWAGGER_PREFIX: string): voi .setDescription("The back-end for OS2IoT") .setVersion("1.0") .addTag("os2iot") + .addApiKey({ type: "apiKey", name: "X-API-KEY", in: "header" }, "X-API-KEY") .addBearerAuth({ type: "http", scheme: "bearer", bearerFormat: "JWT" }) .build(); const document = SwaggerModule.createDocument(app, options); diff --git a/src/services/user-management/auth.service.ts b/src/services/user-management/auth.service.ts index c4ba3c4a..74dedbc8 100644 --- a/src/services/user-management/auth.service.ts +++ b/src/services/user-management/auth.service.ts @@ -25,7 +25,7 @@ export class AuthService { private readonly logger = new Logger(AuthService.name); private readonly KOMBIT_ROLE_URI: string; - async validateUser(username: string, password: string): Promise { + public async validateUser(username: string, password: string): Promise { const user = await this.usersService.findOneUserByEmailWithPassword(username); if (user) { if (!user.active) { @@ -47,65 +47,7 @@ export class AuthService { return null; } - private async getPrivilegesIntermediate(profile: Profile): Promise { - const xml = profile.getAssertionXml(); - const parser = this.getXmlParser(); - - return await parser - .parseStringPromise(xml) - .then((doc: XMLOutput) => { - const assertion = doc["Assertion"]; - const privilegesNode = assertion["AttributeStatement"][0][ - "Attribute" - ].find((x: XMLOutput) => { - return ( - x["$"]["Name"] == "dk:gov:saml:attribute:Privileges_intermediate" - ); - }); - const base64Xml = privilegesNode["AttributeValue"][0]["_"]; - - return base64Xml; - }) - .catch((err: any) => { - this.logger.error("Err: " + err); - this.logger.error("Could not load attribute in SAML response"); - return null; - }); - } - - private async isAllowed(privilegesBase64: string): Promise { - const decodedXml = Buffer.from(privilegesBase64, "base64").toString("binary"); - - const parser = this.getXmlParser(); - - return await parser - .parseStringPromise(decodedXml) - .then((doc: XMLOutput) => { - return doc["PrivilegeList"]["PrivilegeGroup"].some( - (privilegeGroups: XMLOutput) => - privilegeGroups["Privilege"].some( - (privileges: XMLOutput) => - privileges["_"].indexOf(this.KOMBIT_ROLE_URI) > -1 - ) - ); - }) - .catch((err: any) => { - this.logger.error("Could not find privileges in result"); - return false; - }); - } - - private getXmlParser() { - const parserConfig = { - explicitRoot: true, - explicitCharkey: true, - tagNameProcessors: [xml2js.processors.stripPrefix], - }; - const parser = new xml2js.Parser(parserConfig); - return parser; - } - - async validateKombitUser(profile: Profile): Promise { + public async validateKombitUser(profile: Profile): Promise { const privilegesBase64 = await this.getPrivilegesIntermediate(profile); if (!privilegesBase64 || !this.isAllowed(privilegesBase64)) { // User doesn't have brugersystemrolle ... @@ -114,17 +56,13 @@ export class AuthService { // Check if they have attribute to allow them into OS2IOT let user = await this.usersService.findOneByNameId(profile.nameID); if (user) { - this.logger.debug( - `User from Kombit ('${profile.nameID}') already exists with id: ${user.id}` - ); + this.logger.debug(`User from Kombit ('${profile.nameID}') already exists with id: ${user.id}`); if (!user.active) { this.logger.debug(`User (${user.id}) is disabled, not allowed!`); throw new UnauthorizedException(ErrorCodes.UserInactive); } } else { - this.logger.debug( - `User from Kombit ('${profile.nameID}') does not already exist, will create.` - ); + this.logger.debug(`User from Kombit ('${profile.nameID}') does not already exist, will create.`); user = await this.usersService.createUserFromKombit(profile); } @@ -134,18 +72,14 @@ export class AuthService { return user; } - async issueJwt( - email: string, - id: number, - isKombit?: boolean - ): Promise { + public async issueJwt(email: string, id: number, isKombit?: boolean): Promise { const payload: JwtPayloadDto = { username: email, sub: id, isKombit: isKombit }; return { accessToken: this.jwtService.sign(payload), }; } - async validateApiKey(apiKey: string): Promise { + public async validateApiKey(apiKey: string): Promise { const apiKeyDb = await this.apiKeyService.findOne(apiKey); if (!apiKeyDb) { @@ -154,4 +88,56 @@ export class AuthService { return apiKeyDb; } + + private getXmlParser() { + const parserConfig = { + explicitRoot: true, + explicitCharkey: true, + tagNameProcessors: [xml2js.processors.stripPrefix], + }; + const parser = new xml2js.Parser(parserConfig); + return parser; + } + + private async getPrivilegesIntermediate(profile: Profile): Promise { + const xml = profile.getAssertionXml(); + const parser = this.getXmlParser(); + + return await parser + .parseStringPromise(xml) + .then((doc: XMLOutput) => { + const assertion = doc["Assertion"]; + const privilegesNode = assertion["AttributeStatement"][0]["Attribute"].find((x: XMLOutput) => { + return x["$"]["Name"] == "dk:gov:saml:attribute:Privileges_intermediate"; + }); + const base64Xml = privilegesNode["AttributeValue"][0]["_"]; + + return base64Xml; + }) + .catch((err: any) => { + this.logger.error("Err: " + err); + this.logger.error("Could not load attribute in SAML response"); + return null; + }); + } + + private async isAllowed(privilegesBase64: string): Promise { + const decodedXml = Buffer.from(privilegesBase64, "base64").toString("binary"); + + const parser = this.getXmlParser(); + + return await parser + .parseStringPromise(decodedXml) + .then((doc: XMLOutput) => { + return doc["PrivilegeList"]["PrivilegeGroup"].some((privilegeGroups: XMLOutput) => + privilegeGroups["Privilege"].some( + (privileges: XMLOutput) => privileges["_"].indexOf(this.KOMBIT_ROLE_URI) > -1 + ) + ); + }) + .catch((err: any) => { + this.logger.error("Could not find privileges in result"); + return false; + }); + } }