|
@@ -12,6 +12,7 @@ import mongoose from 'mongoose';
|
|
|
import type { ExternalGroupProviderType } from '~/features/external-user-group/interfaces/external-user-group';
|
|
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 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 ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
|
|
|
|
|
+import type { UserRelatedGroupsData } from '~/interfaces/page';
|
|
|
import { UserGroupPageGrantStatus, type GroupGrantData } from '~/interfaces/page';
|
|
import { UserGroupPageGrantStatus, type GroupGrantData } from '~/interfaces/page';
|
|
|
import type { IRecordApplicableGrant, PopulatedGrantedGroup } from '~/interfaces/page-grant';
|
|
import type { IRecordApplicableGrant, PopulatedGrantedGroup } from '~/interfaces/page-grant';
|
|
|
import type { PageDocument, PageModel } from '~/server/models/page';
|
|
import type { PageDocument, PageModel } from '~/server/models/page';
|
|
@@ -117,7 +118,7 @@ class PageGrantService implements IPageGrantService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private validateComparableTarget(comparable: ComparableTarget) {
|
|
private validateComparableTarget(comparable: ComparableTarget) {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
|
|
|
|
+ const Page = mongoose.model<IPage, PageModel>('Page');
|
|
|
|
|
|
|
|
const { grant, grantedUserIds, grantedGroupIds } = comparable;
|
|
const { grant, grantedUserIds, grantedGroupIds } = comparable;
|
|
|
|
|
|
|
@@ -139,7 +140,7 @@ class PageGrantService implements IPageGrantService {
|
|
|
*/
|
|
*/
|
|
|
this.validateComparableTarget(target);
|
|
this.validateComparableTarget(target);
|
|
|
|
|
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
|
|
|
|
+ const Page = mongoose.model<IPage, PageModel>('Page');
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
* ancestor side
|
|
* ancestor side
|
|
@@ -289,53 +290,45 @@ class PageGrantService implements IPageGrantService {
|
|
|
* Prepare ComparableTarget
|
|
* Prepare ComparableTarget
|
|
|
* @returns Promise<ComparableAncestor>
|
|
* @returns Promise<ComparableAncestor>
|
|
|
*/
|
|
*/
|
|
|
- private async generateComparableTarget(
|
|
|
|
|
- grant: PageGrant | undefined, grantedUserIds: ObjectIdLike[] | undefined, grantedGroupIds: IGrantedGroup[] | undefined, includeApplicable: boolean,
|
|
|
|
|
|
|
+ private async generateComparableTargetWithApplicableData(
|
|
|
|
|
+ grant: PageGrant | undefined, grantedUserIds: ObjectIdLike[] | undefined, grantedGroupIds: IGrantedGroup[] | undefined,
|
|
|
): Promise<ComparableTarget> {
|
|
): Promise<ComparableTarget> {
|
|
|
- if (includeApplicable) {
|
|
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
|
|
-
|
|
|
|
|
- let applicableUserIds: ObjectIdLike[] | undefined;
|
|
|
|
|
- let applicableGroupIds: ObjectIdLike[] | undefined;
|
|
|
|
|
|
|
+ const Page = mongoose.model<IPage, PageModel>('Page');
|
|
|
|
|
|
|
|
- if (grant === Page.GRANT_USER_GROUP) {
|
|
|
|
|
- if (grantedGroupIds == null || grantedGroupIds.length === 0) {
|
|
|
|
|
- throw Error('Target user group is not given');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ let applicableUserIds: ObjectIdLike[] | undefined;
|
|
|
|
|
+ let applicableGroupIds: ObjectIdLike[] | undefined;
|
|
|
|
|
|
|
|
- const { grantedUserGroups: grantedUserGroupIds, grantedExternalUserGroups: grantedExternalUserGroupIds } = divideByType(grantedGroupIds);
|
|
|
|
|
- const targetUserGroups = await UserGroup.find({ _id: { $in: grantedUserGroupIds } });
|
|
|
|
|
- const targetExternalUserGroups = await ExternalUserGroup.find({ _id: { $in: grantedExternalUserGroupIds } });
|
|
|
|
|
- if (targetUserGroups.length === 0 && targetExternalUserGroups.length === 0) {
|
|
|
|
|
- throw Error('Target user group does not exist');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (grant === Page.GRANT_USER_GROUP) {
|
|
|
|
|
+ if (grantedGroupIds == null || grantedGroupIds.length === 0) {
|
|
|
|
|
+ throw Error('Target user group is not given');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const userGroupRelations = await UserGroupRelation.find({ relatedGroup: { $in: targetUserGroups.map(g => g._id) } });
|
|
|
|
|
- const externalUserGroupRelations = await ExternalUserGroupRelation.find({ relatedGroup: { $in: targetExternalUserGroups.map(g => g._id) } });
|
|
|
|
|
- applicableUserIds = Array.from(new Set([...userGroupRelations, ...externalUserGroupRelations].map(u => u.relatedUser as ObjectIdLike)));
|
|
|
|
|
-
|
|
|
|
|
- const applicableUserGroups = (await Promise.all(targetUserGroups.map((group) => {
|
|
|
|
|
- return UserGroup.findGroupsWithDescendantsById(group._id);
|
|
|
|
|
- }))).flat();
|
|
|
|
|
- const applicableExternalUserGroups = (await Promise.all(targetExternalUserGroups.map((group) => {
|
|
|
|
|
- return ExternalUserGroup.findGroupsWithDescendantsById(group._id);
|
|
|
|
|
- }))).flat();
|
|
|
|
|
- applicableGroupIds = [...applicableUserGroups, ...applicableExternalUserGroups].map(g => g._id);
|
|
|
|
|
|
|
+ const { grantedUserGroups: grantedUserGroupIds, grantedExternalUserGroups: grantedExternalUserGroupIds } = divideByType(grantedGroupIds);
|
|
|
|
|
+ const targetUserGroups = await UserGroup.find({ _id: { $in: grantedUserGroupIds } });
|
|
|
|
|
+ const targetExternalUserGroups = await ExternalUserGroup.find({ _id: { $in: grantedExternalUserGroupIds } });
|
|
|
|
|
+ if (targetUserGroups.length === 0 && targetExternalUserGroups.length === 0) {
|
|
|
|
|
+ throw Error('Target user group does not exist');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
- grant,
|
|
|
|
|
- grantedUserIds,
|
|
|
|
|
- grantedGroupIds,
|
|
|
|
|
- applicableUserIds,
|
|
|
|
|
- applicableGroupIds,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const userGroupRelations = await UserGroupRelation.find({ relatedGroup: { $in: targetUserGroups.map(g => g._id) } });
|
|
|
|
|
+ const externalUserGroupRelations = await ExternalUserGroupRelation.find({ relatedGroup: { $in: targetExternalUserGroups.map(g => g._id) } });
|
|
|
|
|
+ applicableUserIds = Array.from(new Set([...userGroupRelations, ...externalUserGroupRelations].map(u => u.relatedUser as ObjectIdLike)));
|
|
|
|
|
+
|
|
|
|
|
+ const applicableUserGroups = (await Promise.all(targetUserGroups.map((group) => {
|
|
|
|
|
+ return UserGroup.findGroupsWithDescendantsById(group._id);
|
|
|
|
|
+ }))).flat();
|
|
|
|
|
+ const applicableExternalUserGroups = (await Promise.all(targetExternalUserGroups.map((group) => {
|
|
|
|
|
+ return ExternalUserGroup.findGroupsWithDescendantsById(group._id);
|
|
|
|
|
+ }))).flat();
|
|
|
|
|
+ applicableGroupIds = [...applicableUserGroups, ...applicableExternalUserGroups].map(g => g._id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
grant,
|
|
grant,
|
|
|
grantedUserIds,
|
|
grantedUserIds,
|
|
|
grantedGroupIds,
|
|
grantedGroupIds,
|
|
|
|
|
+ applicableUserIds,
|
|
|
|
|
+ applicableGroupIds,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -345,7 +338,7 @@ class PageGrantService implements IPageGrantService {
|
|
|
* @returns Promise<ComparableAncestor>
|
|
* @returns Promise<ComparableAncestor>
|
|
|
*/
|
|
*/
|
|
|
private async generateComparableAncestor(targetPath: string, includeNotMigratedPages: boolean): Promise<ComparableAncestor> {
|
|
private async generateComparableAncestor(targetPath: string, includeNotMigratedPages: boolean): Promise<ComparableAncestor> {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
|
|
|
|
+ const Page = mongoose.model<IPage, PageModel>('Page');
|
|
|
const { PageQueryBuilder } = Page;
|
|
const { PageQueryBuilder } = Page;
|
|
|
|
|
|
|
|
let applicableUserIds: ObjectIdLike[] | undefined;
|
|
let applicableUserIds: ObjectIdLike[] | undefined;
|
|
@@ -400,7 +393,7 @@ class PageGrantService implements IPageGrantService {
|
|
|
* @returns ComparableDescendants
|
|
* @returns ComparableDescendants
|
|
|
*/
|
|
*/
|
|
|
private async generateComparableDescendants(targetPath: string, user, includeNotMigratedPages = false): Promise<ComparableDescendants> {
|
|
private async generateComparableDescendants(targetPath: string, user, includeNotMigratedPages = false): Promise<ComparableDescendants> {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
|
|
|
|
+ const Page = mongoose.model<IPage, PageModel>('Page');
|
|
|
|
|
|
|
|
// Build conditions
|
|
// Build conditions
|
|
|
const $match: {$or: any} = {
|
|
const $match: {$or: any} = {
|
|
@@ -520,11 +513,11 @@ class PageGrantService implements IPageGrantService {
|
|
|
const comparableAncestor = await this.generateComparableAncestor(targetPath, includeNotMigratedPages);
|
|
const comparableAncestor = await this.generateComparableAncestor(targetPath, includeNotMigratedPages);
|
|
|
|
|
|
|
|
if (!shouldCheckDescendants) { // checking the parent is enough
|
|
if (!shouldCheckDescendants) { // checking the parent is enough
|
|
|
- const comparableTarget = await this.generateComparableTarget(grant, grantedUserIds, grantedGroupIds, false);
|
|
|
|
|
|
|
+ const comparableTarget: ComparableTarget = { grant, grantedUserIds, grantedGroupIds };
|
|
|
return this.validateGrant(comparableTarget, comparableAncestor);
|
|
return this.validateGrant(comparableTarget, comparableAncestor);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const comparableTarget = await this.generateComparableTarget(grant, grantedUserIds, grantedGroupIds, true);
|
|
|
|
|
|
|
+ const comparableTarget = await this.generateComparableTargetWithApplicableData(grant, grantedUserIds, grantedGroupIds);
|
|
|
const comparableDescendants = await this.generateComparableDescendants(targetPath, user, includeNotMigratedPages);
|
|
const comparableDescendants = await this.generateComparableDescendants(targetPath, user, includeNotMigratedPages);
|
|
|
|
|
|
|
|
return this.validateGrant(comparableTarget, comparableAncestor, comparableDescendants);
|
|
return this.validateGrant(comparableTarget, comparableAncestor, comparableDescendants);
|
|
@@ -660,12 +653,20 @@ class PageGrantService implements IPageGrantService {
|
|
|
return data;
|
|
return data;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Get the group grant data of page.
|
|
|
|
|
+ * To calculate if a group can be granted to page, the same logic as isGrantNormalized will be executed, except only the ancestor info will be used.
|
|
|
|
|
+ */
|
|
|
async getPageGroupGrantData(page: PageDocument, user): Promise<GroupGrantData> {
|
|
async getPageGroupGrantData(page: PageDocument, user): Promise<GroupGrantData> {
|
|
|
|
|
+ if (isTopPage(page.path)) {
|
|
|
|
|
+ return { userRelatedGroups: [], nonUserRelatedGrantedGroups: [] };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const userRelatedGroups = await this.getUserRelatedGroups(user);
|
|
const userRelatedGroups = await this.getUserRelatedGroups(user);
|
|
|
- const userRelatedGroupsData = userRelatedGroups.map((group) => {
|
|
|
|
|
|
|
+ let userRelatedGroupsData: UserRelatedGroupsData[] = userRelatedGroups.map((group) => {
|
|
|
const provider = group.type === GroupType.externalUserGroup ? group.item.provider : undefined;
|
|
const provider = group.type === GroupType.externalUserGroup ? group.item.provider : undefined;
|
|
|
return {
|
|
return {
|
|
|
- // TODO: change un-grantable groups to UserGroupPageGrantStatus.cannotGrant (https://redmine.weseek.co.jp/issues/142310)
|
|
|
|
|
|
|
+ // default status as notGranted
|
|
|
id: group.item._id.toString(), name: group.item.name, type: group.type, provider, status: UserGroupPageGrantStatus.notGranted,
|
|
id: group.item._id.toString(), name: group.item.name, type: group.type, provider, status: UserGroupPageGrantStatus.notGranted,
|
|
|
};
|
|
};
|
|
|
});
|
|
});
|
|
@@ -679,6 +680,8 @@ class PageGrantService implements IPageGrantService {
|
|
|
|
|
|
|
|
const populatedGrantedGroups = await this.getPopulatedGrantedGroups(page.grantedGroups);
|
|
const populatedGrantedGroups = await this.getPopulatedGrantedGroups(page.grantedGroups);
|
|
|
|
|
|
|
|
|
|
+ // Set the status of user-related granted groups as isGranted
|
|
|
|
|
+ // Append non-user-related granted groups to nonUserRelatedGrantedGroups
|
|
|
populatedGrantedGroups.forEach((group) => {
|
|
populatedGrantedGroups.forEach((group) => {
|
|
|
const userRelatedGrantedGroup = userRelatedGroupsData.find((userRelatedGroup) => {
|
|
const userRelatedGrantedGroup = userRelatedGroupsData.find((userRelatedGroup) => {
|
|
|
return userRelatedGroup.id === group.item._id.toString();
|
|
return userRelatedGroup.id === group.item._id.toString();
|
|
@@ -694,6 +697,30 @@ class PageGrantService implements IPageGrantService {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // Check if group can be granted to page for non-granted groups
|
|
|
|
|
+ const grantedUserIds = page.grantedUsers?.map(user => getIdForRef(user)) ?? [];
|
|
|
|
|
+ const comparableAncestor = await this.generateComparableAncestor(page.path, false);
|
|
|
|
|
+ userRelatedGroupsData = userRelatedGroupsData.map((groupData) => {
|
|
|
|
|
+ if (groupData.status === UserGroupPageGrantStatus.isGranted) {
|
|
|
|
|
+ return groupData;
|
|
|
|
|
+ }
|
|
|
|
|
+ const groupsToGrant = [...(page.grantedGroups ?? []), { item: groupData.id, type: groupData.type }];
|
|
|
|
|
+ const comparableTarget: ComparableTarget = {
|
|
|
|
|
+ grant: PageGrant.GRANT_USER_GROUP,
|
|
|
|
|
+ grantedUserIds,
|
|
|
|
|
+ grantedGroupIds: groupsToGrant,
|
|
|
|
|
+ };
|
|
|
|
|
+ const status = this.validateGrant(comparableTarget, comparableAncestor) ? UserGroupPageGrantStatus.notGranted : UserGroupPageGrantStatus.cannotGrant;
|
|
|
|
|
+ return { ...groupData, status };
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const statusPriority = {
|
|
|
|
|
+ [UserGroupPageGrantStatus.notGranted]: 0,
|
|
|
|
|
+ [UserGroupPageGrantStatus.isGranted]: 1,
|
|
|
|
|
+ [UserGroupPageGrantStatus.cannotGrant]: 2,
|
|
|
|
|
+ };
|
|
|
|
|
+ userRelatedGroupsData.sort((a, b) => statusPriority[a.status] - statusPriority[b.status]);
|
|
|
|
|
+
|
|
|
return { userRelatedGroups: userRelatedGroupsData, nonUserRelatedGrantedGroups };
|
|
return { userRelatedGroups: userRelatedGroupsData, nonUserRelatedGrantedGroups };
|
|
|
}
|
|
}
|
|
|
|
|
|