mirror of
https://github.com/docmost/docmost.git
synced 2026-08-30 18:36:26 +08:00
restriction info
This commit is contained in:
@@ -91,7 +91,7 @@ export class PagePermissionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post('list')
|
@Post('members')
|
||||||
async getPagePermissions(
|
async getPagePermissions(
|
||||||
@Body() dto: PageIdDto,
|
@Body() dto: PageIdDto,
|
||||||
@Body() pagination: PaginationOptions,
|
@Body() pagination: PaginationOptions,
|
||||||
@@ -103,6 +103,15 @@ export class PagePermissionController {
|
|||||||
pagination,
|
pagination,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post('info')
|
||||||
|
async getPageRestrictionInfo(
|
||||||
|
@Body() dto: PageIdDto,
|
||||||
|
@AuthUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.pagePermissionService.getPageRestrictionInfo(dto.pageId, user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateMemberIds(dto: { userIds?: string[]; groupIds?: string[] }) {
|
function validateMemberIds(dto: { userIds?: string[]; groupIds?: string[] }) {
|
||||||
|
|||||||
@@ -26,6 +26,18 @@ import {
|
|||||||
SpaceCaslSubject,
|
SpaceCaslSubject,
|
||||||
} from '../../casl/interfaces/space-ability.type';
|
} from '../../casl/interfaces/space-ability.type';
|
||||||
|
|
||||||
|
export type PageRestrictionInfo = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
hasDirectRestriction: boolean;
|
||||||
|
hasInheritedRestriction: boolean;
|
||||||
|
userAccess: {
|
||||||
|
canView: boolean;
|
||||||
|
canEdit: boolean;
|
||||||
|
canManage: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PagePermissionService {
|
export class PagePermissionService {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -342,6 +354,86 @@ export class PagePermissionService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get page restriction info for the current user.
|
||||||
|
*
|
||||||
|
* Security: User must be a space member. Returns 404 for pages the user cannot view
|
||||||
|
* to avoid leaking existence of restricted pages.
|
||||||
|
*/
|
||||||
|
async getPageRestrictionInfo(
|
||||||
|
pageId: string,
|
||||||
|
authUser: User,
|
||||||
|
): Promise<PageRestrictionInfo> {
|
||||||
|
const page = await this.pageRepo.findById(pageId);
|
||||||
|
if (!page) {
|
||||||
|
throw new NotFoundException('Page not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const ability = await this.spaceAbility.createForUser(
|
||||||
|
authUser,
|
||||||
|
page.spaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [hasDirectRestriction, hasAnyRestriction, canView, canEdit] =
|
||||||
|
await Promise.all([
|
||||||
|
this.pagePermissionRepo.findPageAccessByPageId(pageId).then((r) => !!r),
|
||||||
|
this.pagePermissionRepo.hasRestrictedAncestor(pageId),
|
||||||
|
this.canViewPage(authUser.id, pageId),
|
||||||
|
this.canEditPage(authUser.id, pageId),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Security: return 404 to avoid leaking existence of restricted pages
|
||||||
|
if (!canView) {
|
||||||
|
throw new NotFoundException('Page not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasInheritedRestriction = hasAnyRestriction && !hasDirectRestriction;
|
||||||
|
|
||||||
|
// Determine if user can manage permissions
|
||||||
|
const canManage = this.computeCanManage(ability, canEdit, canView);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: page.id,
|
||||||
|
title: page.title,
|
||||||
|
hasDirectRestriction,
|
||||||
|
hasInheritedRestriction,
|
||||||
|
userAccess: {
|
||||||
|
canView,
|
||||||
|
canEdit,
|
||||||
|
canManage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute if user can manage page permissions based on precomputed access values.
|
||||||
|
* Mirrors validateWriteAccess logic without throwing.
|
||||||
|
*/
|
||||||
|
private computeCanManage(
|
||||||
|
ability: Awaited<ReturnType<SpaceAbilityFactory['createForUser']>>,
|
||||||
|
canEdit: boolean,
|
||||||
|
canView: boolean,
|
||||||
|
): boolean {
|
||||||
|
if (ability.cannot(SpaceCaslAction.Edit, SpaceCaslSubject.Page)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canEdit) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSpaceAdmin = ability.can(
|
||||||
|
SpaceCaslAction.Manage,
|
||||||
|
SpaceCaslSubject.Page,
|
||||||
|
);
|
||||||
|
|
||||||
|
return isSpaceAdmin && canView;
|
||||||
|
}
|
||||||
|
|
||||||
async validateLastWriter(pageAccessId: string): Promise<void> {
|
async validateLastWriter(pageAccessId: string): Promise<void> {
|
||||||
const writerCount =
|
const writerCount =
|
||||||
await this.pagePermissionRepo.countWritersByPageAccessId(pageAccessId);
|
await this.pagePermissionRepo.countWritersByPageAccessId(pageAccessId);
|
||||||
|
|||||||
Reference in New Issue
Block a user