mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
fix: prevent user from deleting their own roles
This commit is contained in:
parent
549b946452
commit
cb64234ab1
@ -8,10 +8,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
||||||
import { NotFoundException } from '@nestjs/common';
|
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { MongooseModule } from '@nestjs/mongoose';
|
import { MongooseModule } from '@nestjs/mongoose';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { Session as ExpressSession } from 'express-session';
|
||||||
|
|
||||||
import { AttachmentRepository } from '@/attachment/repositories/attachment.repository';
|
import { AttachmentRepository } from '@/attachment/repositories/attachment.repository';
|
||||||
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
|
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
|
||||||
@ -42,7 +43,6 @@ describe('RoleController', () => {
|
|||||||
let roleService: RoleService;
|
let roleService: RoleService;
|
||||||
let permissionService: PermissionService;
|
let permissionService: PermissionService;
|
||||||
let userService: UserService;
|
let userService: UserService;
|
||||||
let notFoundId: string;
|
|
||||||
let roleAdmin: Role;
|
let roleAdmin: Role;
|
||||||
let rolePublic: Role;
|
let rolePublic: Role;
|
||||||
|
|
||||||
@ -190,17 +190,48 @@ describe('RoleController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('deleteOne', () => {
|
describe('deleteOne', () => {
|
||||||
it('should delete role by id', async () => {
|
it("should throw ForbiddenException if the role is part of the user's roles", async () => {
|
||||||
const result = await roleController.deleteOne(roleAdmin.id);
|
const session = { passport: { user: { id: 'user1' } } } as ExpressSession;
|
||||||
notFoundId = roleAdmin.id;
|
const roleId = 'role1';
|
||||||
expect(result).toEqual({ acknowledged: true, deletedCount: 1 });
|
|
||||||
|
userService.findOneAndPopulate = jest.fn().mockResolvedValue({
|
||||||
|
roles: [{ id: roleId }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(roleController.deleteOne(roleId, session)).rejects.toThrow(
|
||||||
|
ForbiddenException,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw a NotFoundException when attempting to delete a role by id', async () => {
|
it('should throw NotFoundException if the role is not found', async () => {
|
||||||
await expect(roleController.deleteOne(notFoundId)).rejects.toThrow(
|
const session = { passport: { user: { id: 'user1' } } } as ExpressSession;
|
||||||
|
const roleId = 'role2';
|
||||||
|
|
||||||
|
userService.findOneAndPopulate = jest.fn().mockResolvedValue({
|
||||||
|
roles: [{ id: 'role1' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
roleService.deleteOne = jest.fn().mockResolvedValue({ deletedCount: 0 });
|
||||||
|
|
||||||
|
await expect(roleController.deleteOne(roleId, session)).rejects.toThrow(
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return the result if the role is successfully deleted', async () => {
|
||||||
|
const session = { passport: { user: { id: 'user1' } } } as ExpressSession;
|
||||||
|
const roleId = 'role2';
|
||||||
|
|
||||||
|
userService.findOneAndPopulate = jest.fn().mockResolvedValue({
|
||||||
|
roles: [{ id: 'role1' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteResult = { deletedCount: 1 };
|
||||||
|
roleService.deleteOne = jest.fn().mockResolvedValue(deleteResult);
|
||||||
|
|
||||||
|
const result = await roleController.deleteOne(roleId, session);
|
||||||
|
expect(result).toEqual(deleteResult);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateOne', () => {
|
describe('updateOne', () => {
|
||||||
@ -225,6 +256,13 @@ describe('RoleController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw a NotFoundException when attempting to modify a role', async () => {
|
it('should throw a NotFoundException when attempting to modify a role', async () => {
|
||||||
|
const notFoundId = 'nonexistentRoleId';
|
||||||
|
const roleUpdateDto = { name: 'newRoleName' };
|
||||||
|
|
||||||
|
roleService.updateOne = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(new NotFoundException());
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
roleController.updateOne(notFoundId, roleUpdateDto),
|
roleController.updateOne(notFoundId, roleUpdateDto),
|
||||||
).rejects.toThrow(NotFoundException);
|
).rejects.toThrow(NotFoundException);
|
||||||
|
@ -19,8 +19,11 @@ import {
|
|||||||
Patch,
|
Patch,
|
||||||
Query,
|
Query,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
|
Session,
|
||||||
|
ForbiddenException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
|
import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
|
||||||
|
import { Session as ExpressSession } from 'express-session';
|
||||||
import { TFilterQuery } from 'mongoose';
|
import { TFilterQuery } from 'mongoose';
|
||||||
|
|
||||||
import { CsrfInterceptor } from '@/interceptors/csrf.interceptor';
|
import { CsrfInterceptor } from '@/interceptors/csrf.interceptor';
|
||||||
@ -34,6 +37,7 @@ import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
|||||||
import { RoleCreateDto, RoleUpdateDto } from '../dto/role.dto';
|
import { RoleCreateDto, RoleUpdateDto } from '../dto/role.dto';
|
||||||
import { Role, RoleStub } from '../schemas/role.schema';
|
import { Role, RoleStub } from '../schemas/role.schema';
|
||||||
import { RoleService } from '../services/role.service';
|
import { RoleService } from '../services/role.service';
|
||||||
|
import { UserService } from '../services/user.service';
|
||||||
|
|
||||||
@UseInterceptors(CsrfInterceptor)
|
@UseInterceptors(CsrfInterceptor)
|
||||||
@Controller('role')
|
@Controller('role')
|
||||||
@ -41,6 +45,7 @@ export class RoleController extends BaseController<Role, RoleStub> {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly roleService: RoleService,
|
private readonly roleService: RoleService,
|
||||||
private readonly logger: LoggerService,
|
private readonly logger: LoggerService,
|
||||||
|
private readonly userService: UserService,
|
||||||
) {
|
) {
|
||||||
super(roleService);
|
super(roleService);
|
||||||
}
|
}
|
||||||
@ -142,12 +147,24 @@ export class RoleController extends BaseController<Role, RoleStub> {
|
|||||||
@CsrfCheck(true)
|
@CsrfCheck(true)
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@HttpCode(204)
|
@HttpCode(204)
|
||||||
async deleteOne(@Param('id') id: string) {
|
async deleteOne(@Param('id') id: string, @Session() session: ExpressSession) {
|
||||||
const result = await this.roleService.deleteOne(id);
|
const roles = (
|
||||||
if (result.deletedCount === 0) {
|
await this.userService.findOneAndPopulate(session.passport?.user?.id, [
|
||||||
this.logger.warn(`Unable to delete Role by id ${id}`);
|
'roles',
|
||||||
throw new NotFoundException(`Role with ID ${id} not found`);
|
])
|
||||||
|
).roles.map((role) => role.id);
|
||||||
|
if (roles.includes(id)) {
|
||||||
|
throw new ForbiddenException("Your account's role can't be deleted");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const result = await this.roleService.deleteOne(id);
|
||||||
|
if (result.deletedCount === 0) {
|
||||||
|
throw new NotFoundException(`Role with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new NotFoundException(`Role with ID ${id} not found`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,8 @@ export const Roles = () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { mutateAsync: deleteRole } = useDelete(EntityType.ROLE, {
|
const { mutateAsync: deleteRole } = useDelete(EntityType.ROLE, {
|
||||||
onError: () => {
|
onError: (error) => {
|
||||||
toast.error(t("message.internal_server_error"));
|
toast.error(t(error.message || "message.internal_server_error"));
|
||||||
},
|
},
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
deleteDialogCtl.closeDialog();
|
deleteDialogCtl.closeDialog();
|
||||||
|
Loading…
Reference in New Issue
Block a user