refactor(server): role actions definition (#9962)

This commit is contained in:
forehalo
2025-02-06 04:54:34 +00:00
parent 31d251d44f
commit d3843d8f11
18 changed files with 773 additions and 1085 deletions
@@ -0,0 +1,20 @@
import { Type } from '@nestjs/common';
import { Field, ObjectType } from '@nestjs/graphql';
import { ApplyType } from '../utils/types';
export function registerObjectType<T>(
fields: Record<string, Type<any>>,
options: {
name: string;
}
) {
const Inner = ApplyType<T>();
for (const [key, value] of Object.entries(fields)) {
Field(() => value)(Inner.prototype, key);
}
ObjectType(options.name)(Inner);
return Inner;
}
@@ -16,6 +16,7 @@ export {
export * from './error'; export * from './error';
export { EventBus, OnEvent } from './event'; export { EventBus, OnEvent } from './event';
export type { GraphqlContext } from './graphql'; export type { GraphqlContext } from './graphql';
export { registerObjectType } from './graphql/register';
export * from './guard'; export * from './guard';
export { CryptoHelper, URLHelper } from './helpers'; export { CryptoHelper, URLHelper } from './helpers';
export { AFFiNELogger } from './logger'; export { AFFiNELogger } from './logger';
@@ -45,6 +45,14 @@ export type LeafPaths<
}[keyof T] }[keyof T]
: never; : never;
export type LeafVisitor<T, P extends string = ''> = {
[K in keyof T]: T[K] extends object
? LeafVisitor<T[K], Join<P, K>>
: P extends ''
? K
: Join<P, K>;
};
export interface FileUpload { export interface FileUpload {
filename: string; filename: string;
mimetype: string; mimetype: string;
@@ -0,0 +1,293 @@
# Snapshot report for `src/core/permission/__tests__/actions.spec.ts`
The actual snapshot is saved in `actions.spec.ts.snap`.
Generated by [AVA](https://avajs.dev).
## should be able to fixup doc role from workspace role and doc role
> WorkspaceRole: External, DocRole: External
'External'
> WorkspaceRole: External, DocRole: Reader
'Reader'
> WorkspaceRole: External, DocRole: Editor
'Editor'
> WorkspaceRole: External, DocRole: Manager
'Editor'
> WorkspaceRole: External, DocRole: Owner
'Editor'
> WorkspaceRole: Collaborator, DocRole: External
'External'
> WorkspaceRole: Collaborator, DocRole: Reader
'Reader'
> WorkspaceRole: Collaborator, DocRole: Editor
'Editor'
> WorkspaceRole: Collaborator, DocRole: Manager
'Manager'
> WorkspaceRole: Collaborator, DocRole: Owner
'Owner'
> WorkspaceRole: Admin, DocRole: External
'Manager'
> WorkspaceRole: Admin, DocRole: Reader
'Manager'
> WorkspaceRole: Admin, DocRole: Editor
'Manager'
> WorkspaceRole: Admin, DocRole: Manager
'Manager'
> WorkspaceRole: Admin, DocRole: Owner
'Owner'
> WorkspaceRole: Owner, DocRole: External
'Owner'
> WorkspaceRole: Owner, DocRole: Reader
'Owner'
> WorkspaceRole: Owner, DocRole: Editor
'Owner'
> WorkspaceRole: Owner, DocRole: Manager
'Owner'
> WorkspaceRole: Owner, DocRole: Owner
'Owner'
## should be able to get correct permissions from WorkspaceRole
> WorkspaceRole: External
{
'Workspace.CreateDoc': false,
'Workspace.Delete': false,
'Workspace.Organize.Read': true,
'Workspace.Properties.Create': false,
'Workspace.Properties.Delete': false,
'Workspace.Properties.Read': false,
'Workspace.Properties.Update': false,
'Workspace.Settings.Read': false,
'Workspace.Settings.Update': false,
'Workspace.Sync': false,
'Workspace.TransferOwner': false,
'Workspace.Users.Manage': false,
'Workspace.Users.Read': false,
}
> WorkspaceRole: Collaborator
{
'Workspace.CreateDoc': true,
'Workspace.Delete': false,
'Workspace.Organize.Read': true,
'Workspace.Properties.Create': false,
'Workspace.Properties.Delete': false,
'Workspace.Properties.Read': true,
'Workspace.Properties.Update': false,
'Workspace.Settings.Read': true,
'Workspace.Settings.Update': false,
'Workspace.Sync': true,
'Workspace.TransferOwner': false,
'Workspace.Users.Manage': false,
'Workspace.Users.Read': true,
}
> WorkspaceRole: Admin
{
'Workspace.CreateDoc': true,
'Workspace.Delete': false,
'Workspace.Organize.Read': true,
'Workspace.Properties.Create': true,
'Workspace.Properties.Delete': true,
'Workspace.Properties.Read': true,
'Workspace.Properties.Update': true,
'Workspace.Settings.Read': true,
'Workspace.Settings.Update': true,
'Workspace.Sync': true,
'Workspace.TransferOwner': false,
'Workspace.Users.Manage': true,
'Workspace.Users.Read': true,
}
> WorkspaceRole: Owner
{
'Workspace.CreateDoc': true,
'Workspace.Delete': true,
'Workspace.Organize.Read': true,
'Workspace.Properties.Create': true,
'Workspace.Properties.Delete': true,
'Workspace.Properties.Read': true,
'Workspace.Properties.Update': true,
'Workspace.Settings.Read': true,
'Workspace.Settings.Update': true,
'Workspace.Sync': true,
'Workspace.TransferOwner': true,
'Workspace.Users.Manage': true,
'Workspace.Users.Read': true,
}
## should be able to get correct permissions from DocRole
> DocRole: External
{
'Doc.Copy': true,
'Doc.Delete': false,
'Doc.Duplicate': false,
'Doc.Properties.Read': true,
'Doc.Properties.Update': false,
'Doc.Publish': false,
'Doc.Read': true,
'Doc.Restore': false,
'Doc.TransferOwner': false,
'Doc.Trash': false,
'Doc.Update': false,
'Doc.Users.Manage': false,
'Doc.Users.Read': false,
}
> DocRole: Reader
{
'Doc.Copy': true,
'Doc.Delete': false,
'Doc.Duplicate': true,
'Doc.Properties.Read': true,
'Doc.Properties.Update': false,
'Doc.Publish': false,
'Doc.Read': true,
'Doc.Restore': false,
'Doc.TransferOwner': false,
'Doc.Trash': false,
'Doc.Update': false,
'Doc.Users.Manage': false,
'Doc.Users.Read': true,
}
> DocRole: Editor
{
'Doc.Copy': true,
'Doc.Delete': true,
'Doc.Duplicate': true,
'Doc.Properties.Read': true,
'Doc.Properties.Update': true,
'Doc.Publish': false,
'Doc.Read': true,
'Doc.Restore': true,
'Doc.TransferOwner': false,
'Doc.Trash': true,
'Doc.Update': true,
'Doc.Users.Manage': false,
'Doc.Users.Read': true,
}
> DocRole: Manager
{
'Doc.Copy': true,
'Doc.Delete': true,
'Doc.Duplicate': true,
'Doc.Properties.Read': true,
'Doc.Properties.Update': true,
'Doc.Publish': true,
'Doc.Read': true,
'Doc.Restore': true,
'Doc.TransferOwner': false,
'Doc.Trash': true,
'Doc.Update': true,
'Doc.Users.Manage': true,
'Doc.Users.Read': true,
}
> DocRole: Owner
{
'Doc.Copy': true,
'Doc.Delete': true,
'Doc.Duplicate': true,
'Doc.Properties.Read': true,
'Doc.Properties.Update': true,
'Doc.Publish': true,
'Doc.Read': true,
'Doc.Restore': true,
'Doc.TransferOwner': true,
'Doc.Trash': true,
'Doc.Update': true,
'Doc.Users.Manage': true,
'Doc.Users.Read': true,
}
## should be able to find minimal workspace role from action
> Snapshot 1
{
'Workspace.CreateDoc': 'Collaborator',
'Workspace.Delete': 'Owner',
'Workspace.Organize.Read': 'External',
'Workspace.Properties.Create': 'Admin',
'Workspace.Properties.Delete': 'Admin',
'Workspace.Properties.Read': 'Collaborator',
'Workspace.Properties.Update': 'Admin',
'Workspace.Settings.Read': 'Collaborator',
'Workspace.Settings.Update': 'Admin',
'Workspace.Sync': 'Collaborator',
'Workspace.TransferOwner': 'Owner',
'Workspace.Users.Manage': 'Admin',
'Workspace.Users.Read': 'Collaborator',
}
## should be able to find minimal doc role from action
> Snapshot 1
{
'Doc.Copy': 'External',
'Doc.Delete': 'Editor',
'Doc.Duplicate': 'Reader',
'Doc.Properties.Read': 'External',
'Doc.Properties.Update': 'Editor',
'Doc.Publish': 'Manager',
'Doc.Read': 'External',
'Doc.Restore': 'Editor',
'Doc.TransferOwner': 'Owner',
'Doc.Trash': 'Editor',
'Doc.Update': 'Editor',
'Doc.Users.Manage': 'Manager',
'Doc.Users.Read': 'Reader',
}
@@ -1,665 +0,0 @@
# Snapshot report for `src/core/permission/__tests__/role.spec.ts`
The actual snapshot is saved in `role.spec.ts.snap`.
Generated by [AVA](https://avajs.dev).
## should be able to get correct permissions from WorkspaceRole: External and DocRole: External
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: false,
Doc_Duplicate: false,
Doc_Properties_Read: true,
Doc_Properties_Update: false,
Doc_Publish: false,
Doc_Read: true,
Doc_Restore: false,
Doc_TransferOwner: false,
Doc_Trash: false,
Doc_Update: false,
Doc_Users_Manage: false,
Doc_Users_Read: false,
Workspace_CreateDoc: false,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: false,
Workspace_Properties_Update: false,
Workspace_Settings_Read: false,
Workspace_Settings_Update: false,
Workspace_Sync: false,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: false,
}
## should be able to get correct permissions from WorkspaceRole: External and DocRole: Reader
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: false,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: false,
Doc_Publish: false,
Doc_Read: true,
Doc_Restore: false,
Doc_TransferOwner: false,
Doc_Trash: false,
Doc_Update: false,
Doc_Users_Manage: false,
Doc_Users_Read: true,
Workspace_CreateDoc: false,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: false,
Workspace_Properties_Update: false,
Workspace_Settings_Read: false,
Workspace_Settings_Update: false,
Workspace_Sync: false,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: false,
}
## should be able to get correct permissions from WorkspaceRole: External and DocRole: Editor
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: false,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: false,
Doc_Users_Read: true,
Workspace_CreateDoc: false,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: false,
Workspace_Properties_Update: false,
Workspace_Settings_Read: false,
Workspace_Settings_Update: false,
Workspace_Sync: false,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: false,
}
## should be able to get correct permissions from WorkspaceRole: External and DocRole: Manager
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: false,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: false,
Workspace_Properties_Update: false,
Workspace_Settings_Read: false,
Workspace_Settings_Update: false,
Workspace_Sync: false,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: false,
}
## should be able to get correct permissions from WorkspaceRole: External and DocRole: Owner
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: true,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: false,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: false,
Workspace_Properties_Update: false,
Workspace_Settings_Read: false,
Workspace_Settings_Update: false,
Workspace_Sync: false,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: false,
}
## should be able to get correct permissions from WorkspaceRole: Collaborator and DocRole: External
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: false,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: false,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: true,
Workspace_Properties_Update: false,
Workspace_Settings_Read: true,
Workspace_Settings_Update: false,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Collaborator and DocRole: Reader
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: false,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: false,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: true,
Workspace_Properties_Update: false,
Workspace_Settings_Read: true,
Workspace_Settings_Update: false,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Collaborator and DocRole: Editor
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: false,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: false,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: true,
Workspace_Properties_Update: false,
Workspace_Settings_Read: true,
Workspace_Settings_Update: false,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Collaborator and DocRole: Manager
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: true,
Workspace_Properties_Update: false,
Workspace_Settings_Read: true,
Workspace_Settings_Update: false,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Collaborator and DocRole: Owner
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: true,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: false,
Workspace_Properties_Delete: false,
Workspace_Properties_Read: true,
Workspace_Properties_Update: false,
Workspace_Settings_Read: true,
Workspace_Settings_Update: false,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: false,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Admin and DocRole: External
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Admin and DocRole: Reader
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Admin and DocRole: Editor
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Admin and DocRole: Manager
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Admin and DocRole: Owner
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: true,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: false,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: false,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Owner and DocRole: External
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: true,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: true,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Owner and DocRole: Reader
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: true,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: true,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Owner and DocRole: Editor
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: true,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: true,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Owner and DocRole: Manager
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: false,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: true,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: true,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
## should be able to get correct permissions from WorkspaceRole: Owner and DocRole: Owner
> Snapshot 1
{
Doc_Copy: true,
Doc_Delete: true,
Doc_Duplicate: true,
Doc_Properties_Read: true,
Doc_Properties_Update: true,
Doc_Publish: true,
Doc_Read: true,
Doc_Restore: true,
Doc_TransferOwner: true,
Doc_Trash: true,
Doc_Update: true,
Doc_Users_Manage: true,
Doc_Users_Read: true,
Workspace_CreateDoc: true,
Workspace_Delete: true,
Workspace_Organize_Read: true,
Workspace_Properties_Create: true,
Workspace_Properties_Delete: true,
Workspace_Properties_Read: true,
Workspace_Properties_Update: true,
Workspace_Settings_Read: true,
Workspace_Settings_Update: true,
Workspace_Sync: true,
Workspace_TransferOwner: true,
Workspace_Users_Manage: true,
Workspace_Users_Read: true,
}
@@ -0,0 +1,83 @@
import test from 'ava';
import {
Action,
DOC_ACTION_TO_MINIMAL_ROLE_MAP,
DocRole,
fixupDocRole,
mapDocRoleToPermissions,
mapWorkspaceRoleToPermissions,
WORKSPACE_ACTION_TO_MINIMAL_ROLE_MAP,
WorkspaceRole,
} from '../types';
test('should be able to get the correct action path', t => {
t.is(Action.Workspace.CreateDoc, 'Workspace.CreateDoc');
t.is(Action.Workspace.Users.Read, 'Workspace.Users.Read');
t.is(Action.Doc.Copy, 'Doc.Copy');
t.is(Action.Doc.Users.Manage, 'Doc.Users.Manage');
t.not(Action.Workspace.Delete, 'Wrong.Action.Name');
function test(_action: Action) {}
// Action visitor result can be passed to function that accepts [ActionName]
test(Action.Workspace.CreateDoc);
// @ts-expect-error make sure type checked
test('Wrong.Action.Name');
});
const workspaceRoles = Object.values(WorkspaceRole).filter(
r => typeof r === 'number'
) as WorkspaceRole[];
const docRoles = Object.values(DocRole).filter(
r => typeof r === 'number'
) as DocRole[];
test(`should be able to fixup doc role from workspace role and doc role`, t => {
for (const workspaceRole of workspaceRoles) {
for (const docRole of docRoles) {
t.snapshot(
DocRole[fixupDocRole(workspaceRole, docRole)],
`WorkspaceRole: ${WorkspaceRole[workspaceRole]}, DocRole: ${DocRole[docRole]}`
);
}
}
});
test(`should be able to get correct permissions from WorkspaceRole`, t => {
for (const workspaceRole of workspaceRoles) {
t.snapshot(
mapWorkspaceRoleToPermissions(workspaceRole),
`WorkspaceRole: ${WorkspaceRole[workspaceRole]}`
);
}
});
test(`should be able to get correct permissions from DocRole`, t => {
for (const docRole of docRoles) {
t.snapshot(
mapDocRoleToPermissions(docRole),
`DocRole: ${DocRole[docRole]}`
);
}
});
test('should be able to find minimal workspace role from action', t => {
t.snapshot(
Object.fromEntries(
Array.from(WORKSPACE_ACTION_TO_MINIMAL_ROLE_MAP.entries()).map(
([action, role]) => [action, WorkspaceRole[role]]
)
)
);
});
test('should be able to find minimal doc role from action', t => {
t.snapshot(
Object.fromEntries(
Array.from(DOC_ACTION_TO_MINIMAL_ROLE_MAP.entries()).map(
([action, role]) => [action, DocRole[role]]
)
)
);
});
@@ -1,36 +0,0 @@
import test from 'ava';
import { DocRole, WorkspaceRole } from '../index';
import { Actions, ActionsKeys, mapRoleToActions } from '../types';
// create a matrix representing the all possible permission of WorkspaceRole and DocRole
const matrix = Object.values(WorkspaceRole)
.filter(r => typeof r !== 'string')
.flatMap(workspaceRole =>
Object.values(DocRole)
.filter(r => typeof r !== 'string')
.map(docRole => ({
workspaceRole,
docRole,
}))
);
for (const { workspaceRole, docRole } of matrix) {
const permission = mapRoleToActions(workspaceRole, docRole);
test(`should be able to get correct permissions from WorkspaceRole: ${WorkspaceRole[workspaceRole]} and DocRole: ${DocRole[docRole]}`, t => {
t.snapshot(permission);
});
}
test('ActionsKeys value should be the same order of the Actions objects', t => {
for (const [index, value] of ActionsKeys.entries()) {
const [k, k1, k2] = value.split('.');
if (k2) {
// @ts-expect-error
t.is(Actions[k][k1][k2], index);
} else {
// @ts-expect-error
t.is(Actions[k][k1], index);
}
}
});
@@ -9,4 +9,15 @@ import { PermissionService } from './service';
export class PermissionModule {} export class PermissionModule {}
export { PermissionService } from './service'; export { PermissionService } from './service';
export { DocRole, PublicPageMode, WorkspaceRole } from './types'; export {
DOC_ACTIONS,
type DocActionPermissions,
DocRole,
fixupDocRole,
mapDocRoleToPermissions,
mapWorkspaceRoleToPermissions,
PublicPageMode,
WORKSPACE_ACTIONS,
type WorkspaceActionPermissions,
WorkspaceRole,
} from './types';
@@ -13,11 +13,11 @@ import {
WorkspacePermissionNotFound, WorkspacePermissionNotFound,
} from '../../base'; } from '../../base';
import { import {
AllPossibleGraphQLDocActionsKeys, DocAction,
docActionRequiredRole,
docActionRequiredWorkspaceRole,
DocRole, DocRole,
findMinimalDocRole,
PublicPageMode, PublicPageMode,
requiredWorkspaceRoleByDocRole,
WorkspaceRole, WorkspaceRole,
} from './types'; } from './types';
@@ -175,7 +175,7 @@ export class PermissionService {
return isPublicWorkspace || publicPages > 0; return isPublicWorkspace || publicPages > 0;
} }
return this.tryCheckPage(ws, id, 'Doc_Read', user); return this.tryCheckPage(ws, id, 'Doc.Read', user);
} }
async getWorkspaceMemberStatus(ws: string, user: string) { async getWorkspaceMemberStatus(ws: string, user: string) {
@@ -526,7 +526,7 @@ export class PermissionService {
async checkCloudPagePermission( async checkCloudPagePermission(
workspaceId: string, workspaceId: string,
pageId: string, pageId: string,
action: AllPossibleGraphQLDocActionsKeys, action: DocAction,
userId?: string userId?: string
) { ) {
const hasWorkspace = await this.hasWorkspace(workspaceId); const hasWorkspace = await this.hasWorkspace(workspaceId);
@@ -538,7 +538,7 @@ export class PermissionService {
async checkPagePermission( async checkPagePermission(
ws: string, ws: string,
page: string, page: string,
action: AllPossibleGraphQLDocActionsKeys, action: DocAction,
user?: string user?: string
) { ) {
if (!(await this.tryCheckPage(ws, page, action, user))) { if (!(await this.tryCheckPage(ws, page, action, user))) {
@@ -549,12 +549,12 @@ export class PermissionService {
async tryCheckPage( async tryCheckPage(
ws: string, ws: string,
page: string, page: string,
action: AllPossibleGraphQLDocActionsKeys, action: DocAction,
user?: string user?: string
) { ) {
const role = findMinimalDocRole(action); const role = docActionRequiredRole(action);
// check whether page is public // check whether page is public
if (action === 'Doc_Read') { if (action === 'Doc.Read') {
const count = await this.prisma.workspacePage.count({ const count = await this.prisma.workspacePage.count({
where: { where: {
workspaceId: ws, workspaceId: ws,
@@ -602,7 +602,7 @@ export class PermissionService {
return this.tryCheckWorkspace( return this.tryCheckWorkspace(
ws, ws,
user, user,
requiredWorkspaceRoleByDocRole(role) docActionRequiredWorkspaceRole(action)
); );
} }
@@ -1,4 +1,4 @@
import assert from 'node:assert'; import { LeafPaths, LeafVisitor } from '../../base';
export enum PublicPageMode { export enum PublicPageMode {
Page, Page,
@@ -20,234 +20,322 @@ export enum WorkspaceRole {
Owner = 99, Owner = 99,
} }
/**
* Definitions of all possible actions
*
* NOTE(@forehalo): if you add any new actions, please don't forget to add the corresponding role in [RoleActionsMap]
*/
export const Actions = { export const Actions = {
// Workspace Actions
Workspace: { Workspace: {
Sync: 1, Sync: '',
CreateDoc: 2, CreateDoc: '',
Delete: 11, Delete: '',
TransferOwner: 12, TransferOwner: '',
Organize: { Organize: {
Read: 0, Read: '',
}, },
Users: { Users: {
Read: 3, Read: '',
Manage: 6, Manage: '',
}, },
Properties: { Properties: {
Read: 4, Read: '',
Create: 8, Create: '',
Update: 9, Update: '',
Delete: 10, Delete: '',
}, },
Settings: { Settings: {
Read: 5, Read: '',
Update: 7, Update: '',
}, },
}, },
// Doc Actions
Doc: { Doc: {
Read: 13, Read: '',
Copy: 14, Copy: '',
Duplicate: 17, Duplicate: '',
Trash: 18, Trash: '',
Restore: 19, Restore: '',
Delete: 20, Delete: '',
Update: 22, Update: '',
Publish: 23, Publish: '',
TransferOwner: 25, TransferOwner: '',
Properties: { Properties: {
Read: 15, Read: '',
Update: 21, Update: '',
}, },
Users: { Users: {
Read: 16, Read: '',
Manage: 24, Manage: '',
}, },
}, },
} as const; } as const;
type ActionsKeysUnion = typeof Actions extends { export const RoleActionsMap = {
[k in infer _K extends string]: infer _V; WorkspaceRole: {
} get [WorkspaceRole.External]() {
? _V extends { return [Action.Workspace.Organize.Read];
[k1 in infer _K1 extends string]: infer _V1; },
} get [WorkspaceRole.Collaborator]() {
? _V1 extends { return [
[k2 in infer _K2 extends string]: number; ...this[WorkspaceRole.External],
Action.Workspace.Sync,
Action.Workspace.CreateDoc,
Action.Workspace.Users.Read,
Action.Workspace.Properties.Read,
Action.Workspace.Settings.Read,
];
},
get [WorkspaceRole.Admin]() {
return [
...this[WorkspaceRole.Collaborator],
Action.Workspace.Users.Manage,
Action.Workspace.Settings.Update,
Action.Workspace.Properties.Create,
Action.Workspace.Properties.Update,
Action.Workspace.Properties.Delete,
];
},
get [WorkspaceRole.Owner]() {
return [
...this[WorkspaceRole.Admin],
Action.Workspace.Delete,
Action.Workspace.TransferOwner,
];
},
},
DocRole: {
get [DocRole.External]() {
return [Action.Doc.Read, Action.Doc.Copy, Action.Doc.Properties.Read];
},
get [DocRole.Reader]() {
return [
...this[DocRole.External],
Action.Doc.Users.Read,
Action.Doc.Duplicate,
];
},
get [DocRole.Editor]() {
return [
...this[DocRole.Reader],
Action.Doc.Trash,
Action.Doc.Restore,
Action.Doc.Delete,
Action.Doc.Properties.Update,
Action.Doc.Update,
];
},
get [DocRole.Manager]() {
return [
...this[DocRole.Editor],
Action.Doc.Publish,
Action.Doc.Users.Manage,
];
},
get [DocRole.Owner]() {
return [...this[DocRole.Manager], Action.Doc.TransferOwner];
},
},
} as const;
type ResourceActionName<T extends keyof typeof Actions> =
`${T}.${LeafPaths<(typeof Actions)[T]>}`;
export type WorkspaceAction = ResourceActionName<'Workspace'>;
export type DocAction = ResourceActionName<'Doc'>;
export type Action = WorkspaceAction | DocAction;
export type WorkspaceActionPermissions = {
[key in WorkspaceAction]: boolean;
};
export type DocActionPermissions = {
[key in DocAction]: boolean;
};
const cache = new WeakMap<object, any>();
const buildPathReader = (
obj: any,
isLeaf: (val: any) => boolean,
prefix?: string
): any => {
if (cache.has(obj)) {
return cache.get(obj);
}
const reader = new Proxy(obj, {
get(target, prop) {
if (typeof prop === 'symbol') {
return undefined;
} }
? _K1 extends keyof (typeof Actions)[_K]
? _K2 extends keyof (typeof Actions)[_K][_K1]
? `${_K}.${_K1}.${_K2}`
: never
: never
: _V1 extends number
? `${_K}.${_K1}`
: never
: never
: never;
type ExcludeObjectKeys< const newPath = prefix ? `${prefix}.${prop}` : prop;
T,
Key extends keyof typeof Actions,
Split extends string,
> = T extends `${infer _K extends Key}.${infer _K1}.${infer _K2}`
? _K1 extends keyof (typeof Actions)[_K]
? _K2 extends keyof (typeof Actions)[_K][_K1]
? `${_K}${Split}${_K1}${Split}${_K2}`
: never
: never
: T extends `${infer _K extends Key}.${infer _K1}`
? _K1 extends keyof (typeof Actions)[_K]
? (typeof Actions)[_K][_K1] extends number
? `${_K}${Split}${_K1}`
: never
: never
: never;
export type AllPossibleActionsKeys = ExcludeObjectKeys< if (isLeaf(target[prop])) {
ActionsKeysUnion, return newPath;
keyof typeof Actions, }
'.'
>;
export type AllPossibleGraphQLWorkspaceActionsKeys = ExcludeObjectKeys< return buildPathReader(target[prop], isLeaf, newPath);
ActionsKeysUnion, },
'Workspace', });
'_'
>;
export type AllPossibleGraphQLDocActionsKeys = ExcludeObjectKeys<
ActionsKeysUnion,
'Doc',
'_'
>;
type AllPossibleGraphQLActionsKeys = cache.set(obj, reader);
| AllPossibleGraphQLWorkspaceActionsKeys return reader;
| AllPossibleGraphQLDocActionsKeys; };
export const ActionsKeys: AllPossibleActionsKeys[] = [ // Create the proxy that returns the path string
'Workspace.Organize.Read', export const Action: LeafVisitor<typeof Actions> = buildPathReader(
'Workspace.Sync', Actions,
'Workspace.CreateDoc', val => typeof val === 'string'
'Workspace.Users.Read',
'Workspace.Properties.Read',
'Workspace.Settings.Read',
'Workspace.Users.Manage',
'Workspace.Settings.Update',
'Workspace.Properties.Create',
'Workspace.Properties.Update',
'Workspace.Properties.Delete',
'Workspace.Delete',
'Workspace.TransferOwner',
'Doc.Read',
'Doc.Copy',
'Doc.Properties.Read',
'Doc.Users.Read',
'Doc.Duplicate',
'Doc.Trash',
'Doc.Restore',
'Doc.Delete',
'Doc.Properties.Update',
'Doc.Update',
'Doc.Publish',
'Doc.Users.Manage',
'Doc.TransferOwner',
] as const;
assert(
ActionsKeys.length === Actions.Doc.TransferOwner + 1,
'ActionsKeys length is not correct'
); );
function permissionKeyToGraphQLKey(key: string) { export const WORKSPACE_ACTIONS =
const k = key.split('.'); RoleActionsMap.WorkspaceRole[WorkspaceRole.Owner];
return k.join('_') as keyof PermissionsList; export const DOC_ACTIONS = RoleActionsMap.DocRole[DocRole.Owner];
export function mapWorkspaceRoleToPermissions(workspaceRole: WorkspaceRole) {
const permissions = WORKSPACE_ACTIONS.reduce(
(map, action) => {
map[action] = false;
return map;
},
{} as Record<WorkspaceAction, boolean>
);
RoleActionsMap.WorkspaceRole[workspaceRole].forEach(action => {
permissions[action] = true;
});
return permissions;
} }
const DefaultActionsMap = Object.fromEntries( export function mapDocRoleToPermissions(docRole: DocRole) {
ActionsKeys.map(key => [permissionKeyToGraphQLKey(key), false]) const permissions = DOC_ACTIONS.reduce(
) as PermissionsList; (map, action) => {
map[action] = false;
return map;
},
{} as Record<DocAction, boolean>
);
export type WorkspacePermissionsList = { RoleActionsMap.DocRole[docRole].forEach(action => {
[k in AllPossibleGraphQLWorkspaceActionsKeys]: boolean; permissions[action] = true;
};
export type PermissionsList = {
[key in AllPossibleGraphQLActionsKeys]: boolean;
};
export function mapWorkspaceRoleToWorkspaceActions(
workspaceRole: WorkspaceRole
) {
const permissionList = { ...DefaultActionsMap };
(RoleActionsMap.WorkspaceRole[workspaceRole] ?? []).forEach(action => {
permissionList[permissionKeyToGraphQLKey(ActionsKeys[action])] = true;
}); });
return Object.fromEntries(
Object.entries(permissionList).filter(([k, _]) => return permissions;
k.startsWith('Workspace_') }
/**
* Exchange the real operatable [DocRole] with [WorkspaceRole].
*
* Some [WorkspaceRole] has higher permission than the specified [DocRole].
* for example the owner of the workspace can edit all the docs by default,
* So [WorkspaceRole.Owner] will fixup [Doc.External] to [Doc.Manager]
*
* @example
*
* // Owner of the workspace but not specified a role in the doc
* fixupDocRole(WorkspaceRole.Owner, DocRole.External) // returns DocRole.Manager
*/
export function fixupDocRole(
workspaceRole: WorkspaceRole = WorkspaceRole.External,
docRole: DocRole = DocRole.External
) {
switch (workspaceRole) {
case WorkspaceRole.External:
// Workspace External user won't be able to have any high permission doc role
// set the maximum to Editor in case we have [Can Edit with share link] feature
return Math.min(DocRole.Editor, docRole);
// Workspace Owner will always fallback to Doc Owner
case WorkspaceRole.Owner:
return DocRole.Owner;
// Workspace Admin will always fallback to Doc Manager
case WorkspaceRole.Admin:
return Math.max(DocRole.Manager, docRole);
default:
return docRole;
}
}
/**
* a map from [WorkspaceRole] to { [WorkspaceActionName]: boolean }
*/
const WorkspaceRolePermissionsMap = new Map(
Object.values(WorkspaceRole)
.filter(r => typeof r === 'number')
.map(
role =>
[role, mapWorkspaceRoleToPermissions(role as WorkspaceRole)] as [
WorkspaceRole,
Record<WorkspaceAction, boolean>,
]
) )
);
/**
* a map from [WorkspaceActionName] to required [WorkspaceRole]
*
* @testonly use [workspaceActionRequiredRole] instead
*/
export const WORKSPACE_ACTION_TO_MINIMAL_ROLE_MAP = new Map(
RoleActionsMap.WorkspaceRole[WorkspaceRole.Owner].map(
action =>
[
action,
Math.min(
...[...WorkspaceRolePermissionsMap.entries()]
.filter(([_, permissions]) => permissions[action])
.map(([role, _]) => role)
),
] as [WorkspaceAction, WorkspaceRole]
)
);
/**
* a map from [DocRole] to { [DocActionName]: boolean }
*/
const DocRolePermissionsMap = new Map(
Object.values(DocRole)
.filter(r => typeof r === 'number')
.map(docRole => {
const permissions = mapDocRoleToPermissions(docRole as DocRole);
return [docRole, permissions] as [DocRole, Record<DocAction, boolean>];
})
);
/**
* a map from [DocActionName] to required [DocRole]
* @testonly use [docActionRequiredRole] instead
*/
export const DOC_ACTION_TO_MINIMAL_ROLE_MAP = new Map(
RoleActionsMap.DocRole[DocRole.Owner].map(
action =>
[
action,
Math.min(
...[...DocRolePermissionsMap.entries()]
.filter(([_, permissions]) => permissions[action])
.map(([role, _]) => role)
),
] as [DocAction, DocRole]
)
);
export function docActionRequiredRole(action: DocAction): DocRole {
return (
DOC_ACTION_TO_MINIMAL_ROLE_MAP.get(action) ??
/* if we forget to put new action to [RoleActionsMap.DocRole] */ DocRole.Owner
); );
} }
export function mapRoleToActions( /**
workspaceRole?: WorkspaceRole, * Useful when a workspace member doesn't have a specified role in the doc, but want to check the permission of the action
docRole?: DocRole */
) { export function docActionRequiredWorkspaceRole(
const workspaceActions = workspaceRole action: DocAction
? (RoleActionsMap.WorkspaceRole[workspaceRole] ?? [])
: [];
const docActions = (function () {
// Doc owner/manager permission can not be overridden by workspace role
if (docRole !== undefined && docRole >= DocRole.Manager) {
return RoleActionsMap.DocRole[docRole];
}
switch (workspaceRole) {
case WorkspaceRole.Admin:
case WorkspaceRole.Owner:
return RoleActionsMap.DocRole[DocRole.Manager];
case WorkspaceRole.Collaborator:
return RoleActionsMap.DocRole[DocRole.Editor];
default:
return docRole !== undefined
? (RoleActionsMap.DocRole[docRole] ?? [])
: [];
}
})();
const permissionList = { ...DefaultActionsMap };
[...workspaceActions, ...docActions].forEach(action => {
permissionList[permissionKeyToGraphQLKey(ActionsKeys[action])] = true;
});
return permissionList;
}
export function findMinimalDocRole(
action: AllPossibleGraphQLDocActionsKeys
): DocRole {
const [_, actionKey, actionKey2] = action.split('_');
const actionValue: number = actionKey2
? // @ts-expect-error Actions[actionKey] exists
Actions.Doc[actionKey][actionKey2]
: // @ts-expect-error Actions[actionKey] exists
Actions.Doc[actionKey];
if (actionValue <= Actions.Doc.Properties.Read) {
return DocRole.External;
}
if (actionValue <= Actions.Doc.Duplicate) {
return DocRole.Reader;
}
if (actionValue <= Actions.Doc.Update) {
return DocRole.Editor;
}
if (actionValue <= Actions.Doc.Users.Manage) {
return DocRole.Manager;
}
return DocRole.Owner;
}
export function requiredWorkspaceRoleByDocRole(
docRole: DocRole
): WorkspaceRole { ): WorkspaceRole {
const docRole = docActionRequiredRole(action);
switch (docRole) { switch (docRole) {
case DocRole.Owner: case DocRole.Owner:
return WorkspaceRole.Owner; return WorkspaceRole.Owner;
@@ -260,69 +348,11 @@ export function requiredWorkspaceRoleByDocRole(
} }
} }
export const RoleActionsMap = { export function workspaceActionRequiredRole(
WorkspaceRole: { action: WorkspaceAction
get [WorkspaceRole.External]() { ): WorkspaceRole {
return [Actions.Workspace.Organize.Read]; return (
}, WORKSPACE_ACTION_TO_MINIMAL_ROLE_MAP.get(action) ??
get [WorkspaceRole.Collaborator]() { /* if we forget to put new action to [RoleActionsMap.WorkspaceRole] */ WorkspaceRole.Owner
return [ );
...this[WorkspaceRole.External], }
Actions.Workspace.Sync,
Actions.Workspace.CreateDoc,
Actions.Workspace.Users.Read,
Actions.Workspace.Properties.Read,
Actions.Workspace.Settings.Read,
];
},
get [WorkspaceRole.Admin]() {
return [
...this[WorkspaceRole.Collaborator],
Actions.Workspace.Users.Manage,
Actions.Workspace.Settings.Update,
Actions.Workspace.Properties.Create,
Actions.Workspace.Properties.Update,
Actions.Workspace.Properties.Delete,
];
},
get [WorkspaceRole.Owner]() {
return [
...this[WorkspaceRole.Admin],
Actions.Workspace.Delete,
Actions.Workspace.TransferOwner,
];
},
},
DocRole: {
get [DocRole.External]() {
return [Actions.Doc.Read, Actions.Doc.Copy, Actions.Doc.Properties.Read];
},
get [DocRole.Reader]() {
return [
...this[DocRole.External],
Actions.Doc.Users.Read,
Actions.Doc.Duplicate,
];
},
get [DocRole.Editor]() {
return [
...this[DocRole.Reader],
Actions.Doc.Trash,
Actions.Doc.Restore,
Actions.Doc.Delete,
Actions.Doc.Properties.Update,
Actions.Doc.Update,
];
},
get [DocRole.Manager]() {
return [
...this[DocRole.Editor],
Actions.Doc.Publish,
Actions.Doc.Users.Manage,
];
},
get [DocRole.Owner]() {
return [...this[DocRole.Manager], Actions.Doc.TransferOwner];
},
},
} as const;
@@ -147,7 +147,7 @@ export class WorkspacesController {
await this.permission.checkPagePermission( await this.permission.checkPagePermission(
docId.workspace, docId.workspace,
docId.guid, docId.guid,
'Doc_Read', 'Doc.Read',
user.id user.id
); );
@@ -79,7 +79,7 @@ export class DocHistoryResolver {
await this.permission.checkPagePermission( await this.permission.checkPagePermission(
docId.workspace, docId.workspace,
docId.guid, docId.guid,
'Doc_Restore', 'Doc.Update',
user.id user.id
); );
@@ -21,19 +21,22 @@ import {
ExpectToRevokePublicPage, ExpectToRevokePublicPage,
ExpectToUpdateDocUserRole, ExpectToUpdateDocUserRole,
PageIsNotPublic, PageIsNotPublic,
registerObjectType,
} from '../../../base'; } from '../../../base';
import { CurrentUser } from '../../auth'; import { CurrentUser } from '../../auth';
import { import {
DOC_ACTIONS,
type DocActionPermissions,
DocRole, DocRole,
fixupDocRole,
mapDocRoleToPermissions,
PermissionService, PermissionService,
PublicPageMode, PublicPageMode,
WorkspaceRole, WorkspaceRole,
} from '../../permission'; } from '../../permission';
import { mapRoleToActions, PermissionsList } from '../../permission/types';
import { UserType } from '../../user'; import { UserType } from '../../user';
import { DocID } from '../../utils/doc'; import { DocID } from '../../utils/doc';
import { WorkspaceType } from '../types'; import { WorkspaceType } from '../types';
import { WorkspacePermissions } from './workspace';
registerEnumType(PublicPageMode, { registerEnumType(PublicPageMode, {
name: 'PublicPageMode', name: 'PublicPageMode',
@@ -130,38 +133,12 @@ class GrantedDocUsersConnection {
pageInfo!: PageInfo; pageInfo!: PageInfo;
} }
@ObjectType() const DocPermissions = registerObjectType<DocActionPermissions>(
export class RolePermissions Object.fromEntries(
extends WorkspacePermissions DOC_ACTIONS.map(action => [action.replaceAll('.', '_'), Boolean])
implements PermissionsList ),
{ { name: 'DocPermissions' }
@Field() );
Doc_Read!: boolean;
@Field()
Doc_Copy!: boolean;
@Field()
Doc_Properties_Read!: boolean;
@Field()
Doc_Users_Read!: boolean;
@Field()
Doc_Duplicate!: boolean;
@Field()
Doc_Trash!: boolean;
@Field()
Doc_Restore!: boolean;
@Field()
Doc_Delete!: boolean;
@Field()
Doc_Properties_Update!: boolean;
@Field()
Doc_Update!: boolean;
@Field()
Doc_Publish!: boolean;
@Field()
Doc_Users_Manage!: boolean;
@Field()
Doc_TransferOwner!: boolean;
}
@ObjectType() @ObjectType()
class DocType { class DocType {
@@ -174,8 +151,8 @@ class DocType {
@Field(() => DocRole) @Field(() => DocRole)
role!: DocRole; role!: DocRole;
@Field(() => RolePermissions) @Field(() => DocPermissions)
permissions!: RolePermissions; permissions!: DocActionPermissions;
} }
@Resolver(() => WorkspaceType) @Resolver(() => WorkspaceType)
@@ -278,9 +255,8 @@ export class PagePermissionResolver {
id: pageId, id: pageId,
public: page?.public ?? false, public: page?.public ?? false,
role: permission?.type ?? DocRole.External, role: permission?.type ?? DocRole.External,
permissions: mapRoleToActions( permissions: mapDocRoleToPermissions(
workspacePermission?.type, fixupDocRole(workspacePermission?.type, permission?.type)
permission?.type
), ),
}; };
} }
@@ -395,7 +371,7 @@ export class PagePermissionResolver {
await this.permission.checkPagePermission( await this.permission.checkPagePermission(
docId.workspace, docId.workspace,
docId.guid, docId.guid,
'Doc_Publish', 'Doc.Publish',
user.id user.id
); );
@@ -443,7 +419,7 @@ export class PagePermissionResolver {
await this.permission.checkPagePermission( await this.permission.checkPagePermission(
docId.workspace, docId.workspace,
docId.guid, docId.guid,
'Doc_Publish', 'Doc.Publish',
user.id user.id
); );
@@ -491,7 +467,7 @@ export class PagePermissionResolver {
await this.permission.checkPagePermission( await this.permission.checkPagePermission(
doc.workspace, doc.workspace,
doc.guid, doc.guid,
'Doc_Users_Manage', 'Doc.Users.Manage',
user.id user.id
); );
await this.permission.grantPagePermission( await this.permission.grantPagePermission(
@@ -22,6 +22,7 @@ import {
InternalServerError, InternalServerError,
MemberQuotaExceeded, MemberQuotaExceeded,
QueryTooLong, QueryTooLong,
registerObjectType,
RequestMutex, RequestMutex,
SpaceAccessDenied, SpaceAccessDenied,
SpaceNotFound, SpaceNotFound,
@@ -33,11 +34,13 @@ import {
import { Models } from '../../../models'; import { Models } from '../../../models';
import { CurrentUser, Public } from '../../auth'; import { CurrentUser, Public } from '../../auth';
import { type Editor, PgWorkspaceDocStorageAdapter } from '../../doc'; import { type Editor, PgWorkspaceDocStorageAdapter } from '../../doc';
import { PermissionService, WorkspaceRole } from '../../permission';
import { import {
mapWorkspaceRoleToWorkspaceActions, mapWorkspaceRoleToPermissions,
WorkspacePermissionsList, PermissionService,
} from '../../permission/types'; WORKSPACE_ACTIONS,
type WorkspaceActionPermissions,
WorkspaceRole,
} from '../../permission';
import { QuotaService, WorkspaceQuotaType } from '../../quota'; import { QuotaService, WorkspaceQuotaType } from '../../quota';
import { UserType } from '../../user'; import { UserType } from '../../user';
import { import {
@@ -72,35 +75,12 @@ class WorkspacePageMeta {
updatedBy!: EditorType | null; updatedBy!: EditorType | null;
} }
@ObjectType() const WorkspacePermissions = registerObjectType<WorkspaceActionPermissions>(
export class WorkspacePermissions implements WorkspacePermissionsList { Object.fromEntries(
@Field() WORKSPACE_ACTIONS.map(action => [action.replaceAll('.', '_'), Boolean])
Workspace_Organize_Read!: boolean; ),
@Field() { name: 'WorkspacePermissions' }
Workspace_Sync!: boolean; );
@Field()
Workspace_CreateDoc!: boolean;
@Field()
Workspace_Users_Read!: boolean;
@Field()
Workspace_Properties_Read!: boolean;
@Field()
Workspace_Settings_Read!: boolean;
@Field()
Workspace_Users_Manage!: boolean;
@Field()
Workspace_Settings_Update!: boolean;
@Field()
Workspace_Properties_Create!: boolean;
@Field()
Workspace_Properties_Update!: boolean;
@Field()
Workspace_Properties_Delete!: boolean;
@Field()
Workspace_Delete!: boolean;
@Field()
Workspace_TransferOwner!: boolean;
}
@ObjectType() @ObjectType()
export class WorkspaceRolePermissions { export class WorkspaceRolePermissions {
@@ -108,7 +88,7 @@ export class WorkspaceRolePermissions {
role!: WorkspaceRole; role!: WorkspaceRole;
@Field(() => WorkspacePermissions) @Field(() => WorkspacePermissions)
permissions!: WorkspacePermissions; permissions!: WorkspaceActionPermissions;
} }
/** /**
@@ -363,7 +343,7 @@ export class WorkspaceResolver {
} }
return { return {
role: workspace.type, role: workspace.type,
permissions: mapWorkspaceRoleToWorkspaceActions(workspace.type), permissions: mapWorkspaceRoleToPermissions(workspace.type),
}; };
} }
@@ -335,7 +335,7 @@ export class CopilotResolver {
await this.permissions.checkCloudPagePermission( await this.permissions.checkCloudPagePermission(
workspaceId, workspaceId,
docId, docId,
'Doc_Read', 'Doc.Read',
user.id user.id
); );
} else { } else {
@@ -369,7 +369,7 @@ export class CopilotResolver {
await this.permissions.checkCloudPagePermission( await this.permissions.checkCloudPagePermission(
options.workspaceId, options.workspaceId,
options.docId, options.docId,
'Doc_Read', 'Doc.Update',
user.id user.id
); );
const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`; const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`;
@@ -403,7 +403,7 @@ export class CopilotResolver {
await this.permissions.checkCloudPagePermission( await this.permissions.checkCloudPagePermission(
workspaceId, workspaceId,
docId, docId,
'Doc_Update', 'Doc.Update',
user.id user.id
); );
const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${workspaceId}`; const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${workspaceId}`;
@@ -431,7 +431,7 @@ export class CopilotResolver {
await this.permissions.checkCloudPagePermission( await this.permissions.checkCloudPagePermission(
options.workspaceId, options.workspaceId,
options.docId, options.docId,
'Doc_Copy', 'Doc.Update',
user.id user.id
); );
const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`; const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`;
@@ -460,7 +460,7 @@ export class CopilotResolver {
await this.permissions.checkCloudPagePermission( await this.permissions.checkCloudPagePermission(
options.workspaceId, options.workspaceId,
options.docId, options.docId,
'Doc_Delete', 'Doc.Update',
user.id user.id
); );
if (!options.sessionIds.length) { if (!options.sessionIds.length) {
+17 -30
View File
@@ -204,6 +204,22 @@ type DocNotFoundDataType {
spaceId: String! spaceId: String!
} }
type DocPermissions {
Doc_Copy: Boolean!
Doc_Delete: Boolean!
Doc_Duplicate: Boolean!
Doc_Properties_Read: Boolean!
Doc_Properties_Update: Boolean!
Doc_Publish: Boolean!
Doc_Read: Boolean!
Doc_Restore: Boolean!
Doc_TransferOwner: Boolean!
Doc_Trash: Boolean!
Doc_Update: Boolean!
Doc_Users_Manage: Boolean!
Doc_Users_Read: Boolean!
}
"""User permission in doc""" """User permission in doc"""
enum DocRole { enum DocRole {
Editor Editor
@@ -215,7 +231,7 @@ enum DocRole {
type DocType { type DocType {
id: String! id: String!
permissions: RolePermissions! permissions: DocPermissions!
public: Boolean! public: Boolean!
role: DocRole! role: DocRole!
} }
@@ -778,35 +794,6 @@ type RemoveAvatar {
success: Boolean! success: Boolean!
} }
type RolePermissions {
Doc_Copy: Boolean!
Doc_Delete: Boolean!
Doc_Duplicate: Boolean!
Doc_Properties_Read: Boolean!
Doc_Properties_Update: Boolean!
Doc_Publish: Boolean!
Doc_Read: Boolean!
Doc_Restore: Boolean!
Doc_TransferOwner: Boolean!
Doc_Trash: Boolean!
Doc_Update: Boolean!
Doc_Users_Manage: Boolean!
Doc_Users_Read: Boolean!
Workspace_CreateDoc: Boolean!
Workspace_Delete: Boolean!
Workspace_Organize_Read: Boolean!
Workspace_Properties_Create: Boolean!
Workspace_Properties_Delete: Boolean!
Workspace_Properties_Read: Boolean!
Workspace_Properties_Update: Boolean!
Workspace_Settings_Read: Boolean!
Workspace_Settings_Update: Boolean!
Workspace_Sync: Boolean!
Workspace_TransferOwner: Boolean!
Workspace_Users_Manage: Boolean!
Workspace_Users_Read: Boolean!
}
type RuntimeConfigNotFoundDataType { type RuntimeConfigNotFoundDataType {
key: String! key: String!
} }