mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
fix(server): batch grant page roles (#10007)
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import type { Prisma, WorkspacePageUserPermission } from '@prisma/client';
|
||||
import { PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
|
||||
import { groupBy } from 'lodash-es';
|
||||
|
||||
import {
|
||||
CanNotBatchGrantPageOwnerPermissions,
|
||||
DocAccessDenied,
|
||||
EventBus,
|
||||
OnEvent,
|
||||
SpaceAccessDenied,
|
||||
SpaceOwnerNotFound,
|
||||
SpaceShouldHaveOnlyOneOwner,
|
||||
WorkspacePermissionNotFound,
|
||||
} from '../../base';
|
||||
import {
|
||||
@@ -737,17 +737,15 @@ export class PermissionService {
|
||||
].filter(Boolean) as Prisma.PrismaPromise<any>[]
|
||||
);
|
||||
|
||||
return p;
|
||||
return p as WorkspacePageUserPermission;
|
||||
}
|
||||
|
||||
async revokePage(ws: string, page: string, users: string[]) {
|
||||
async revokePage(ws: string, page: string, user: string) {
|
||||
const result = await this.prisma.workspacePageUserPermission.deleteMany({
|
||||
where: {
|
||||
workspaceId: ws,
|
||||
pageId: page,
|
||||
userId: {
|
||||
in: users,
|
||||
},
|
||||
userId: user,
|
||||
type: {
|
||||
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading
|
||||
not: DocRole.Owner,
|
||||
@@ -758,74 +756,30 @@ export class PermissionService {
|
||||
return result.count > 0;
|
||||
}
|
||||
|
||||
async grantPagePermission(
|
||||
async batchGrantPage(
|
||||
workspaceId: string,
|
||||
pageId: string,
|
||||
userIds: string[],
|
||||
role: DocRole
|
||||
) {
|
||||
if (userIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
if (role === DocRole.Owner && userIds.length > 1) {
|
||||
throw new SpaceShouldHaveOnlyOneOwner({ spaceId: workspaceId });
|
||||
return 0;
|
||||
}
|
||||
|
||||
return await this.prisma.$transaction(async tx =>
|
||||
Promise.all(
|
||||
userIds.map(id =>
|
||||
tx.workspacePageUserPermission.upsert({
|
||||
where: {
|
||||
workspaceId_pageId_userId: {
|
||||
workspaceId,
|
||||
pageId,
|
||||
userId: id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
workspaceId,
|
||||
pageId,
|
||||
userId: id,
|
||||
type: role,
|
||||
},
|
||||
update: {
|
||||
type: role,
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (role === DocRole.Owner) {
|
||||
throw new CanNotBatchGrantPageOwnerPermissions();
|
||||
}
|
||||
|
||||
async updatePagePermission(
|
||||
workspaceId: string,
|
||||
pageId: string,
|
||||
userId: string,
|
||||
role: DocRole
|
||||
) {
|
||||
const permission = await this.prisma.workspacePageUserPermission.findFirst({
|
||||
where: {
|
||||
const result = await this.prisma.workspacePageUserPermission.createMany({
|
||||
skipDuplicates: true,
|
||||
data: userIds.map(id => ({
|
||||
workspaceId,
|
||||
pageId,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!permission) {
|
||||
return this.grantPage(workspaceId, pageId, userId, role);
|
||||
}
|
||||
|
||||
return await this.prisma.workspacePageUserPermission.update({
|
||||
where: {
|
||||
workspaceId_pageId_userId: {
|
||||
workspaceId,
|
||||
pageId,
|
||||
userId,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
userId: id,
|
||||
type: role,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
return result.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
mapDocRoleToPermissions,
|
||||
PermissionService,
|
||||
PublicPageMode,
|
||||
WorkspaceRole,
|
||||
} from '../../permission';
|
||||
import { PublicUserType } from '../../user';
|
||||
import { DocID } from '../../utils/doc';
|
||||
@@ -94,15 +93,15 @@ class UpdateDocUserRoleInput {
|
||||
}
|
||||
|
||||
@InputType()
|
||||
class RevokeDocUserRolesInput {
|
||||
class RevokeDocUserRoleInput {
|
||||
@Field(() => String)
|
||||
docId!: string;
|
||||
|
||||
@Field(() => String)
|
||||
workspaceId!: string;
|
||||
|
||||
@Field(() => [String])
|
||||
userIds!: string[];
|
||||
@Field(() => String)
|
||||
userId!: string;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
@@ -263,10 +262,18 @@ export class PagePermissionResolver {
|
||||
complexity: 4,
|
||||
})
|
||||
async pageGrantedUsersList(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Parent() workspace: WorkspaceType,
|
||||
@Args('pageId') pageId: string,
|
||||
@Args('pagination') pagination: PaginationInput
|
||||
): Promise<PaginatedGrantedDocUserType> {
|
||||
await this.permission.checkPagePermission(
|
||||
workspace.id,
|
||||
pageId,
|
||||
'Doc.Users.Read',
|
||||
user.id
|
||||
);
|
||||
|
||||
const docId = new DocID(pageId, workspace.id);
|
||||
const [permissions, totalCount] = await this.prisma.$transaction(tx => {
|
||||
return Promise.all([
|
||||
@@ -454,7 +461,7 @@ export class PagePermissionResolver {
|
||||
'Doc.Users.Manage',
|
||||
user.id
|
||||
);
|
||||
await this.permission.grantPagePermission(
|
||||
await this.permission.batchGrantPage(
|
||||
doc.workspace,
|
||||
doc.guid,
|
||||
input.userIds,
|
||||
@@ -471,7 +478,7 @@ export class PagePermissionResolver {
|
||||
@Mutation(() => Boolean)
|
||||
async revokeDocUserRoles(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Args('input') input: RevokeDocUserRolesInput
|
||||
@Args('input') input: RevokeDocUserRoleInput
|
||||
): Promise<boolean> {
|
||||
const doc = new DocID(input.docId, input.workspaceId);
|
||||
const pairs = {
|
||||
@@ -488,15 +495,16 @@ export class PagePermissionResolver {
|
||||
'Expect doc not to be workspace'
|
||||
);
|
||||
}
|
||||
await this.permission.checkWorkspace(
|
||||
await this.permission.checkPagePermission(
|
||||
doc.workspace,
|
||||
user.id,
|
||||
WorkspaceRole.Collaborator
|
||||
doc.guid,
|
||||
'Doc.Users.Manage',
|
||||
user.id
|
||||
);
|
||||
await this.permission.revokePage(doc.workspace, doc.guid, input.userIds);
|
||||
await this.permission.revokePage(doc.workspace, doc.guid, input.userId);
|
||||
this.logger.log('Revoke doc user roles', {
|
||||
...pairs,
|
||||
userIds: input.userIds,
|
||||
userId: input.userId,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -521,38 +529,36 @@ export class PagePermissionResolver {
|
||||
'Expect doc not to be workspace'
|
||||
);
|
||||
}
|
||||
await this.permission.checkWorkspace(
|
||||
|
||||
await this.permission.checkPagePermission(
|
||||
doc.workspace,
|
||||
user.id,
|
||||
WorkspaceRole.Collaborator
|
||||
doc.guid,
|
||||
input.role === DocRole.Owner ? 'Doc.TransferOwner' : 'Doc.Users.Manage',
|
||||
user.id
|
||||
);
|
||||
|
||||
await this.permission.grantPage(
|
||||
doc.workspace,
|
||||
doc.guid,
|
||||
input.userId,
|
||||
input.role
|
||||
);
|
||||
|
||||
if (input.role === DocRole.Owner) {
|
||||
const ret = await this.permission.grantPagePermission(
|
||||
doc.workspace,
|
||||
doc.guid,
|
||||
[input.userId],
|
||||
input.role
|
||||
);
|
||||
this.logger.log('Transfer doc owner', {
|
||||
...pairs,
|
||||
userId: input.userId,
|
||||
role: input.role,
|
||||
});
|
||||
return ret.length > 0;
|
||||
} else {
|
||||
await this.permission.updatePagePermission(
|
||||
doc.workspace,
|
||||
doc.guid,
|
||||
input.userId,
|
||||
input.role
|
||||
);
|
||||
this.logger.log('Update doc user role', {
|
||||
...pairs,
|
||||
userId: input.userId,
|
||||
role: input.role,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
@@ -580,7 +586,7 @@ export class PagePermissionResolver {
|
||||
);
|
||||
}
|
||||
try {
|
||||
await this.permission.checkCloudPagePermission(
|
||||
await this.permission.checkPagePermission(
|
||||
doc.workspace,
|
||||
doc.guid,
|
||||
'Doc.Users.Manage',
|
||||
|
||||
Reference in New Issue
Block a user