fix: Rechteausweitung ueber Rollenzuweisung und weitere Auth-Haertung
Ein Sicherheitsaudit deckte auf, dass jede Rolle mit users:update sich selbst per PATCH /users/:id auf eine Admin-Rolle heraufstufen konnte, da roleId ohne eigene Berechtigung und ohne Selbstbezug-Pruefung akzeptiert wurde. Ausserdem lag ein fest im Code hinterlegtes Standard-Admin-Passwort vor, das bei fehlender SEED_ADMIN_PASSWORD-Env greift. - Neue Berechtigung users:assign-role, getrennt von users:create/update, fuer jede Rollenzuweisung erforderlich - Selbstaenderung der eigenen Rolle serverseitig blockiert (auch fuer Admins), ebenso Selbst-Reset der eigenen MFA ueber die Admin-Route - Seed erzeugt bei fehlender Env ein zufaelliges statt eines fixen Passworts - Wiederverwendung eines bereits rotierten Refresh-Tokens (Diebstahl- Indiz) widerruft nun alle Sessions des betroffenen Nutzers - Rate-Limit auf MFA-Enrollment-Bestaetigung ergaenzt Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
7bd2a3dc3a
commit
1b16b49913
7 files changed
+146
-7
No files matched your search
@@ -1,4 +1,8 @@
|
||||
import { ConflictException, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
ConflictException,
|
||||
ForbiddenException,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AuditLogService } from '../audit/audit-log.service';
|
||||
@@ -22,6 +26,7 @@ describe('UsersService', () => {
|
||||
let permissionResolver: {
|
||||
invalidateUser: jest.Mock;
|
||||
invalidateAll: jest.Mock;
|
||||
hasAny: jest.Mock;
|
||||
};
|
||||
|
||||
const staffRole = {
|
||||
@@ -77,6 +82,7 @@ describe('UsersService', () => {
|
||||
permissionResolver = {
|
||||
invalidateUser: jest.fn(),
|
||||
invalidateAll: jest.fn(),
|
||||
hasAny: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
usersService = new UsersService(
|
||||
prisma as unknown as PrismaService,
|
||||
@@ -324,4 +330,53 @@ describe('UsersService', () => {
|
||||
).rejects.toBeInstanceOf(ConflictException);
|
||||
expect(prisma.user.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('update throws ForbiddenException when the caller tries to change their own role', async () => {
|
||||
prisma.user.findUnique.mockResolvedValue(adminUser);
|
||||
|
||||
await expect(
|
||||
usersService.update(adminUser.id, { roleId: staffRole.id }, adminUser.id),
|
||||
).rejects.toBeInstanceOf(ForbiddenException);
|
||||
expect(prisma.user.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('update throws ForbiddenException when the caller lacks users:assign-role', async () => {
|
||||
prisma.user.findUnique.mockResolvedValue(existingUser);
|
||||
prisma.role.findUnique.mockResolvedValue(adminRole);
|
||||
permissionResolver.hasAny.mockResolvedValue(false);
|
||||
|
||||
await expect(
|
||||
usersService.update(
|
||||
existingUser.id,
|
||||
{ roleId: adminRole.id },
|
||||
adminUser.id,
|
||||
),
|
||||
).rejects.toBeInstanceOf(ForbiddenException);
|
||||
expect(prisma.user.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('create throws ForbiddenException when the caller lacks users:assign-role', async () => {
|
||||
prisma.user.findUnique.mockResolvedValue(null);
|
||||
permissionResolver.hasAny.mockResolvedValue(false);
|
||||
|
||||
await expect(
|
||||
usersService.create(
|
||||
{
|
||||
email: '[email protected]',
|
||||
password: 'password123',
|
||||
name: 'New User',
|
||||
roleId: staffRole.id,
|
||||
},
|
||||
adminUser.id,
|
||||
),
|
||||
).rejects.toBeInstanceOf(ForbiddenException);
|
||||
expect(prisma.user.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('resetMfa throws ForbiddenException when targeting your own account', async () => {
|
||||
await expect(
|
||||
usersService.resetMfa(adminUser.id, adminUser.id),
|
||||
).rejects.toBeInstanceOf(ForbiddenException);
|
||||
expect(prisma.user.update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user