Skip to content

Commit

Permalink
Merge pull request #9044 from weseek/imprv/152978-show-pages-with-gra…
Browse files Browse the repository at this point in the history
…nts-that-are-set-to-be-visible-in-security-settings-on-recentchanges-as-well

imprv: Show pages with grants that are set to be visible in security settings on RecentChanges and PageTree as well
  • Loading branch information
yuki-takei committed Aug 26, 2024
2 parents a58919b + fe24ca1 commit 389b4b4
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 10 deletions.
28 changes: 23 additions & 5 deletions apps/app/src/server/models/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ type PaginatedPages = {
offset: number
}

export type FindRecentUpdatedPagesOption = {
offset: number,
limit: number,
includeWipPage: boolean,
includeTrashed: boolean,
isRegExpEscapedFromPath: boolean,
sort: 'updatedAt'
desc: number
hideRestrictedByOwner: boolean,
hideRestrictedByGroup: boolean,
}

export type CreateMethod = (path: string, body: string, user, options: IOptionsForCreate) => Promise<HydratedDocument<PageDocument>>

export interface PageModel extends Model<PageDocument> {
Expand All @@ -79,7 +91,7 @@ export interface PageModel extends Model<PageDocument> {
countByPathAndViewer(path: string | null, user, userGroups?, includeEmpty?:boolean): Promise<number>
findParentByPath(path: string | null): Promise<HydratedDocument<PageDocument> | null>
findTargetAndAncestorsByPathOrId(pathOrId: string): Promise<TargetAndAncestorsResult>
findRecentUpdatedPages(path: string, user, option, includeEmpty?: boolean): Promise<PaginatedPages>
findRecentUpdatedPages(path: string, user, option: FindRecentUpdatedPagesOption, includeEmpty?: boolean): Promise<PaginatedPages>
generateGrantCondition(
user, userGroups: ObjectIdLike[] | null, includeAnyoneWithTheLink?: boolean, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
): { $or: any[] }
Expand Down Expand Up @@ -414,13 +426,19 @@ export class PageQueryBuilder {
}

// add viewer condition to PageQueryBuilder instance
async addViewerCondition(user, userGroups = null, includeAnyoneWithTheLink = false): Promise<PageQueryBuilder> {
async addViewerCondition(
user,
userGroups = null,
includeAnyoneWithTheLink = false,
showPagesRestrictedByOwner = false,
showPagesRestrictedByGroup = false,
): Promise<PageQueryBuilder> {
const relatedUserGroups = (user != null && userGroups == null) ? [
...(await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user)),
...(await ExternalUserGroupRelation.findAllUserGroupIdsRelatedToUser(user)),
] : userGroups;

this.addConditionToFilteringByViewer(user, relatedUserGroups, includeAnyoneWithTheLink);
this.addConditionToFilteringByViewer(user, relatedUserGroups, includeAnyoneWithTheLink, showPagesRestrictedByOwner, showPagesRestrictedByGroup);
return this;
}

Expand Down Expand Up @@ -664,7 +682,7 @@ schema.statics.countByPathAndViewer = async function(path: string | null, user,
};

schema.statics.findRecentUpdatedPages = async function(
path: string, user, options, includeEmpty = false,
path: string, user, options: FindRecentUpdatedPagesOption, includeEmpty = false,
): Promise<PaginatedPages> {

const sortOpt = {};
Expand All @@ -690,7 +708,7 @@ schema.statics.findRecentUpdatedPages = async function(

queryBuilder.addConditionToListWithDescendants(path, options);
queryBuilder.populateDataToList(User.USER_FIELDS_EXCEPT_CONFIDENTIAL);
await queryBuilder.addViewerCondition(user);
await queryBuilder.addViewerCondition(user, undefined, undefined, !options.hideRestrictedByOwner, !options.hideRestrictedByGroup);
const pages = await Page.paginate(queryBuilder.query.clone(), {
lean: true, sort: sortOpt, offset: options.offset, limit: options.limit,
});
Expand Down
9 changes: 8 additions & 1 deletion apps/app/src/server/routes/apiv3/page-listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { query, oneOf } from 'express-validator';
import type { HydratedDocument } from 'mongoose';
import mongoose from 'mongoose';

import { configManager } from '~/server/service/config-manager';
import type { IPageGrantService } from '~/server/service/page-grant';
import loggerFactory from '~/utils/logger';

Expand All @@ -18,6 +19,7 @@ import type { PageDocument, PageModel } from '../../models/page';

import type { ApiV3Response } from './interfaces/apiv3-response';


const logger = loggerFactory('growi:routes:apiv3:page-tree');

/*
Expand Down Expand Up @@ -103,8 +105,13 @@ const routerFactory = (crowi: Crowi): Router => {

const pageService = crowi.pageService;

const hideRestrictedByOwner = await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner');
const hideRestrictedByGroup = await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup');

try {
const pages = await pageService.findChildrenByParentPathOrIdAndViewer((id || path)as string, req.user);
const pages = await pageService.findChildrenByParentPathOrIdAndViewer(
(id || path)as string, req.user, undefined, !hideRestrictedByOwner, !hideRestrictedByGroup,
);
return res.apiv3({ children: pages });
}
catch (err) {
Expand Down
9 changes: 9 additions & 0 deletions apps/app/src/server/routes/apiv3/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@ module.exports = (crowi) => {
const offset = parseInt(req.query.offset) || 0;
const includeWipPage = req.query.includeWipPage === 'true'; // Need validation using express-validator

const hideRestrictedByOwner = await crowi.configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner');
const hideRestrictedByGroup = await crowi.configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup');

/**
* @type {import('~/server/models/page').FindRecentUpdatedPagesOption}
*/
const queryOptions = {
offset,
limit,
Expand All @@ -234,7 +240,10 @@ module.exports = (crowi) => {
isRegExpEscapedFromPath: true,
sort: 'updatedAt',
desc: -1,
hideRestrictedByOwner,
hideRestrictedByGroup,
};

try {
const result = await Page.findRecentUpdatedPages('/', req.user, queryOptions);
if (result.pages.length > limit) {
Expand Down
11 changes: 8 additions & 3 deletions apps/app/src/server/service/page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4332,8 +4332,13 @@ class PageService implements IPageService {
/*
* Find all children by parent's path or id. Using id should be prioritized
*/
async findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups = null)
: Promise<(HydratedDocument<PageDocument> & { processData?: IPageOperationProcessData })[]> {
async findChildrenByParentPathOrIdAndViewer(
parentPathOrId: string,
user,
userGroups = null,
showPagesRestrictedByOwner = false,
showPagesRestrictedByGroup = false,
): Promise<(HydratedDocument<PageDocument> & { processData?: IPageOperationProcessData })[]> {
const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
let queryBuilder: PageQueryBuilder;
if (hasSlash(parentPathOrId)) {
Expand All @@ -4346,7 +4351,7 @@ class PageService implements IPageService {
// Use $eq for user-controlled sources. see: https://codeql.github.com/codeql-query-help/javascript/js-sql-injection/#recommendation
queryBuilder = new PageQueryBuilder(Page.find({ parent: { $eq: parentId } } as any), true); // TODO: improve type
}
await queryBuilder.addViewerCondition(user, userGroups);
await queryBuilder.addViewerCondition(user, userGroups, undefined, showPagesRestrictedByOwner, showPagesRestrictedByGroup);

const pages: HydratedDocument<PageDocument>[] = await queryBuilder
.addConditionToSortPagesByAscPath()
Expand Down
4 changes: 3 additions & 1 deletion apps/app/src/server/service/page/page-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export interface IPageService {
getEventEmitter: () => EventEmitter,
deleteMultipleCompletely: (pages: ObjectIdLike[], user: IUser | undefined) => Promise<void>,
findAncestorsChildrenByPathAndViewer(path: string, user, userGroups?): Promise<Record<string, PageDocument[]>>,
findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>,
findChildrenByParentPathOrIdAndViewer(
parentPathOrId: string, user, userGroups?, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
): Promise<PageDocument[]>,
shortBodiesMapByPageIds(pageIds?: Types.ObjectId[], user?): Promise<Record<string, string | null>>,
constructBasicPageInfo(page: PageDocument, isGuestUser?: boolean): IPageInfo | Omit<IPageInfoForEntity, 'bookmarkCount'>,
normalizeAllPublicPages(): Promise<void>,
Expand Down

0 comments on commit 389b4b4

Please sign in to comment.