feat: harden sessions and track downstream acknowledgements

This commit is contained in:
hectorzhao
2026-07-14 14:18:43 +08:00
parent 3d37adcc9f
commit 8c03663f24
43 changed files with 1733 additions and 150 deletions
+30 -9
View File
@@ -128,6 +128,8 @@ export class UsersService {
async update(id: string, data: UpdateUserDto, scopeTenantId?: string) {
const current = await this.getExisting(id, scopeTenantId);
const roleCode = data.roleCode ?? current.roles[0]?.role.code as UserRoleCode | undefined;
const roleChanged = data.roleCode !== undefined && data.roleCode !== current.roles[0]?.role.code;
const tenantChanged = data.tenantId !== undefined && data.tenantId !== current.tenantId;
this.assertUserInput({
tenantId: data.tenantId ?? current.tenantId ?? undefined,
username: data.username ?? current.username,
@@ -153,7 +155,7 @@ export class UsersService {
phone: data.phone === undefined ? undefined : normalizeOptional(data.phone),
displayName: data.displayName ?? current.displayName,
status: data.status ?? current.status,
...(data.status === 'disabled' && current.status !== 'disabled' ? { sessionVersion: { increment: 1 } } : {}),
...((data.status === 'disabled' && current.status !== 'disabled') || roleChanged || tenantChanged ? { sessionVersion: { increment: 1 } } : {}),
},
include: { tenant: true, roles: { include: { role: true } } },
});
@@ -235,6 +237,17 @@ export class UsersService {
return updated;
}
async verifyCurrentPassword(id: string, password: string) {
if (!password) {
throw new BadRequestException('请输入当前密码');
}
const current = await this.getExisting(id);
if (current.passwordHash !== hashPassword(password)) {
throw new BadRequestException('当前密码不正确');
}
return current;
}
listRoles() {
return this.prisma.role.findMany({
include: { permissions: { include: { permission: true } } },
@@ -262,18 +275,26 @@ export class UsersService {
}
assignRole(data: AssignRoleDto) {
return this.prisma.userRole.upsert({
where: { userId_roleId: { userId: data.userId, roleId: data.roleId } },
update: {},
create: data,
return this.prisma.$transaction(async (tx) => {
const assignment = await tx.userRole.upsert({
where: { userId_roleId: { userId: data.userId, roleId: data.roleId } },
update: {},
create: data,
});
await tx.user.update({ where: { id: data.userId }, data: { sessionVersion: { increment: 1 } } });
return assignment;
});
}
assignPermission(data: AssignPermissionDto) {
return this.prisma.rolePermission.upsert({
where: { roleId_permissionId: { roleId: data.roleId, permissionId: data.permissionId } },
update: {},
create: data,
return this.prisma.$transaction(async (tx) => {
const assignment = await tx.rolePermission.upsert({
where: { roleId_permissionId: { roleId: data.roleId, permissionId: data.permissionId } },
update: {},
create: data,
});
await tx.user.updateMany({ where: { roles: { some: { roleId: data.roleId } } }, data: { sessionVersion: { increment: 1 } } });
return assignment;
});
}