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/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/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/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; + }); + } }