Skip to content

Commit

Permalink
Feature/IOT-1583: Move application (#255)
Browse files Browse the repository at this point in the history
* Added endpoint for updating permission ids.

* rename updateOrganization to changeOrganization. Also fail log on error.

* Added endpoint to get all orgs with application admin. Also added save, with default permissions if none specified.

---------

Co-authored-by: August Andersen <[email protected]>
  • Loading branch information
MadsApollo and August Andersen authored Sep 13, 2024
1 parent ee442fe commit a022186
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 8 deletions.
38 changes: 31 additions & 7 deletions src/controllers/admin-controller/application.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,21 @@ import {
ApiUnauthorizedResponse,
} from "@nestjs/swagger";

import { ComposeAuthGuard } from "@auth/compose-auth.guard";
import { ApplicationAdmin, Read } from "@auth/roles.decorator";
import { RolesGuard } from "@auth/roles.guard";
import { ApiAuth } from "@auth/swagger-auth-decorator";
import { CreateApplicationDto } from "@dto/create-application.dto";
import { DeleteResponseDto } from "@dto/delete-application-response.dto";
import { ListAllApplicationsResponseDto } from "@dto/list-all-applications-response.dto";
import { ListAllApplicationsDto } from "@dto/list-all-applications.dto";
import { ListAllEntitiesDto } from "@dto/list-all-entities.dto";
import { ListAllIoTDevicesResponseDto } from "@dto/list-all-iot-devices-response.dto";
import { IoTDevicesListToMapResponseDto } from "@dto/list-all-iot-devices-to-map-response.dto";
import { UpdateApplicationOrganizationDto } from "@dto/update-application-organization.dto";
import { UpdateApplicationDto } from "@dto/update-application.dto";
import { Application } from "@entities/application.entity";
import { ActionType } from "@entities/audit-log-entry";
import { AuthenticatedRequest } from "@entities/dto/internal/authenticated-request";
import { ErrorCodes } from "@enum/error-codes.enum";
import {
Expand All @@ -42,14 +49,8 @@ import {
checkIfUserHasAccessToOrganization,
OrganizationAccessScope,
} from "@helpers/security-helper";
import { ApplicationService } from "@services/device-management/application.service";
import { AuditLog } from "@services/audit-log.service";
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";
import { IoTDevicesListToMapResponseDto } from "@dto/list-all-iot-devices-to-map-response.dto";
import { ApplicationService } from "@services/device-management/application.service";

@ApiTags("Application")
@Controller("application")
Expand Down Expand Up @@ -186,6 +187,29 @@ export class ApplicationController {
return application;
}

@ApplicationAdmin()
@Put("updateApplicationOrganization/:id")
@Header("Cache-Control", "none")
@ApiOperation({ summary: "Update application organization" })
@ApiBadRequestResponse()
async changeOrganization(
@Req() req: AuthenticatedRequest,
@Param("id", new ParseIntPipe()) id: number,
@Body() updateApplicationDto: UpdateApplicationOrganizationDto
): Promise<Application> {
checkIfUserHasAccessToApplication(req, id, ApplicationAccessScope.Write);
try {
const application = await this.applicationService.changeOrganization(id, updateApplicationDto, req.user.userId);

AuditLog.success(ActionType.UPDATE, Application.name, req.user.userId, application.id, application.name);
return application;
} catch (error) {
AuditLog.fail(ActionType.UPDATE, Application.name, req.user.userId);

throw error;
}
}

@ApplicationAdmin()
@Delete(":id")
@ApiOperation({ summary: "Delete an existing Application" })
Expand Down
17 changes: 16 additions & 1 deletion src/controllers/user-management/organization.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from "@nestjs/swagger";

import { JwtAuthGuard } from "@auth/jwt-auth.guard";
import { GlobalAdmin, Read, UserAdmin } from "@auth/roles.decorator";
import { ApplicationAdmin, GlobalAdmin, Read, UserAdmin } from "@auth/roles.decorator";
import { RolesGuard } from "@auth/roles.guard";
import { DeleteResponseDto } from "@dto/delete-application-response.dto";
import { AuthenticatedRequest } from "@dto/internal/authenticated-request";
Expand Down Expand Up @@ -114,6 +114,21 @@ export class OrganizationController {
}
}

@Get("applicationAdmin")
@ApiOperation({ summary: "Get list of all Organizations" })
@ApplicationAdmin()
async findAllWithApplicationAdmin(
@Req() req: AuthenticatedRequest,
@Query() query?: ListAllEntitiesDto
): Promise<ListAllOrganizationsResponseDto> {
if (req.user.permissions.isGlobalAdmin) {
return this.organizationService.findAllPaginated(query);
} else {
const allowedOrganizations = req.user.permissions.getAllOrganizationsWithApplicationAdmin();
return this.organizationService.findAllInOrganizationList(allowedOrganizations, query);
}
}

@Get(":id")
@ApiOperation({ summary: "Get one Organization" })
@ApiNotFoundResponse()
Expand Down
4 changes: 4 additions & 0 deletions src/entities/dto/update-application-organization.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class UpdateApplicationOrganizationDto {
public organizationId: number;
public permissionIds: number[];
}
1 change: 1 addition & 0 deletions src/services/chirpstack/chirpstack-application.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export class ApplicationChirpstackService extends GenericChirpstackConfiguration
application.setDescription(dto.description ? dto.description : this.DEFAULT_DESCRIPTION);
application.setName(this.applicationNamePrefix + dto.name);
application.setTenantId(await this.getDefaultOrganizationId());
application.getTagsMap().set(this.ORG_ID_KEY, dto.belongsTo.id.toString());
req.setApplication(application);
try {
await this.put("applications", this.applicationServiceClient, req);
Expand Down
39 changes: 39 additions & 0 deletions src/services/device-management/application.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ import { DataTargetType } from "@enum/data-target-type.enum";
import { MulticastService } from "@services/chirpstack/multicast.service";
import { ApplicationChirpstackService } from "@services/chirpstack/chirpstack-application.service";
import { IoTDevicesListToMapResponseDto } from "@dto/list-all-iot-devices-to-map-response.dto";
import { UpdateApplicationOrganizationDto } from "@dto/update-application-organization.dto";
import { Permission } from "@entities/permissions/permission.entity";

@Injectable()
export class ApplicationService {
constructor(
@InjectRepository(Application)
private applicationRepository: Repository<Application>,
@InjectRepository(Permission)
private permissionRepository: Repository<Permission>,
@InjectRepository(IoTDevice)
private iotDeviceRepository: Repository<IoTDevice>,
@Inject(forwardRef(() => OrganizationService))
Expand Down Expand Up @@ -254,6 +258,41 @@ export class ApplicationService {
return this.applicationRepository.save(mappedApplication, {});
}

async changeOrganization(
id: number,
updateApplicationDto: UpdateApplicationOrganizationDto,
userId: number
): Promise<Application> {
const existingApplication = await this.applicationRepository.findOneOrFail({
where: { id },
});

let permissions = await this.permissionRepository.find({
where: { id: In(updateApplicationDto.permissionIds) },
relations: [nameof<Permission>("organization")],
});

const permissionOrganizationSet = new Set<number>(permissions.map(p => p.organization.id));
let newOrganization = [...permissionOrganizationSet].length !== 1 ? undefined : permissions[0].organization;

if (!newOrganization) {
newOrganization = await this.organizationService.findByIdWithPermissions(updateApplicationDto.organizationId);
permissions = newOrganization.permissions.filter(perm => perm.automaticallyAddNewApplications);
}

if (!newOrganization || newOrganization.id !== updateApplicationDto.organizationId) {
throw new BadRequestException(ErrorCodes.InvalidPost);
}

existingApplication.permissions = permissions;
existingApplication.belongsTo = newOrganization;

await this.chirpstackApplicationService.updateApplication(existingApplication);

existingApplication.updatedBy = userId;
return this.applicationRepository.save(existingApplication, {});
}

async delete(id: number): Promise<DeleteResult> {
const application = await this.applicationRepository.findOne({
where: { id },
Expand Down

0 comments on commit a022186

Please sign in to comment.