feat(server): doc level permission (#9760)

close CLOUD-89 CLOUD-90 CLOUD-91 CLOUD-92
This commit is contained in:
Brooooooklyn
2025-02-05 07:06:57 +00:00
parent 64de83b13d
commit abeff8bb1a
36 changed files with 2257 additions and 324 deletions
@@ -13,7 +13,7 @@ import { AppModule } from '../app.module';
import { EventBus } from '../base';
import { AuthService } from '../core/auth';
import { DocContentService } from '../core/doc-renderer';
import { Permission, PermissionService } from '../core/permission';
import { PermissionService, WorkspaceRole } from '../core/permission';
import { QuotaManagementService, QuotaService, QuotaType } from '../core/quota';
import { WorkspaceType } from '../core/workspaces';
import {
@@ -29,7 +29,6 @@ import {
inviteUser,
inviteUsers,
leaveWorkspace,
PermissionEnum,
revokeInviteLink,
revokeMember,
revokeUser,
@@ -105,7 +104,7 @@ const init = async (
const invite = async (
email: string,
permission: PermissionEnum = 'Write',
permission: WorkspaceRole = WorkspaceRole.Collaborator,
shouldSendEmail: boolean = false
) => {
const member = await signUp(app, email.split('@')[0], email, '123456');
@@ -193,9 +192,12 @@ const init = async (
] as const;
};
const admin = await invite(`${prefix}admin@affine.pro`, 'Admin');
const admin = await invite(`${prefix}admin@affine.pro`, WorkspaceRole.Admin);
const write = await invite(`${prefix}write@affine.pro`);
const read = await invite(`${prefix}read@affine.pro`, 'Read');
const read = await invite(
`${prefix}read@affine.pro`,
WorkspaceRole.Collaborator
);
return {
invite,
@@ -268,7 +270,7 @@ test('should be able to check seat limit', async t => {
{
// invite
await t.throwsAsync(
invite('member3@affine.pro', 'Read'),
invite('member3@affine.pro', WorkspaceRole.Collaborator),
{ message: 'You have exceeded your workspace member quota.' },
'should throw error if exceed member limit'
);
@@ -276,7 +278,7 @@ test('should be able to check seat limit', async t => {
memberLimit: 5,
});
await t.notThrowsAsync(
invite('member4@affine.pro', 'Read'),
invite('member4@affine.pro', WorkspaceRole.Collaborator),
'should not throw error if not exceed member limit'
);
}
@@ -324,17 +326,35 @@ test('should be able to grant team member permission', async t => {
const { owner, teamWorkspace: ws, admin, write, read } = await init(app);
await t.throwsAsync(
grantMember(app, read.token.token, ws.id, write.id, 'Write'),
grantMember(
app,
read.token.token,
ws.id,
write.id,
WorkspaceRole.Collaborator
),
{ instanceOf: Error },
'should throw error if not owner'
);
await t.throwsAsync(
grantMember(app, write.token.token, ws.id, read.id, 'Write'),
grantMember(
app,
write.token.token,
ws.id,
read.id,
WorkspaceRole.Collaborator
),
{ instanceOf: Error },
'should throw error if not owner'
);
await t.throwsAsync(
grantMember(app, admin.token.token, ws.id, read.id, 'Write'),
grantMember(
app,
admin.token.token,
ws.id,
read.id,
WorkspaceRole.Collaborator
),
{ instanceOf: Error },
'should throw error if not owner'
);
@@ -342,15 +362,29 @@ test('should be able to grant team member permission', async t => {
{
// owner should be able to grant permission
t.true(
await permissions.tryCheckWorkspaceIs(ws.id, read.id, Permission.Read),
await permissions.tryCheckWorkspaceIs(
ws.id,
read.id,
WorkspaceRole.Collaborator
),
'should be able to check permission'
);
t.truthy(
await grantMember(app, owner.token.token, ws.id, read.id, 'Admin'),
await grantMember(
app,
owner.token.token,
ws.id,
read.id,
WorkspaceRole.Admin
),
'should be able to grant permission'
);
t.true(
await permissions.tryCheckWorkspaceIs(ws.id, read.id, Permission.Admin),
await permissions.tryCheckWorkspaceIs(
ws.id,
read.id,
WorkspaceRole.Admin
),
'should be able to check permission'
);
}
@@ -692,17 +726,33 @@ test('should be able to emit events', async t => {
{
const { teamWorkspace: tws, owner, read } = await init(app);
await grantMember(app, owner.token.token, tws.id, read.id, 'Admin');
await grantMember(
app,
owner.token.token,
tws.id,
read.id,
WorkspaceRole.Admin
);
t.deepEqual(
event.emit.lastCall.args,
[
'workspace.members.roleChanged',
{ userId: read.id, workspaceId: tws.id, permission: Permission.Admin },
{
userId: read.id,
workspaceId: tws.id,
permission: WorkspaceRole.Admin,
},
],
'should emit role changed event'
);
await grantMember(app, owner.token.token, tws.id, read.id, 'Owner');
await grantMember(
app,
owner.token.token,
tws.id,
read.id,
WorkspaceRole.Owner
);
const [ownershipTransferred] = event.emit
.getCalls()
.map(call => call.args)