Explorar o código

add nonUserRelatedGrantedGroups and userRelatedGroups to is-grant-normalized response

Futa Arai %!s(int64=2) %!d(string=hai) anos
pai
achega
8542c0ec96

+ 30 - 4
apps/app/src/interfaces/page.ts

@@ -2,6 +2,8 @@ import type {
   GroupType, IGrantedGroup, IPageHasId, Nullable, PageGrant, Origin,
 } from '@growi/core';
 
+import type { ExternalGroupProviderType } from '~/features/external-user-group/interfaces/external-user-group';
+
 import type { IPageOperationProcessData } from './page-operation';
 
 export {
@@ -10,13 +12,37 @@ export {
 
 export type IPageForItem = Partial<IPageHasId & {isTarget?: boolean, processData?: IPageOperationProcessData}>;
 
-export type IPageGrantData = {
-  grant: PageGrant,
-  userRelatedGrantedGroups?: {
+export const UserGroupPageGrantStatus = {
+  isGranted: 'isGranted',
+  notGranted: 'notGranted',
+  cannotGrant: 'cannotGrant',
+};
+type UserGroupPageGrantStatus = typeof UserGroupPageGrantStatus[keyof typeof UserGroupPageGrantStatus];
+export type UserRelatedGroupsData = {
+  id: string,
+  name: string,
+  type: GroupType,
+  provider?: ExternalGroupProviderType,
+  status: UserGroupPageGrantStatus,
+}
+export type GroupGrantData = {
+  userRelatedGroups: UserRelatedGroupsData[],
+  nonUserRelatedGrantedGroups: {
     id: string,
     name: string,
     type: GroupType,
-  }[]
+    provider?: ExternalGroupProviderType,
+  }[],
+}
+// current grant data of page
+export type IPageGrantData = {
+  grant: PageGrant,
+  groupGrantData?: GroupGrantData,
+}
+// grant selected by user which is not yet applied
+export type IPageSelectedGrant = {
+  grant: PageGrant,
+  userRelatedGrantedGroups?: IGrantedGroup[]
 }
 
 export type IDeleteSinglePageApiv1Result = {

+ 17 - 35
apps/app/src/server/routes/apiv3/page/index.js → apps/app/src/server/routes/apiv3/page/index.ts

@@ -1,23 +1,25 @@
 import path from 'path';
 
+import type { IPage } from '@growi/core';
 import {
   AllSubscriptionStatusType, SubscriptionStatusType,
 } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { convertToNewAffiliationPath } from '@growi/core/dist/utils/page-path-utils';
+import mongoose from 'mongoose';
 import sanitize from 'sanitize-filename';
 
-import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
 import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
+import type { IPageGrantData } from '~/interfaces/page';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
 import { excludeReadOnlyUser } from '~/server/middlewares/exclude-read-only-user';
 import { GlobalNotificationSettingEvent } from '~/server/models';
+import type { PageModel } from '~/server/models/page';
 import Subscription from '~/server/models/subscription';
-import UserGroup from '~/server/models/user-group';
 import { configManager } from '~/server/service/config-manager';
+import type { IPageGrantService } from '~/server/service/page-grant';
 import { preNotifyService } from '~/server/service/pre-notify';
-import { divideByType } from '~/server/util/granted-group';
 import loggerFactory from '~/utils/logger';
 
 import { checkPageExistenceHandlersFactory } from './check-page-existence';
@@ -466,8 +468,7 @@ module.exports = (crowi) => {
       return res.apiv3Err(err, 500);
     }
 
-    const result = { page };
-    result.seenUser = page.seenUsers;
+    const result = { page, seenUser: page.seenUsers };
 
     const parameters = {
       targetModel: SupportedTargetModel.MODEL_PAGE,
@@ -568,7 +569,9 @@ module.exports = (crowi) => {
   router.get('/is-grant-normalized', loginRequiredStrictly, validator.isGrantNormalized, apiV3FormValidator, async(req, res) => {
     const { pageId } = req.query;
 
-    const Page = crowi.model('Page');
+    const Page = mongoose.model<IPage, PageModel>('Page');
+    const pageGrantService = crowi.pageGrantService as IPageGrantService;
+
     const page = await Page.findByIdAndViewer(pageId, req.user, null, false);
 
     if (page == null) {
@@ -580,28 +583,19 @@ module.exports = (crowi) => {
       path, grant, grantedUsers, grantedGroups,
     } = page;
 
-    let isGrantNormalized;
+    let isGrantNormalized = false;
     try {
-      isGrantNormalized = await crowi.pageGrantService.isGrantNormalized(req.user, path, grant, grantedUsers, grantedGroups, false, false);
+      isGrantNormalized = await pageGrantService.isGrantNormalized(req.user, path, grant, grantedUsers, grantedGroups, false, false);
     }
     catch (err) {
       logger.error('Error occurred while processing isGrantNormalized.', err);
       return res.apiv3Err(err, 500);
     }
 
-    const userRelatedGrantedGroups = await crowi.pageGrantService.getUserRelatedGrantedGroups(page, req.user);
-    const { grantedUserGroups, grantedExternalUserGroups } = divideByType(userRelatedGrantedGroups);
-    const currentPageUserGroups = await UserGroup.find({ _id: { $in: grantedUserGroups } });
-    const currentPageExternalUserGroups = await ExternalUserGroup.find({ _id: { $in: grantedExternalUserGroups } });
-    const grantedUserGroupData = currentPageUserGroups.map((group) => {
-      return { id: group._id, name: group.name, type: 'UserGroup' };
-    });
-    const grantedExternalUserGroupData = currentPageExternalUserGroups.map((group) => {
-      return { id: group._id, name: group.name, type: 'ExternalUserGroup' };
-    });
-    const currentPageGrant = {
+    const currentPageGroupGrantData = await pageGrantService.getPageGroupGrantData(page, req.user);
+    const currentPageGrant: IPageGrantData = {
       grant,
-      userRelatedGrantedGroups: [...grantedUserGroupData, ...grantedExternalUserGroupData],
+      groupGrantData: currentPageGroupGrantData,
     };
 
     // page doesn't have parent page
@@ -626,22 +620,10 @@ module.exports = (crowi) => {
       return res.apiv3({ isGrantNormalized, grantData });
     }
 
-    const userRelatedParentGrantedGroups = await crowi.pageGrantService.getUserRelatedGrantedGroups(parentPage, req.user);
-    const {
-      grantedUserGroups: parentGrantedUserGroupIds,
-      grantedExternalUserGroups: parentGrantedExternalUserGroupIds,
-    } = divideByType(userRelatedParentGrantedGroups);
-    const parentPageUserGroups = await UserGroup.find({ _id: { $in: parentGrantedUserGroupIds } });
-    const parentPageExternalUserGroups = await ExternalUserGroup.find({ _id: { $in: parentGrantedExternalUserGroupIds } });
-    const parentGrantedUserGroupData = parentPageUserGroups.map((group) => {
-      return { id: group._id, name: group.name };
-    });
-    const parentGrantedExternalUserGroupData = parentPageExternalUserGroups.map((group) => {
-      return { id: group._id, name: group.name };
-    });
-    const parentPageGrant = {
+    const parentPageGroupGrantData = await pageGrantService.getPageGroupGrantData(parentPage, req.user);
+    const parentPageGrant: IPageGrantData = {
       grant: parentPage.grant,
-      userRelatedGrantedGroups: [...parentGrantedUserGroupData, ...parentGrantedExternalUserGroupData],
+      groupGrantData: parentPageGroupGrantData,
     };
 
     const grantData = {

+ 58 - 2
apps/app/src/server/service/page-grant.ts

@@ -1,3 +1,4 @@
+import type { IPage } from '@growi/core';
 import {
   type IGrantedGroup,
   PageGrant, GroupType, getIdForRef, isPopulated,
@@ -8,8 +9,10 @@ import {
 import escapeStringRegexp from 'escape-string-regexp';
 import mongoose from 'mongoose';
 
+import type { ExternalGroupProviderType } from '~/features/external-user-group/interfaces/external-user-group';
 import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
+import { UserGroupPageGrantStatus, type GroupGrantData } from '~/interfaces/page';
 import type { IRecordApplicableGrant, PopulatedGrantedGroup } from '~/interfaces/page-grant';
 import type { PageDocument, PageModel } from '~/server/models/page';
 import UserGroup from '~/server/models/user-group';
@@ -98,9 +101,11 @@ export interface IPageGrantService {
     userRelatedGroups: PopulatedGrantedGroup[], previousGrantedGroups: IGrantedGroup[], grant?: PageGrant, grantedGroups?: IGrantedGroup[],
   ) => boolean,
   getUserRelatedGroups: (user) => Promise<PopulatedGrantedGroup[]>,
+  getPopulatedGrantedGroups: (grantedGroups: IGrantedGroup[]) => Promise<PopulatedGrantedGroup[]>,
   getUserRelatedGrantedGroups: (page: PageDocument, user) => Promise<IGrantedGroup[]>,
   getUserRelatedGrantedGroupsSyncronously: (userRelatedGroups: PopulatedGrantedGroup[], page: PageDocument) => IGrantedGroup[],
-  isUserGrantedPageAccess: (page: PageDocument, user, userRelatedGroups: PopulatedGrantedGroup[]) => boolean
+  isUserGrantedPageAccess: (page: PageDocument, user, userRelatedGroups: PopulatedGrantedGroup[]) => boolean,
+  getPageGroupGrantData: (page: PageDocument, user) => Promise<GroupGrantData>
 }
 
 class PageGrantService implements IPageGrantService {
@@ -564,7 +569,7 @@ class PageGrantService implements IPageGrantService {
   }
 
   async calcApplicableGrantData(page, user): Promise<IRecordApplicableGrant> {
-    const Page = mongoose.model('Page') as unknown as PageModel;
+    const Page = mongoose.model<IPage, PageModel>('Page');
 
     // -- Public only if top page
     const isOnlyPublicApplicable = isTopPage(page.path);
@@ -655,6 +660,43 @@ class PageGrantService implements IPageGrantService {
     return data;
   }
 
+  async getPageGroupGrantData(page: PageDocument, user): Promise<GroupGrantData> {
+    const userRelatedGroups = await this.getUserRelatedGroups(user);
+    const userRelatedGroupsData = userRelatedGroups.map((group) => {
+      const provider = group.type === GroupType.externalUserGroup ? group.item.provider : undefined;
+      return {
+        // TODO: change un-grantable groups to UserGroupPageGrantStatus.cannotGrant (https://redmine.weseek.co.jp/issues/142310)
+        id: group.item._id.toString(), name: group.item.name, type: group.type, provider, status: UserGroupPageGrantStatus.notGranted,
+      };
+    });
+
+    const nonUserRelatedGrantedGroups: {
+      id: string,
+      name: string,
+      type: GroupType,
+      provider?: ExternalGroupProviderType,
+    }[] = [];
+
+    const populatedGrantedGroups = await this.getPopulatedGrantedGroups(page.grantedGroups);
+
+    populatedGrantedGroups.forEach((group) => {
+      const userRelatedGrantedGroup = userRelatedGroupsData.find((userRelatedGroup) => {
+        return userRelatedGroup.id === group.item._id.toString();
+      });
+      if (userRelatedGrantedGroup != null) {
+        userRelatedGrantedGroup.status = UserGroupPageGrantStatus.isGranted;
+      }
+      else {
+        const provider = group.type === GroupType.externalUserGroup ? group.item.provider : undefined;
+        nonUserRelatedGrantedGroups.push({
+          id: group.item._id.toString(), name: group.item.name, type: group.type, provider,
+        });
+      }
+    });
+
+    return { userRelatedGroups: userRelatedGroupsData, nonUserRelatedGrantedGroups };
+  }
+
   /*
    * get all groups that user is related to
    */
@@ -671,6 +713,20 @@ class PageGrantService implements IPageGrantService {
     ];
   }
 
+  async getPopulatedGrantedGroups(grantedGroups: IGrantedGroup[]): Promise<PopulatedGrantedGroup[]> {
+    const { grantedUserGroups, grantedExternalUserGroups } = divideByType(grantedGroups);
+    const userGroupDocuments = await UserGroup.find({ _id: { $in: grantedUserGroups } });
+    const externalUserGroupDocuments = await ExternalUserGroup.find({ _id: { $in: grantedExternalUserGroups } });
+    return [
+      ...(userGroupDocuments.map((group) => {
+        return { type: GroupType.userGroup, item: group };
+      })),
+      ...(externalUserGroupDocuments.map((group) => {
+        return { type: GroupType.externalUserGroup, item: group };
+      })),
+    ];
+  }
+
   /*
    * get all groups of Page that user is related to
    */