mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 10:22:55 +08:00
feat(server): support making doc private in workspace (#10744)
This commit is contained in:
@@ -6,6 +6,10 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
## should be able to fixup doc role from workspace role and doc role
|
## should be able to fixup doc role from workspace role and doc role
|
||||||
|
|
||||||
|
> WorkspaceRole: External, DocRole: None
|
||||||
|
|
||||||
|
'None'
|
||||||
|
|
||||||
> WorkspaceRole: External, DocRole: External
|
> WorkspaceRole: External, DocRole: External
|
||||||
|
|
||||||
'External'
|
'External'
|
||||||
@@ -26,6 +30,10 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
'Editor'
|
'Editor'
|
||||||
|
|
||||||
|
> WorkspaceRole: Collaborator, DocRole: None
|
||||||
|
|
||||||
|
'None'
|
||||||
|
|
||||||
> WorkspaceRole: Collaborator, DocRole: External
|
> WorkspaceRole: Collaborator, DocRole: External
|
||||||
|
|
||||||
'External'
|
'External'
|
||||||
@@ -46,6 +54,10 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
'Owner'
|
'Owner'
|
||||||
|
|
||||||
|
> WorkspaceRole: Admin, DocRole: None
|
||||||
|
|
||||||
|
'Manager'
|
||||||
|
|
||||||
> WorkspaceRole: Admin, DocRole: External
|
> WorkspaceRole: Admin, DocRole: External
|
||||||
|
|
||||||
'Manager'
|
'Manager'
|
||||||
@@ -66,6 +78,10 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
'Owner'
|
'Owner'
|
||||||
|
|
||||||
|
> WorkspaceRole: Owner, DocRole: None
|
||||||
|
|
||||||
|
'Owner'
|
||||||
|
|
||||||
> WorkspaceRole: Owner, DocRole: External
|
> WorkspaceRole: Owner, DocRole: External
|
||||||
|
|
||||||
'Owner'
|
'Owner'
|
||||||
@@ -190,6 +206,24 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
## should be able to get correct permissions from DocRole
|
## should be able to get correct permissions from DocRole
|
||||||
|
|
||||||
|
> DocRole: None
|
||||||
|
|
||||||
|
{
|
||||||
|
'Doc.Copy': false,
|
||||||
|
'Doc.Delete': false,
|
||||||
|
'Doc.Duplicate': false,
|
||||||
|
'Doc.Properties.Read': false,
|
||||||
|
'Doc.Properties.Update': false,
|
||||||
|
'Doc.Publish': false,
|
||||||
|
'Doc.Read': false,
|
||||||
|
'Doc.Restore': false,
|
||||||
|
'Doc.TransferOwner': false,
|
||||||
|
'Doc.Trash': false,
|
||||||
|
'Doc.Update': false,
|
||||||
|
'Doc.Users.Manage': false,
|
||||||
|
'Doc.Users.Read': false,
|
||||||
|
}
|
||||||
|
|
||||||
> DocRole: External
|
> DocRole: External
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
Binary file not shown.
@@ -36,8 +36,9 @@ const docRoles = Object.values(DocRole).filter(
|
|||||||
test(`should be able to fixup doc role from workspace role and doc role`, t => {
|
test(`should be able to fixup doc role from workspace role and doc role`, t => {
|
||||||
for (const workspaceRole of workspaceRoles) {
|
for (const workspaceRole of workspaceRoles) {
|
||||||
for (const docRole of docRoles) {
|
for (const docRole of docRoles) {
|
||||||
|
const fixedDocRole = fixupDocRole(workspaceRole, docRole);
|
||||||
t.snapshot(
|
t.snapshot(
|
||||||
DocRole[fixupDocRole(workspaceRole, docRole)!],
|
fixedDocRole === null ? null : DocRole[fixedDocRole],
|
||||||
`WorkspaceRole: ${WorkspaceRole[workspaceRole]}, DocRole: ${DocRole[docRole]}`
|
`WorkspaceRole: ${WorkspaceRole[workspaceRole]}, DocRole: ${DocRole[docRole]}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,44 @@ test('should return [External] if doc is public', async t => {
|
|||||||
t.is(role, DocRole.External);
|
t.is(role, DocRole.External);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should return null if doc role is [None]', async t => {
|
||||||
|
await models.doc.setDefaultRole(ws.id, 'doc1', DocRole.None);
|
||||||
|
|
||||||
|
await models.workspaceUser.set(
|
||||||
|
ws.id,
|
||||||
|
user.id,
|
||||||
|
WorkspaceRole.Collaborator,
|
||||||
|
WorkspaceMemberStatus.Accepted
|
||||||
|
);
|
||||||
|
|
||||||
|
const role = await ac.getRole({
|
||||||
|
workspaceId: ws.id,
|
||||||
|
docId: 'doc1',
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.is(role, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return [External] if doc role is [None] but doc is public', async t => {
|
||||||
|
await models.doc.setDefaultRole(ws.id, 'doc1', DocRole.None);
|
||||||
|
await models.workspaceUser.set(
|
||||||
|
ws.id,
|
||||||
|
user.id,
|
||||||
|
WorkspaceRole.Collaborator,
|
||||||
|
WorkspaceMemberStatus.Accepted
|
||||||
|
);
|
||||||
|
await models.doc.publish(ws.id, 'doc1');
|
||||||
|
|
||||||
|
const role = await ac.getRole({
|
||||||
|
workspaceId: ws.id,
|
||||||
|
docId: 'doc1',
|
||||||
|
userId: 'random-user-id',
|
||||||
|
});
|
||||||
|
|
||||||
|
t.is(role, DocRole.External);
|
||||||
|
});
|
||||||
|
|
||||||
test('should return mapped permissions', async t => {
|
test('should return mapped permissions', async t => {
|
||||||
const { permissions } = await ac.role({
|
const { permissions } = await ac.role({
|
||||||
workspaceId: ws.id,
|
workspaceId: ws.id,
|
||||||
|
|||||||
@@ -81,8 +81,12 @@ export class DocAccessController extends AccessController<'doc'> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// if user is in workspace but doc role is not set, fallback to default doc role
|
// if user is in workspace but doc role is not set, fallback to default doc role
|
||||||
if (workspaceRole && workspaceRole !== WorkspaceRole.External) {
|
if (workspaceRole !== null && workspaceRole !== WorkspaceRole.External) {
|
||||||
docRole = defaultDocRole.workspace;
|
docRole =
|
||||||
|
defaultDocRole.external !== null
|
||||||
|
? // edgecase: when doc role set to [None] for workspace member, but doc is public, we should fallback to external role
|
||||||
|
Math.max(defaultDocRole.workspace, defaultDocRole.external)
|
||||||
|
: defaultDocRole.workspace;
|
||||||
} else {
|
} else {
|
||||||
// else fallback to external doc role
|
// else fallback to external doc role
|
||||||
docRole = defaultDocRole.external;
|
docRole = defaultDocRole.external;
|
||||||
@@ -92,7 +96,10 @@ export class DocAccessController extends AccessController<'doc'> {
|
|||||||
// we need to fixup doc role to make sure it's not miss set
|
// we need to fixup doc role to make sure it's not miss set
|
||||||
// for example: workspace owner will have doc owner role
|
// for example: workspace owner will have doc owner role
|
||||||
// workspace external will not have role higher than editor
|
// workspace external will not have role higher than editor
|
||||||
return fixupDocRole(workspaceRole, docRole);
|
const role = fixupDocRole(workspaceRole, docRole);
|
||||||
|
|
||||||
|
// never return [None]
|
||||||
|
return role === DocRole.None ? null : role;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async defaultDocRole(workspaceId: string, docId: string) {
|
private async defaultDocRole(workspaceId: string, docId: string) {
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ export function mapDocRoleToPermissions(docRole: DocRole | null) {
|
|||||||
{} as Record<DocAction, boolean>
|
{} as Record<DocAction, boolean>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (docRole === null) {
|
if (docRole === null || docRole === DocRole.None) {
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +249,10 @@ export function fixupDocRole(
|
|||||||
workspaceRole: WorkspaceRole | null,
|
workspaceRole: WorkspaceRole | null,
|
||||||
docRole: DocRole | null
|
docRole: DocRole | null
|
||||||
): DocRole | null {
|
): DocRole | null {
|
||||||
if (workspaceRole === null && docRole === null) {
|
if (
|
||||||
|
workspaceRole === null &&
|
||||||
|
(docRole === null || docRole === DocRole.None)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,26 +346,6 @@ export function docActionRequiredRole(action: DocAction): DocRole {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Useful when a workspace member doesn't have a specified role in the doc, but want to check the permission of the action
|
|
||||||
*/
|
|
||||||
export function docActionRequiredWorkspaceRole(
|
|
||||||
action: DocAction
|
|
||||||
): WorkspaceRole {
|
|
||||||
const docRole = docActionRequiredRole(action);
|
|
||||||
|
|
||||||
switch (docRole) {
|
|
||||||
case DocRole.Owner:
|
|
||||||
return WorkspaceRole.Owner;
|
|
||||||
case DocRole.Manager:
|
|
||||||
return WorkspaceRole.Admin;
|
|
||||||
case DocRole.Editor:
|
|
||||||
case DocRole.Reader:
|
|
||||||
case DocRole.External:
|
|
||||||
return WorkspaceRole.Collaborator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function workspaceActionRequiredRole(
|
export function workspaceActionRequiredRole(
|
||||||
action: WorkspaceAction
|
action: WorkspaceAction
|
||||||
): WorkspaceRole {
|
): WorkspaceRole {
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
export enum DocRole {
|
export enum DocRole {
|
||||||
|
/**
|
||||||
|
* `None` equals to `role = null`, it only exists to give a value that API can use
|
||||||
|
*/
|
||||||
|
None = -(1 << 15),
|
||||||
External = 0,
|
External = 0,
|
||||||
Reader = 10,
|
Reader = 10,
|
||||||
Editor = 20,
|
Editor = 20,
|
||||||
|
|||||||
Reference in New Issue
Block a user