Просмотр исходного кода

validate multiple page group change for descendent update

Futa Arai 2 лет назад
Родитель
Сommit
f50719d516

+ 0 - 1
apps/app/src/pages/[[...path]].page.tsx

@@ -30,7 +30,6 @@ import type { IPageGrantData } from '~/interfaces/page';
 import type { RendererConfig } from '~/interfaces/services/renderer';
 import type { PageModel, PageDocument } from '~/server/models/page';
 import type { PageRedirectModel } from '~/server/models/page-redirect';
-import PageService from '~/server/service/page';
 import {
   useCurrentUser,
   useIsForbidden, useIsSharedUser,

+ 17 - 22
apps/app/src/server/service/page.ts

@@ -35,6 +35,7 @@ import {
 import { createBatchStream } from '~/server/util/batch-stream';
 import loggerFactory from '~/utils/logger';
 import { prepareDeleteConfigValuesForCalc } from '~/utils/page-delete-config';
+import { batchProcessPromiseAll } from '~/utils/promise';
 
 import { ObjectIdLike } from '../interfaces/mongoose-utils';
 import { Attachment } from '../models';
@@ -2426,28 +2427,22 @@ class PageService {
 
     const grant = parentPage.grant;
 
-    if (grant === PageGrant.GRANT_USER_GROUP) {
-      const userRelatedParentGrantedGroups = await this.getUserRelatedGrantedGroups(parentPage, user);
-      const childPages = await builder.query;
-      const childPageUpdatePromises = childPages.map((childPage) => {
-        return (async() => {
-          const childGrantedGroups = childPage.grantedGroups || [];
-          const userRelatedChildGrantedGroupIds = (await this.getUserRelatedGrantedGroups(childPage, user)).map(g => getIdForRef(g.item));
-          const userUnrelatedChildGrantedGroups = childGrantedGroups.filter(g => !userRelatedChildGrantedGroupIds.includes(getIdForRef(g.item)));
-          const newChildGrantedGroups = [...userUnrelatedChildGrantedGroups, ...userRelatedParentGrantedGroups];
-          childPage.grantedGroups = newChildGrantedGroups;
-          childPage.save();
-        })();
-      });
-      await Promise.all(childPageUpdatePromises);
-    }
-    else {
-      await builder.query.updateMany({}, {
-        grant,
-        grantedGroups: null,
-        grantedUsers: grant === PageGrant.GRANT_OWNER ? [user._id] : null,
-      });
-    }
+    const childPages = await builder.query;
+    await batchProcessPromiseAll(childPages, 20, async(childPage: any) => {
+      let newChildGrantedGroups: IGrantedGroup[] = [];
+      if (grant === PageGrant.GRANT_USER_GROUP) {
+        const userRelatedParentGrantedGroups = await this.getUserRelatedGrantedGroups(parentPage, user);
+        newChildGrantedGroups = await this.getNewGrantedGroups(userRelatedParentGrantedGroups, childPage, user);
+      }
+      const canChangeGrant = await this.pageGrantService
+        .validateGrantChange(user, childPage.grantedGroups, PageGrant.GRANT_USER_GROUP, newChildGrantedGroups);
+      if (canChangeGrant) {
+        childPage.grant = grant;
+        childPage.grantedUsers = grant === PageGrant.GRANT_OWNER ? [user._id] : null;
+        childPage.grantedGroups = newChildGrantedGroups;
+        childPage.save();
+      }
+    });
   }
 
   /**

+ 5 - 7
apps/app/src/server/service/search-delegator/elasticsearch.ts

@@ -368,13 +368,11 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
       });
     }
 
-    let grantedGroupIds = null;
-    if (page.grantedGroups != null) {
-      grantedGroupIds = page.grantedGroups.map((group) => {
-        const groupId = (group.item._id == null) ? group.item : group.item._id;
-        return groupId.toString();
-      });
-    }
+    let grantedGroupIds = [];
+    grantedGroupIds = page.grantedGroups.map((group) => {
+      const groupId = (group.item._id == null) ? group.item : group.item._id;
+      return groupId.toString();
+    });
 
     return {
       grant: page.grant,

+ 22 - 1
apps/app/test/integration/models/v5.page.test.js

@@ -260,6 +260,21 @@ describe('Page', () => {
         grantedGroups: [],
         parent: upodPageIdgAB1,
       },
+      // grant user A and B with independent groups
+      {
+        path: '/gAB_upod_1/gA_gB_upod_1',
+        grant: PageGrant.GRANT_USER_GROUP,
+        creator: upodUserA,
+        lastUpdateUser: upodUserA,
+        grantedUsers: null,
+        grantedGroups: [
+          { item: upodUserGroupIdA, type: GroupType.userGroup },
+          { item: upodExternalUserGroupIdA, type: GroupType.externalUserGroup },
+          { item: upodUserGroupIdB, type: GroupType.userGroup },
+          { item: upodExternalUserGroupIdB, type: GroupType.externalUserGroup },
+        ],
+        parent: upodPageIdgAB1,
+      },
       // case 2
       {
         _id: upodPageIdPublic2,
@@ -1530,14 +1545,17 @@ describe('Page', () => {
       const upodPagegAB = await Page.findOne({ path: '/gAB_upod_1' });
       const upodPagegB = await Page.findOne({ path: '/gAB_upod_1/gB_upod_1' });
       const upodPageonlyB = await Page.findOne({ path: '/gAB_upod_1/onlyB_upod_1' });
+      const upodPagegAgB = await Page.findOne({ path: '/gAB_upod_1/gA_gB_upod_1' });
 
       expect(upodPagegAB).not.toBeNull();
       expect(upodPagegB).not.toBeNull();
       expect(upodPageonlyB).not.toBeNull();
+      expect(upodPagegAgB).not.toBeNull();
 
       expect(upodPagegAB.grant).toBe(PageGrant.GRANT_USER_GROUP);
       expect(upodPagegB.grant).toBe(PageGrant.GRANT_USER_GROUP);
       expect(upodPageonlyB.grant).toBe(PageGrant.GRANT_OWNER);
+      expect(upodPagegAgB.grant).toBe(PageGrant.GRANT_USER_GROUP);
 
       // Update
       const options = {
@@ -1548,6 +1566,7 @@ describe('Page', () => {
 
       const upodPagegBUpdated = await Page.findOne({ path: '/gAB_upod_1/gB_upod_1' });
       const upodPageonlyBUpdated = await Page.findOne({ path: '/gAB_upod_1/onlyB_upod_1' });
+      const upodPagegAgBUpdated = await Page.findOne({ path: '/gAB_upod_1/gA_gB_upod_1' });
 
       // Changed
       const newGrant = PageGrant.GRANT_PUBLIC;
@@ -1557,8 +1576,10 @@ describe('Page', () => {
       expect(upodPagegBUpdated.grantedGroups).toStrictEqual(upodPagegB.grantedGroups);
       expect(upodPageonlyBUpdated.grant).toBe(PageGrant.GRANT_OWNER);
       expect(upodPageonlyBUpdated.grantedUsers).toStrictEqual(upodPageonlyB.grantedUsers);
+      expect(upodPagegAgBUpdated.grant).toBe(PageGrant.GRANT_USER_GROUP);
+      expect(upodPagegAgBUpdated.grantedGroups).toStrictEqual(upodPagegAgB.grantedGroups);
     });
-    test('(case 2) it should update all granted descendant pages when all descendant pages are granted by the operator', async() => {
+    test('(case 2) it should update all granted descendant pages when all descendant pages are granted to the operator', async() => {
       const upodPagePublic = await Page.findOne({ path: '/public_upod_2' });
       const upodPagegA = await Page.findOne({ path: '/public_upod_2/gA_upod_2' });
       const upodPagegAIsolated = await Page.findOne({ path: '/public_upod_2/gAIsolated_upod_2' });

+ 38 - 10
apps/app/test/integration/service/page-grant.test.js

@@ -399,7 +399,7 @@ describe('PageGrantService', () => {
       const targetPath = '/NEW';
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -423,7 +423,7 @@ describe('PageGrantService', () => {
       const targetPath = `${pageRootPublicPath}/NEW`;
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -447,7 +447,7 @@ describe('PageGrantService', () => {
       const targetPath = `${pageE1PublicPath}/NEW`;
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -459,7 +459,7 @@ describe('PageGrantService', () => {
       const targetPath = `${pageE2User1Path}/NEW`;
       const grant = Page.GRANT_OWNER;
       const grantedUserIds = [user1._id];
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -471,7 +471,7 @@ describe('PageGrantService', () => {
       const targetPath = `${pageE3GroupParentPath}/NEW`;
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -497,7 +497,7 @@ describe('PageGrantService', () => {
       const targetPath = emptyPagePath1;
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = true;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -509,7 +509,7 @@ describe('PageGrantService', () => {
       const targetPath = emptyPagePath2;
       const grant = Page.GRANT_OWNER;
       const grantedUserIds = [user1._id];
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = true;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -533,7 +533,7 @@ describe('PageGrantService', () => {
       const targetPath = emptyPagePath1;
       const grant = Page.GRANT_OWNER;
       const grantedUserIds = [user1._id];
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = true;
 
       const result = await pageGrantService.isGrantNormalized(user1, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants);
@@ -547,7 +547,7 @@ describe('PageGrantService', () => {
       const targetPath = pageMultipleGroupTreesAndUsersPath;
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(
@@ -561,7 +561,35 @@ describe('PageGrantService', () => {
       const targetPath = pageMultipleGroupTreesAndUsersPath;
       const grant = Page.GRANT_PUBLIC;
       const grantedUserIds = null;
-      const grantedGroupIds = null;
+      const grantedGroupIds = [];
+      const shouldCheckDescendants = false;
+
+      const result = await pageGrantService.isGrantNormalized(
+        user2, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants, false, multipleGroupTreesAndUsersPage.grantedGroups,
+      );
+
+      expect(result).toBe(false);
+    });
+
+    test('Should return false when Target: partially owned by User2 (belongs to one of the groups), and change to owner grant', async() => {
+      const targetPath = pageMultipleGroupTreesAndUsersPath;
+      const grant = Page.GRANT_OWNER;
+      const grantedUserIds = [user2._id];
+      const grantedGroupIds = [];
+      const shouldCheckDescendants = false;
+
+      const result = await pageGrantService.isGrantNormalized(
+        user2, targetPath, grant, grantedUserIds, grantedGroupIds, shouldCheckDescendants, false, multipleGroupTreesAndUsersPage.grantedGroups,
+      );
+
+      expect(result).toBe(false);
+    });
+
+    test('Should return false when Target: partially owned by User2 (belongs to one of the groups), and change to restricted grant', async() => {
+      const targetPath = pageMultipleGroupTreesAndUsersPath;
+      const grant = Page.GRANT_RESTRICTED;
+      const grantedUserIds = null;
+      const grantedGroupIds = [];
       const shouldCheckDescendants = false;
 
       const result = await pageGrantService.isGrantNormalized(