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;
@@ -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({