fix(权限): 修复版本详情写入权限校验

This commit is contained in:
2026-07-08 19:27:41 +08:00
parent cafa0f134a
commit deebc5404a
5 changed files with 191 additions and 17 deletions

View File

@@ -5,16 +5,19 @@ import type { CurrentUser } from './auth-context.service';
describe('PermissionService', () => {
const versionFindUnique = jest.fn();
const projectMemberFindUnique = jest.fn();
const appDataFindUnique = jest.fn();
const prisma = {
version: { findUnique: versionFindUnique },
projectMember: { findUnique: projectMemberFindUnique },
appData: { findUnique: appDataFindUnique },
} as any;
const service = new PermissionService(prisma);
beforeEach(() => {
jest.clearAllMocks();
appDataFindUnique.mockResolvedValue(null);
});
it('allows the built-in super admin wildcard for any permission without a scope lookup', async () => {
@@ -26,13 +29,13 @@ describe('PermissionService', () => {
expect(projectMemberFindUnique).not.toHaveBeenCalled();
});
it('requires scoped users to belong to the project resolved from a version', async () => {
it('denies version work mutations for project members outside the version members list', async () => {
versionFindUnique.mockResolvedValue({ id: 'version-1', projectId: 'project-1', members: [] });
projectMemberFindUnique.mockResolvedValue({ projectId: 'project-1', userId: 'dev-1', role: 'member' });
await expect(service.can(currentUser({ id: 'dev-1', roleId: 'role-dev' }), 'version.devtask:manage', {
versionId: 'version-1',
})).resolves.toBe(true);
})).resolves.toBe(false);
expect(versionFindUnique).toHaveBeenCalledWith({
where: { id: 'version-1' },
@@ -43,6 +46,39 @@ describe('PermissionService', () => {
});
});
it('allows version work mutations when the user has the role permission and is a version member', async () => {
versionFindUnique.mockResolvedValue({
id: 'version-1',
projectId: 'project-1',
members: [{ id: 'dev-1', name: 'Dev One' }],
});
projectMemberFindUnique.mockResolvedValue({ projectId: 'project-1', userId: 'dev-1', role: 'member' });
await expect(service.can(currentUser({ id: 'dev-1', name: 'Dev One', roleId: 'role-dev' }), 'version.devtask:manage', {
versionId: 'version-1',
})).resolves.toBe(true);
});
it('uses configured role permissions before built-in role defaults', async () => {
appDataFindUnique.mockResolvedValue({
value: {
roles: [
{ id: 'role-dev', permissions: ['version.devtask:view'] },
],
},
});
versionFindUnique.mockResolvedValue({
id: 'version-1',
projectId: 'project-1',
members: [{ id: 'dev-1', name: 'Dev One' }],
});
projectMemberFindUnique.mockResolvedValue({ projectId: 'project-1', userId: 'dev-1', role: 'member' });
await expect(service.can(currentUser({ id: 'dev-1', name: 'Dev One', roleId: 'role-dev' }), 'version.devtask:manage', {
versionId: 'version-1',
})).resolves.toBe(false);
});
it('allows a version member when no project membership exists', async () => {
versionFindUnique.mockResolvedValue({
id: 'version-1',