feat(server): allow to set default role in page (#9963)

This commit is contained in:
Brooooooklyn
2025-02-06 17:18:50 +00:00
parent f309f8f3e1
commit 41107eafae
9 changed files with 470 additions and 67 deletions

View File

@@ -571,30 +571,69 @@ export class PermissionService {
}
if (user) {
const count = await this.prisma.workspacePageUserPermission.count({
where: {
workspaceId: ws,
pageId: page,
userId: user,
type: {
gte: role,
const [roleEntity, pageEntity, workspaceRoleEntity] = await Promise.all([
this.prisma.workspacePageUserPermission.findFirst({
where: {
workspaceId: ws,
pageId: page,
userId: user,
},
},
});
select: {
type: true,
},
}),
this.prisma.workspacePage.findFirst({
where: {
workspaceId: ws,
pageId: page,
},
select: {
defaultRole: true,
},
}),
this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId: ws,
userId: user,
OR: this.acceptedCondition,
},
select: {
type: true,
},
}),
]);
// page shared to user
// accessible
if (count > 0) {
if (
// Page role exists, check it first
(roleEntity && roleEntity.type >= role) ||
// if
// - page has a default role
// - the user is in this workspace
// - the user is not an external user in this workspace
// then use the max of the two
(workspaceRoleEntity &&
workspaceRoleEntity.type !== WorkspaceRole.External &&
Math.max(
roleEntity?.type ?? Number.MIN_SAFE_INTEGER,
pageEntity?.defaultRole ?? Number.MIN_SAFE_INTEGER
) >= role)
) {
return true;
} else {
this.logger.log("User's PageRole is lower than required", {
workspaceId: ws,
pageId: page,
userId: user,
requiredRole: DocRole[role],
action,
});
}
this.logger.log("User's role is lower than required", {
workspaceId: ws,
docId: page,
userId: user,
workspaceRole: workspaceRoleEntity
? WorkspaceRole[workspaceRoleEntity.type]
: undefined,
pageRole: roleEntity ? DocRole[roleEntity.type] : undefined,
pageDefaultRole: pageEntity
? DocRole[pageEntity.defaultRole]
: undefined,
requiredRole: DocRole[role],
action,
});
}
// check whether user has workspace related permission

View File

@@ -240,7 +240,7 @@ export function mapDocRoleToPermissions(docRole: DocRole) {
export function fixupDocRole(
workspaceRole: WorkspaceRole = WorkspaceRole.External,
docRole: DocRole = DocRole.External
) {
): DocRole {
switch (workspaceRole) {
case WorkspaceRole.External:
// Workspace External user won't be able to have any high permission doc role

View File

@@ -14,11 +14,13 @@ import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import {
DocAccessDenied,
ExpectToGrantDocUserRoles,
ExpectToPublishPage,
ExpectToRevokeDocUserRoles,
ExpectToRevokePublicPage,
ExpectToUpdateDocUserRole,
PageDefaultRoleCanNotBeOwner,
PageIsNotPublic,
paginate,
Paginated,
@@ -74,6 +76,45 @@ class GrantDocUserRolesInput {
userIds!: string[];
}
@InputType()
class UpdateDocUserRoleInput {
@Field(() => String)
docId!: string;
@Field(() => String)
workspaceId!: string;
@Field(() => String)
userId!: string;
@Field(() => DocRole)
role!: DocRole;
}
@InputType()
class RevokeDocUserRolesInput {
@Field(() => String)
docId!: string;
@Field(() => String)
workspaceId!: string;
@Field(() => [String])
userIds!: string[];
}
@InputType()
class UpdatePageDefaultRoleInput {
@Field(() => String)
docId!: string;
@Field(() => String)
workspaceId!: string;
@Field(() => DocRole)
role!: DocRole;
}
@ObjectType()
class GrantedDocUserType {
@Field(() => String)
@@ -413,12 +454,11 @@ export class PagePermissionResolver {
@Mutation(() => Boolean)
async revokeDocUserRoles(
@CurrentUser() user: CurrentUser,
@Args('docId') docId: string,
@Args('userIds', { type: () => [String] }) userIds: string[]
@Args('input') input: RevokeDocUserRolesInput
): Promise<boolean> {
const doc = new DocID(docId);
const doc = new DocID(input.docId, input.workspaceId);
const pairs = {
spaceId: doc.workspace,
spaceId: input.workspaceId,
docId: doc.guid,
};
if (doc.isWorkspace) {
@@ -436,10 +476,10 @@ export class PagePermissionResolver {
user.id,
WorkspaceRole.Collaborator
);
await this.permission.revokePage(doc.workspace, doc.guid, userIds);
await this.permission.revokePage(doc.workspace, doc.guid, input.userIds);
this.logger.log('Revoke doc user roles', {
...pairs,
userIds: userIds,
userIds: input.userIds,
});
return true;
}
@@ -447,11 +487,9 @@ export class PagePermissionResolver {
@Mutation(() => Boolean)
async updateDocUserRole(
@CurrentUser() user: CurrentUser,
@Args('docId') docId: string,
@Args('userId') userId: string,
@Args('role', { type: () => DocRole }) role: DocRole
@Args('input') input: UpdateDocUserRoleInput
): Promise<boolean> {
const doc = new DocID(docId);
const doc = new DocID(input.docId, input.workspaceId);
const pairs = {
spaceId: doc.workspace,
docId: doc.guid,
@@ -471,32 +509,94 @@ export class PagePermissionResolver {
user.id,
WorkspaceRole.Collaborator
);
if (role === DocRole.Owner) {
if (input.role === DocRole.Owner) {
const ret = await this.permission.grantPagePermission(
doc.workspace,
doc.guid,
[userId],
role
[input.userId],
input.role
);
this.logger.log('Transfer doc owner', {
...pairs,
userId: userId,
role: role,
userId: input.userId,
role: input.role,
});
return ret.length > 0;
} else {
await this.permission.updatePagePermission(
doc.workspace,
doc.guid,
userId,
role
input.userId,
input.role
);
this.logger.log('Update doc user role', {
...pairs,
userId: userId,
role: role,
userId: input.userId,
role: input.role,
});
return true;
}
}
@Mutation(() => Boolean)
async updatePageDefaultRole(
@CurrentUser() user: CurrentUser,
@Args('input') input: UpdatePageDefaultRoleInput
) {
if (input.role === DocRole.Owner) {
this.logger.log('Page default role can not be owner', input);
throw new PageDefaultRoleCanNotBeOwner();
}
const doc = new DocID(input.docId, input.workspaceId);
const pairs = {
spaceId: doc.workspace,
docId: doc.guid,
};
if (doc.isWorkspace) {
this.logger.error(
'Expect to update page default role, but it is a workspace',
pairs
);
throw new ExpectToUpdateDocUserRole(
pairs,
'Expect doc not to be workspace'
);
}
try {
await this.permission.checkCloudPagePermission(
doc.workspace,
doc.guid,
'Doc.Users.Manage',
user.id
);
} catch (error) {
if (error instanceof DocAccessDenied) {
this.logger.log(
'User does not have permission to update page default role',
{
...pairs,
userId: user.id,
}
);
}
throw error;
}
await this.prisma.workspacePage.upsert({
where: {
workspaceId_pageId: {
workspaceId: doc.workspace,
pageId: doc.guid,
},
},
update: {
defaultRole: input.role,
},
create: {
workspaceId: doc.workspace,
pageId: doc.guid,
defaultRole: input.role,
},
});
return true;
}
}