Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,7 @@ export class NewKombitCreationController {
try {
// Don't leak the passwordHash
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { passwordHash, ...user } = await this.userService.findOne(
id,
getExtendedInfo,
getExtendedInfo
);
const { passwordHash, ...user } = await this.userService.findOne(id, getExtendedInfo);

return user;
} catch (err) {
Expand Down
41 changes: 29 additions & 12 deletions src/controllers/user-management/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ import { CreateUserDto } from "@dto/user-management/create-user.dto";
import { UpdateUserDto } from "@dto/user-management/update-user.dto";
import { UserResponseDto } from "@dto/user-response.dto";
import { ErrorCodes } from "@entities/enum/error-codes.enum";
import { checkIfUserIsGlobalAdmin, checkIfUserHasAccessToOrganization, OrganizationAccessScope } from "@helpers/security-helper";
import {
checkIfUserIsGlobalAdmin,
checkIfUserHasAccessToOrganization,
OrganizationAccessScope,
} from "@helpers/security-helper";
import { UserService } from "@services/user-management/user.service";
import { ListAllUsersResponseDto } from "@dto/list-all-users-response.dto";
import { ListAllUsersMinimalResponseDto } from "@dto/list-all-users-minimal-response.dto";
Expand All @@ -56,7 +60,7 @@ export class UserController {
constructor(
private userService: UserService,
private organizationService: OrganizationService
) { }
) {}

private readonly logger = new Logger(UserController.name);

Expand Down Expand Up @@ -111,7 +115,11 @@ export class UserController {
@Req() req: AuthenticatedRequest,
@Body() body: RejectUserDto
): Promise<Organization> {
checkIfUserHasAccessToOrganization(req, body.orgId, OrganizationAccessScope.UserAdministrationWrite);
checkIfUserHasAccessToOrganization(
req,
body.orgId,
OrganizationAccessScope.UserAdministrationWrite
);

const user = await this.userService.findOne(body.userIdToReject);
const organization = await this.organizationService.findByIdWithUsers(body.orgId);
Expand All @@ -130,17 +138,22 @@ export class UserController {
// Verify that we have admin access to the user and that the user is on an organization
const dbUser = await this.userService.findOneWithOrganizations(id);

// Requesting user has to be admin for at least one organization containing the user
// Requesting user has to be admin for at least one organization containing the user
// _OR_ be global admin
if (!req.user.permissions.isGlobalAdmin && !dbUser.permissions.some(perm => req.user.permissions.hasUserAdminOnOrganization(perm.organization.id))) {
if (
!req.user.permissions.isGlobalAdmin &&
!dbUser.permissions.some(perm =>
req.user.permissions.hasUserAdminOnOrganization(perm.organization.id)
)
) {
throw new ForbiddenException();
}
}

// Only a global admin can modify a global admin user
if (dto.globalAdmin) {
checkIfUserIsGlobalAdmin(req);
}

// Don't leak the passwordHash
const { passwordHash: _, ...user } = await this.userService.updateUser(
id,
Expand Down Expand Up @@ -176,7 +189,7 @@ export class UserController {
req.user.username
);

return wasOk
return wasOk;
}

@Get("/awaitingUsers")
Expand Down Expand Up @@ -232,7 +245,6 @@ export class UserController {
// Don't leak the passwordHash
const { passwordHash: _, ...user } = await this.userService.findOne(
id,
getExtendedInfo,
getExtendedInfo
);

Expand All @@ -243,7 +255,10 @@ export class UserController {
}

@Get("organizationUsers/:organizationId")
@ApiOperation({ summary: "Get all users for an organization. Requires UserAdmin priviledges for the specified organization" })
@ApiOperation({
summary:
"Get all users for an organization. Requires UserAdmin priviledges for the specified organization",
})
async findByOrganizationId(
@Req() req: AuthenticatedRequest,
@Param("organizationId", new ParseIntPipe()) organizationId: number,
Expand All @@ -252,7 +267,9 @@ 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
Expand Down
8 changes: 4 additions & 4 deletions src/entities/dto/chirpstack/service-profile.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ export class ServiceProfileDto {

@ApiProperty({ required: false })
@IsInt()
@Min(0)
@Max(5)
@Min(0, { message: "Max data rate må ikke være negativ" })
@Max(7, { message: "Max data rate må ikke være større end 7" })
drMax?: number;

@ApiProperty({ required: true })
@IsInt()
@Min(0)
@Max(5)
@Min(0, { message: "Min data rate må ikke være negativ" })
@Max(7, { message: "Min data rate må ikke være større end 7" })
drMin?: number;

@ApiProperty({ required: false })
Expand Down
2 changes: 1 addition & 1 deletion src/entities/dto/list-all-permissions.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ListAllEntitiesDto } from "./list-all-entities.dto";

export class ListAllPermissionsDto extends ListAllEntitiesDto {
@ApiProperty({ type: String, required: false })
organisationId?: number;
organisationId?: string;

@ApiProperty({ type: String, required: false })
userId?: string;
Expand Down
5 changes: 3 additions & 2 deletions src/modules/device-management/application.module.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { Module, forwardRef } from "@nestjs/common";

import { forwardRef, Module } from "@nestjs/common";
import { ApplicationController } from "@admin-controller/application.controller";
import { SharedModule } from "@modules/shared.module";
import { OrganizationModule } from "@modules/user-management/organization.module";
import { ApplicationService } from "@services/device-management/application.service";
import { ChirpstackAdministrationModule } from "@modules/device-integrations/chirpstack-administration.module";
import { PermissionModule } from "@modules/user-management/permission.module";
import { MulticastModule } from "./multicast.module";
import { DataTargetModule } from "@modules/device-management/data-target.module";

@Module({
imports: [
SharedModule,
forwardRef(() => OrganizationModule),
forwardRef(() => PermissionModule),
forwardRef(() => MulticastModule), // because of circular reference
forwardRef(() => DataTargetModule),
ChirpstackAdministrationModule,
],
exports: [ApplicationService],
Expand Down
4 changes: 2 additions & 2 deletions src/modules/device-management/data-target.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import configuration from "@config/configuration";
import { ApplicationModule } from "@modules/device-management/application.module";
import { SharedModule } from "@modules/shared.module";
import { OrganizationModule } from "@modules/user-management/organization.module";
import { Module } from "@nestjs/common";
import { forwardRef, Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { DataTargetService } from "@services/data-targets/data-target.service";
import { OS2IoTMail } from "@services/os2iot-mail.service";
Expand All @@ -15,7 +15,7 @@ import {
@Module({
imports: [
SharedModule,
ApplicationModule,
forwardRef(() => ApplicationModule),
OrganizationModule,
ConfigModule.forRoot({ load: [configuration] }),
],
Expand Down
21 changes: 16 additions & 5 deletions src/services/data-targets/data-target.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,21 @@ import { OpenDataDkDataset } from "@entities/open-data-dk-dataset.entity";
import { dataTargetTypeMap } from "@enum/data-target-type-mapping";
import { DataTargetType } from "@enum/data-target-type.enum";
import { ErrorCodes } from "@enum/error-codes.enum";
import { BadRequestException, Inject, Injectable, Logger } from "@nestjs/common";
import {
BadRequestException,
forwardRef,
Inject,
Injectable,
Logger,
} from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { ApplicationService } from "@services/device-management/application.service";
import { OS2IoTMail } from "@services/os2iot-mail.service";
import { DeleteResult, Repository, SelectQueryBuilder } from "typeorm";
import { CLIENT_SECRET_PROVIDER, ClientSecretProvider } from "../../helpers/fiware-token.helper";
import {
CLIENT_SECRET_PROVIDER,
ClientSecretProvider,
} from "../../helpers/fiware-token.helper";
import { User } from "@entities/user.entity";

@Injectable()
Expand All @@ -27,9 +36,11 @@ export class DataTargetService {
private dataTargetRepository: Repository<DataTarget>,
@InjectRepository(User)
private userRepository: Repository<User>,
@Inject(forwardRef(() => ApplicationService))
private applicationService: ApplicationService,
@Inject(CLIENT_SECRET_PROVIDER) private clientSecretProvider: ClientSecretProvider,
private oS2IoTMail: OS2IoTMail,
@Inject(CLIENT_SECRET_PROVIDER)
private clientSecretProvider: ClientSecretProvider,
private oS2IoTMail: OS2IoTMail
) {}
private readonly logger = new Logger(DataTargetService.name);

Expand Down Expand Up @@ -97,7 +108,7 @@ export class DataTargetService {
): Promise<DataTarget[]> {
const res = await this.dataTargetRepository
.createQueryBuilder("dt")
.addSelect('dt.clientSecret')
.addSelect("dt.clientSecret")
.innerJoin(
"iot_device_payload_decoder_data_target_connection",
"con",
Expand Down
12 changes: 9 additions & 3 deletions src/services/device-management/application.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { OrganizationService } from "@services/user-management/organization.serv
import { PermissionService } from "@services/user-management/permission.service";
import { DeleteResult, In, Repository } from "typeorm";
import { MulticastService } from "./multicast.service";
import { DataTargetService } from "@services/data-targets/data-target.service";

@Injectable()
export class ApplicationService {
Expand All @@ -40,7 +41,9 @@ export class ApplicationService {
private multicastService: MulticastService,
private chirpstackDeviceService: ChirpstackDeviceService,
@Inject(forwardRef(() => PermissionService))
private permissionService: PermissionService
private permissionService: PermissionService,
@Inject(forwardRef(() => DataTargetService))
private dataTargetService: DataTargetService
) {}

async findAndCountInList(
Expand Down Expand Up @@ -262,7 +265,7 @@ export class ApplicationService {
async delete(id: number): Promise<DeleteResult> {
const application = await this.applicationRepository.findOne({
where: { id },
relations: ["iotDevices", "multicasts"],
relations: ["iotDevices", "multicasts", "dataTargets"],
});

// Don't allow delete if this application contains any sigfox devices.
Expand All @@ -274,6 +277,10 @@ export class ApplicationService {
throw new ConflictException(ErrorCodes.DeleteNotAllowedHasSigfoxDevice);
}

for (const dataTarget of application.dataTargets) {
await this.dataTargetService.delete(dataTarget.id);
}

// Delete all LoRaWAN devices in ChirpStack
const loRaWANDevices = application.iotDevices.filter(
device => device.type === IoTDeviceType.LoRaWAN
Expand All @@ -293,7 +300,6 @@ export class ApplicationService {
dbMulticast.lorawanMulticastDefinition.chirpstackGroupId
);
}

return this.applicationRepository.delete(id);
}

Expand Down
5 changes: 4 additions & 1 deletion src/services/user-management/permission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,10 @@ export class PermissionService {
}
if (orgs) {
qb = qb.andWhere({ organization: In(orgs) });
} else if (query?.organisationId) {
} else if (
query?.organisationId !== undefined &&
query.organisationId !== "undefined"
) {
qb = qb.andWhere("org.id = :orgId", { orgId: +query.organisationId });
}

Expand Down
22 changes: 10 additions & 12 deletions src/services/user-management/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class UserService {
@Inject(forwardRef(() => PermissionService))
private permissionService: PermissionService,
private configService: ConfigService,
private oS2IoTMail: OS2IoTMail,
private oS2IoTMail: OS2IoTMail
) {}

private readonly logger = new Logger(UserService.name, { timestamp: true });
Expand Down Expand Up @@ -90,17 +90,13 @@ export class UserService {
});
}

async findOne(
id: number,
getPermissionOrganisationInfo = false,
getPermissionUsersInfo = false
): Promise<User> {
async findOne(id: number, getExtendedInformation: boolean = false): Promise<User> {
const relations = ["permissions", "requestedOrganizations"];
if (getPermissionOrganisationInfo) {

if (getExtendedInformation) {
relations.push("permissions.organization");
}
if (getPermissionUsersInfo) {
relations.push("permissions.users");
relations.push("permissions.type");
}

return await this.userRepository.findOne({
Expand Down Expand Up @@ -221,7 +217,7 @@ export class UserService {
if (user.nameId != null) {
if (dto.name && user.name != dto.name) {
throw new BadRequestException(ErrorCodes.CannotModifyOnKombitUser);
}
}
if (dto.password) {
throw new BadRequestException(ErrorCodes.CannotModifyOnKombitUser);
}
Expand Down Expand Up @@ -352,11 +348,13 @@ export class UserService {
}
const order: "DESC" | "ASC" =
query?.sort?.toLocaleUpperCase() == "DESC" ? "DESC" : "ASC";

const [data, count] = await this.userRepository
.createQueryBuilder("user")
.innerJoin("user.permissions", "p")
.where('"p"."organizationId" = :organizationId', { organizationId: organizationId })
.where('"p"."organizationId" = :organizationId', {
organizationId: organizationId,
})
.take(+query.limit)
.skip(+query.offset)
.orderBy(orderBy, order)
Expand Down