diff --git a/apps/server/src/core/page-access/page-access.service.ts b/apps/server/src/core/page-access/page-access.service.ts index 893b8486..f37de37d 100644 --- a/apps/server/src/core/page-access/page-access.service.ts +++ b/apps/server/src/core/page-access/page-access.service.ts @@ -37,6 +37,34 @@ export class PageAccessService { } } + /** + * Validate user can view page AND return effective canEdit permission. + * Combines access check + edit permission in a single query pass. + */ + async validateCanViewWithPermissions( + page: Page, + user: User, + ): Promise<{ canEdit: boolean }> { + const ability = await this.spaceAbility.createForUser(user, page.spaceId); + + if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) { + throw new ForbiddenException(); + } + + const { hasAnyRestriction, canAccess, canEdit } = + await this.pagePermissionRepo.canUserEditPage(user.id, page.id); + + if (hasAnyRestriction && !canAccess) { + throw new ForbiddenException(); + } + + return { + canEdit: hasAnyRestriction + ? canEdit + : ability.can(SpaceCaslAction.Edit, SpaceCaslSubject.Page), + }; + } + /** * Validate user can edit page, throws ForbiddenException if not. * If page has restrictions: page-level writer permission determines access. diff --git a/apps/server/src/core/page/page.controller.ts b/apps/server/src/core/page/page.controller.ts index 3d7f102a..c21e6fd6 100644 --- a/apps/server/src/core/page/page.controller.ts +++ b/apps/server/src/core/page/page.controller.ts @@ -67,7 +67,10 @@ export class PageController { throw new NotFoundException('Page not found'); } - await this.pageAccessService.validateCanView(page, user); + const { canEdit } = + await this.pageAccessService.validateCanViewWithPermissions(page, user); + + const permissions = { canEdit }; if (dto.format && dto.format !== 'json' && page.content) { const contentOutput = @@ -77,10 +80,11 @@ export class PageController { return { ...page, content: contentOutput, + permissions, }; } - return page; + return { ...page, permissions }; } @HttpCode(HttpStatus.OK) @@ -120,6 +124,11 @@ export class PageController { createPageDto, ); + const { canEdit } = + await this.pageAccessService.validateCanViewWithPermissions(page, user); + + const permissions = { canEdit }; + if ( createPageDto.format && createPageDto.format !== 'json' && @@ -129,10 +138,10 @@ export class PageController { createPageDto.format === 'markdown' ? jsonToMarkdown(page.content) : jsonToHtml(page.content); - return { ...page, content: contentOutput }; + return { ...page, content: contentOutput, permissions }; } - return page; + return { ...page, permissions }; } @HttpCode(HttpStatus.OK) @@ -152,6 +161,8 @@ export class PageController { user, ); + const permissions = { canEdit: true }; + if ( updatePageDto.format && updatePageDto.format !== 'json' && @@ -161,10 +172,10 @@ export class PageController { updatePageDto.format === 'markdown' ? jsonToMarkdown(updatedPage.content) : jsonToHtml(updatedPage.content); - return { ...updatedPage, content: contentOutput }; + return { ...updatedPage, content: contentOutput, permissions }; } - return updatedPage; + return { ...updatedPage, permissions }; } @HttpCode(HttpStatus.OK)