feat: org auth for app & dataset (#3498)
* feat: auth org resource permission * feat: org auth support for app & dataset
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
Reference in New Issue
Block a user