mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 18:27:05 +00:00
Add user device permission guards
This commit is contained in:
@ -1,97 +0,0 @@
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||
import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories';
|
||||
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
HttpStatus,
|
||||
ExecutionContext,
|
||||
BadRequestException,
|
||||
applyDecorators,
|
||||
SetMetadata,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
|
||||
@Injectable()
|
||||
export class DevicePermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository,
|
||||
private readonly permissionTypeRepository: PermissionTypeRepository,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
try {
|
||||
const { deviceId } = req.headers;
|
||||
const userId = req.user.uuid;
|
||||
|
||||
const requirePermission =
|
||||
this.reflector.getAllAndOverride<PermissionType>('permission', [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
if (!requirePermission) {
|
||||
return true;
|
||||
}
|
||||
await this.checkDevicePermission(deviceId, userId, requirePermission);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkDevicePermission(
|
||||
deviceId: string,
|
||||
userId: string,
|
||||
requirePermission,
|
||||
) {
|
||||
const [userPermissionDetails, permissionDetails] = await Promise.all([
|
||||
this.deviceUserPermissionRepository.findOne({
|
||||
where: { deviceUuid: deviceId, userUuid: userId },
|
||||
}),
|
||||
this.permissionTypeRepository.findOne({
|
||||
where: {
|
||||
type: requirePermission,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
if (!userPermissionDetails) {
|
||||
throw new BadRequestException('User Permission Details Not Found');
|
||||
}
|
||||
if (userPermissionDetails.permissionType.uuid !== permissionDetails.uuid) {
|
||||
throw new BadRequestException(
|
||||
`User Does not have a ${requirePermission}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof BadRequestException) {
|
||||
response
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
||||
} else {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: 'User Permission not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function AuthGuardWithRoles(permission?: string) {
|
||||
return applyDecorators(
|
||||
SetMetadata('permission', permission),
|
||||
UseGuards(JwtAuthGuard),
|
||||
UseGuards(DevicePermissionGuard),
|
||||
);
|
||||
}
|
92
src/guards/user.device.controllable.permission.guard.ts
Normal file
92
src/guards/user.device.controllable.permission.guard.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class CheckUserHaveControllablePermission implements CanActivate {
|
||||
constructor(
|
||||
private readonly deviceRepository: DeviceRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
|
||||
try {
|
||||
const userUuid = req.user.uuid;
|
||||
const { deviceUuid } = req.params;
|
||||
|
||||
const userIsFound = await this.checkUserIsFound(userUuid);
|
||||
if (!userIsFound) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const userDevicePermission = await this.checkUserDevicePermission(
|
||||
userUuid,
|
||||
deviceUuid,
|
||||
);
|
||||
|
||||
if (userDevicePermission === PermissionType.CONTROLLABLE) {
|
||||
return true;
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
'You do not have controllable access to this device',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async checkUserIsFound(userUuid: string) {
|
||||
const userData = await this.userRepository.findOne({
|
||||
where: { uuid: userUuid },
|
||||
});
|
||||
return !!userData;
|
||||
}
|
||||
|
||||
private async checkUserDevicePermission(
|
||||
userUuid: string,
|
||||
deviceUuid: string,
|
||||
): Promise<string> {
|
||||
const device = await this.deviceRepository.findOne({
|
||||
where: { uuid: deviceUuid, permission: { userUuid: userUuid } },
|
||||
relations: ['permission', 'permission.permissionType'],
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
throw new BadRequestException(
|
||||
'You do not have controllable access to this device',
|
||||
);
|
||||
}
|
||||
|
||||
return device.permission[0].permissionType.type; // Assuming permissionType is a string
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
if (error instanceof NotFoundException) {
|
||||
response
|
||||
.status(HttpStatus.NOT_FOUND)
|
||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
||||
} else if (error instanceof BadRequestException) {
|
||||
response.status(HttpStatus.BAD_REQUEST).json({
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
message: error.message,
|
||||
});
|
||||
} else {
|
||||
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
91
src/guards/user.device.permission.guard.ts
Normal file
91
src/guards/user.device.permission.guard.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class CheckUserHavePermission implements CanActivate {
|
||||
constructor(
|
||||
private readonly deviceRepository: DeviceRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
|
||||
try {
|
||||
const userUuid = req.user.uuid;
|
||||
const { deviceUuid } = req.params;
|
||||
|
||||
const userIsFound = await this.checkUserIsFound(userUuid);
|
||||
if (!userIsFound) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const userDevicePermission = await this.checkUserDevicePermission(
|
||||
userUuid,
|
||||
deviceUuid,
|
||||
);
|
||||
|
||||
if (
|
||||
userDevicePermission === PermissionType.READ ||
|
||||
userDevicePermission === PermissionType.CONTROLLABLE
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
throw new BadRequestException('You do not have access to this device');
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async checkUserIsFound(userUuid: string) {
|
||||
const userData = await this.userRepository.findOne({
|
||||
where: { uuid: userUuid },
|
||||
});
|
||||
return !!userData;
|
||||
}
|
||||
|
||||
private async checkUserDevicePermission(
|
||||
userUuid: string,
|
||||
deviceUuid: string,
|
||||
): Promise<string> {
|
||||
const device = await this.deviceRepository.findOne({
|
||||
where: { uuid: deviceUuid, permission: { userUuid: userUuid } },
|
||||
relations: ['permission', 'permission.permissionType'],
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
throw new BadRequestException('You do not have access to this device');
|
||||
}
|
||||
|
||||
return device.permission[0].permissionType.type; // Assuming permissionType is a string
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
if (error instanceof NotFoundException) {
|
||||
response
|
||||
.status(HttpStatus.NOT_FOUND)
|
||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
||||
} else if (error instanceof BadRequestException) {
|
||||
response.status(HttpStatus.BAD_REQUEST).json({
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
message: error.message,
|
||||
});
|
||||
} else {
|
||||
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user