feat: org auth for app & dataset (#3498)

* feat: auth org resource permission

* feat: org auth support for app & dataset
This commit is contained in:
a.e.
2024-12-30 21:09:39 +08:00
committed by archer
parent efecfd44c3
commit fd9600c6f8
5 changed files with 108 additions and 53 deletions

View File

@@ -22,6 +22,7 @@ import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/member
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type'; import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserModelSchema } from '@fastgpt/global/support/user/type';
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type'; import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
import { getOrgsWithParentByTmbId } from './org/controllers';
/** get resource permission for a team member /** get resource permission for a team member
* If there is no permission for the team member, it will return undefined * If there is no permission for the team member, it will return undefined
@@ -40,15 +41,15 @@ export const getResourcePermission = async ({
teamId: string; teamId: string;
tmbId: string; tmbId: string;
} & ( } & (
| { | {
resourceType: 'team'; resourceType: 'team';
resourceId?: undefined; resourceId?: undefined;
} }
| { | {
resourceType: Omit<PerResourceTypeEnum, 'team'>; resourceType: Omit<PerResourceTypeEnum, 'team'>;
resourceId: string; resourceId: string;
} }
)): Promise<PermissionValueType | undefined> => { )): Promise<PermissionValueType | undefined> => {
// Personal permission has the highest priority // Personal permission has the highest priority
const tmbPer = ( const tmbPer = (
await MongoResourcePermission.findOne( await MongoResourcePermission.findOne(
@@ -68,20 +69,45 @@ export const getResourcePermission = async ({
} }
// If there is no personal permission, get the group permission // If there is no personal permission, get the group permission
const groupIdList = (await getGroupsByTmbId({ tmbId, teamId })).map((item) => item._id); const groupPer = await (async () => {
const groupIdList = (await getGroupsByTmbId({ tmbId, teamId })).map((item) => item._id);
if (groupIdList.length === 0) { if (groupIdList.length === 0) {
return undefined; return undefined;
}
// get the maximum permission of the group
const pers = (
await MongoResourcePermission.find(
{
teamId,
resourceType,
groupId: {
$in: groupIdList
},
resourceId
},
'permission'
).lean()
).map((item) => item.permission);
return getGroupPer(pers);
})();
const orgIds = await getOrgsWithParentByTmbId({ tmbId, teamId }).then((item) => Array.from(item));
if (orgIds.length === 0) {
return groupPer;
} }
// get the maximum permission of the group // get the maximum permission of the org
const pers = ( const orgPers = (
await MongoResourcePermission.find( await MongoResourcePermission.find(
{ {
teamId, teamId,
resourceType, resourceType,
groupId: { orgId: {
$in: groupIdList $in: Array.from(orgIds)
}, },
resourceId resourceId
}, },
@@ -89,9 +115,15 @@ export const getResourcePermission = async ({
).lean() ).lean()
).map((item) => item.permission); ).map((item) => item.permission);
const groupPer = getGroupPer(pers); const orgPer = getGroupPer(orgPers);
return groupPer; if (groupPer === undefined) {
return orgPer;
} else if (orgPer === undefined) {
return groupPer;
}
return new Permission().addPer(groupPer, orgPer).value;
}; };
/* 仅取 members 不取 groups */ /* 仅取 members 不取 groups */
@@ -104,15 +136,15 @@ export async function getResourceAllClbs({
teamId: string; teamId: string;
session?: ClientSession; session?: ClientSession;
} & ( } & (
| { | {
resourceType: 'team'; resourceType: 'team';
resourceId?: undefined; resourceId?: undefined;
} }
| { | {
resourceType: Omit<PerResourceTypeEnum, 'team'>; resourceType: Omit<PerResourceTypeEnum, 'team'>;
resourceId?: string | null; resourceId?: string | null;
} }
)): Promise<ResourcePermissionType[]> { )): Promise<ResourcePermissionType[]> {
return MongoResourcePermission.find( return MongoResourcePermission.find(
{ {
resourceType: resourceType, resourceType: resourceType,

View File

@@ -36,6 +36,19 @@ import { MongoOrgMemberModel } from './orgMemberSchema';
export const getOrgsByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) => export const getOrgsByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) =>
MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean(); MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
export const getOrgsWithParentByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) =>
MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean().then((orgs) => {
const orgIds = new Set<string>();
for (const org of orgs) {
const orgId = String(org.orgId);
const parentIds = orgId.split('/').filter((id) => id);
for (const parentId of parentIds) {
orgIds.add(parentId);
}
}
return orgIds;
});
export const getChildrenByOrg = async ({ export const getChildrenByOrg = async ({
org, org,
teamId, teamId,

View File

@@ -75,7 +75,7 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
if (mode !== 'all') return []; if (mode !== 'all') return [];
return orgs.filter((item) => { return orgs.filter((item) => {
if (item.path === '') return false; // exclude root org if (item.path === '') return false; // exclude root org
if (!permission.isOwner && myOrgs.find((i) => String(i._id) !== String(item._id))) if (!permission.isOwner && !myOrgs.find((i) => String(i._id) === String(item._id)))
return false; return false;
if (!searchText) return true; if (!searchText) return true;
return item.name.includes(searchText); return item.name.includes(searchText);

View File

@@ -17,6 +17,7 @@ import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupPer } from '@fastgpt/service/support/permission/controller'; import { getGroupPer } from '@fastgpt/service/support/permission/controller';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgsWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
export type ListAppBody = { export type ListAppBody = {
parentId?: ParentIdType; parentId?: ParentIdType;
@@ -25,7 +26,7 @@ export type ListAppBody = {
searchKey?: string; searchKey?: string;
}; };
/* /*
获取 APP 列表权限 获取 APP 列表权限
1. 校验 folder 权限和获取 team 权限owner 单独处理) 1. 校验 folder 权限和获取 team 权限owner 单独处理)
2. 获取 team 下所有 app 权限。获取我的所有组。并计算出我所有的app权限。 2. 获取 team 下所有 app 权限。获取我的所有组。并计算出我所有的app权限。
@@ -48,19 +49,19 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
}), }),
...(parentId ...(parentId
? [ ? [
authApp({ authApp({
req, req,
authToken: true, authToken: true,
authApiKey: true, authApiKey: true,
appId: parentId, appId: parentId,
per: ReadPermissionVal per: ReadPermissionVal
}) })
] ]
: []) : [])
]); ]);
// Get team all app permissions // Get team all app permissions
const [perList, myGroupMap] = await Promise.all([ const [perList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({ MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.app, resourceType: PerResourceTypeEnum.app,
teamId, teamId,
@@ -77,11 +78,15 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
map.set(String(item._id), 1); map.set(String(item._id), 1);
}); });
return map; return map;
}),
getOrgsWithParentByTmbId({
teamId,
tmbId
}) })
]); ]);
// Get my permissions // Get my permissions
const myPerList = perList.filter( const myPerList = perList.filter(
(item) => String(item.tmbId) === String(tmbId) || myGroupMap.has(String(item.groupId)) (item) => String(item.tmbId) === String(tmbId) || myGroupMap.has(String(item.groupId)) || myOrgSet.has(String(item.orgId))
); );
const findAppsQuery = (() => { const findAppsQuery = (() => {
@@ -99,17 +104,17 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
? {} ? {}
: parentId : parentId
? { ? {
$or: [idList, parseParentIdInMongo(parentId)] $or: [idList, parseParentIdInMongo(parentId)]
} }
: { $or: [idList, { parentId: null }] }; : { $or: [idList, { parentId: null }] };
const searchMatch = searchKey const searchMatch = searchKey
? { ? {
$or: [ $or: [
{ name: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } }, { name: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } },
{ intro: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } } { intro: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } }
] ]
} }
: {}; : {};
if (searchKey) { if (searchKey) {
@@ -153,7 +158,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
)?.permission; )?.permission;
const groupPer = getGroupPer( const groupPer = getGroupPer(
myPerList myPerList
.filter((item) => String(item.resourceId) === appId && !!item.groupId) .filter((item) => String(item.resourceId) === appId && (!!item.groupId || !!item.orgId))
.map((item) => item.permission) .map((item) => item.permission)
); );

View File

@@ -18,6 +18,7 @@ import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getGroupPer } from '@fastgpt/service/support/permission/controller'; import { getGroupPer } from '@fastgpt/service/support/permission/controller';
import { getOrgsWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
export type GetDatasetListBody = { export type GetDatasetListBody = {
parentId: ParentIdType; parentId: ParentIdType;
@@ -38,19 +39,19 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
}), }),
...(parentId ...(parentId
? [ ? [
authDataset({ authDataset({
req, req,
authToken: true, authToken: true,
authApiKey: true, authApiKey: true,
per: ReadPermissionVal, per: ReadPermissionVal,
datasetId: parentId datasetId: parentId
}) })
] ]
: []) : [])
]); ]);
// Get team all app permissions // Get team all app permissions
const [perList, myGroupMap] = await Promise.all([ const [perList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({ MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.dataset, resourceType: PerResourceTypeEnum.dataset,
teamId, teamId,
@@ -67,10 +68,14 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
map.set(String(item._id), 1); map.set(String(item._id), 1);
}); });
return map; return map;
}),
getOrgsWithParentByTmbId({
teamId,
tmbId
}) })
]); ]);
const myPerList = perList.filter( const myPerList = perList.filter(
(item) => String(item.tmbId) === String(tmbId) || myGroupMap.has(String(item.groupId)) (item) => String(item.tmbId) === String(tmbId) || myGroupMap.has(String(item.groupId)) || myOrgSet.has(String(item.orgId))
); );
const findDatasetQuery = (() => { const findDatasetQuery = (() => {
@@ -80,17 +85,17 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
? {} ? {}
: parentId : parentId
? { ? {
$or: [idList, parseParentIdInMongo(parentId)] $or: [idList, parseParentIdInMongo(parentId)]
} }
: { $or: [idList, { parentId: null }] }; : { $or: [idList, { parentId: null }] };
const searchMatch = searchKey const searchMatch = searchKey
? { ? {
$or: [ $or: [
{ name: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } }, { name: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } },
{ intro: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } } { intro: { $regex: new RegExp(`${replaceRegChars(searchKey)}`, 'i') } }
] ]
} }
: {}; : {};
if (searchKey) { if (searchKey) {
@@ -124,7 +129,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
)?.permission; )?.permission;
const groupPer = getGroupPer( const groupPer = getGroupPer(
myPerList myPerList
.filter((item) => String(item.resourceId) === datasetId && !!item.groupId) .filter((item) => String(item.resourceId) === datasetId && (!!item.groupId || !!item.orgId))
.map((item) => item.permission) .map((item) => item.permission)
); );
return new DatasetPermission({ return new DatasetPermission({