mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-27 16:33:49 +00:00
Group role (#2993)
* feat: app/dataset support group (#2898) * pref: member-group (#2862) * feat: group list ordered by updateTime * fix: transfer ownership of group when deleting member * fix: i18n fix * feat: can not set member as admin/owner when user is not active * fix: GroupInfoModal hover input do not change color * fix(fe): searchinput do not scroll * feat: app collaborator with group, remove default permission * feat: dataset collaborator with group, remove default permission * chore(test): pref mock * chore: remove useless code * chore: adjust * fix: add self as collaborator when creating folder * fix(fe): folder manage menu do not show when user has write permission only * fix: dataset folder create * feat: Add code comment * Pref: app move (#2952) * perf: app schema * doc --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
This commit is contained in:
@@ -5,8 +5,6 @@ import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
import { getPermissionSchema } from '@fastgpt/global/support/permission/utils';
|
||||
|
||||
export const AppCollectionName = 'apps';
|
||||
|
||||
@@ -111,8 +109,13 @@ const AppSchema = new Schema({
|
||||
inited: {
|
||||
type: Boolean
|
||||
},
|
||||
inheritPermission: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
...getPermissionSchema(AppDefaultPermissionVal)
|
||||
// abandoned
|
||||
defaultPermission: Number
|
||||
});
|
||||
|
||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||
|
@@ -9,8 +9,6 @@ import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||
import { getPermissionSchema } from '@fastgpt/global/support/permission/utils';
|
||||
import type { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
||||
|
||||
export const DatasetCollectionName = 'datasets';
|
||||
@@ -88,7 +86,13 @@ const DatasetSchema = new Schema({
|
||||
externalReadUrl: {
|
||||
type: String
|
||||
},
|
||||
...getPermissionSchema(DatasetDefaultPermissionVal)
|
||||
inheritPermission: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// abandoned
|
||||
defaultPermission: Number
|
||||
});
|
||||
|
||||
try {
|
||||
|
@@ -13,6 +13,7 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { splitCombinePluginId } from '../../../core/app/plugin/controller';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { AuthModeType, AuthResponseType } from '../type';
|
||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
|
||||
export const authPluginByTmbId = async ({
|
||||
tmbId,
|
||||
@@ -60,7 +61,6 @@ export const authAppByTmbId = async ({
|
||||
if (isRoot) {
|
||||
return {
|
||||
...app,
|
||||
defaultPermission: app.defaultPermission,
|
||||
permission: new AppPermission({ isOwner: true })
|
||||
};
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export const authAppByTmbId = async ({
|
||||
|
||||
const isOwner = tmbPer.isOwner || String(app.tmbId) === String(tmbId);
|
||||
|
||||
const { Per, defaultPermission } = await (async () => {
|
||||
const { Per } = await (async () => {
|
||||
if (
|
||||
AppFolderTypeList.includes(app.type) ||
|
||||
app.inheritPermission === false ||
|
||||
@@ -86,10 +86,9 @@ export const authAppByTmbId = async ({
|
||||
resourceId: appId,
|
||||
resourceType: PerResourceTypeEnum.app
|
||||
});
|
||||
const Per = new AppPermission({ per: rp ?? app.defaultPermission, isOwner });
|
||||
const Per = new AppPermission({ per: rp ?? AppDefaultPermissionVal, isOwner });
|
||||
return {
|
||||
Per,
|
||||
defaultPermission: app.defaultPermission
|
||||
Per
|
||||
};
|
||||
} else {
|
||||
// is not folder and inheritPermission is true and is not root folder.
|
||||
@@ -104,8 +103,7 @@ export const authAppByTmbId = async ({
|
||||
isOwner
|
||||
});
|
||||
return {
|
||||
Per,
|
||||
defaultPermission: parent.defaultPermission
|
||||
Per
|
||||
};
|
||||
}
|
||||
})();
|
||||
@@ -116,7 +114,6 @@ export const authAppByTmbId = async ({
|
||||
|
||||
return {
|
||||
...app,
|
||||
defaultPermission,
|
||||
permission: Per
|
||||
};
|
||||
})();
|
||||
|
@@ -10,12 +10,17 @@ import { MongoResourcePermission } from './schema';
|
||||
import { ClientSession } from 'mongoose';
|
||||
import {
|
||||
PermissionValueType,
|
||||
ResourcePermissionType
|
||||
ResourcePermissionType,
|
||||
ResourcePerWithGroup,
|
||||
ResourcePerWithTmbWithUser
|
||||
} from '@fastgpt/global/support/permission/type';
|
||||
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
|
||||
import { addMinutes } from 'date-fns';
|
||||
import { getGroupsByTmbId } from './memberGroup/controllers';
|
||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
|
||||
/** get resource permission for a team member
|
||||
* If there is no permission for the team member, it will return undefined
|
||||
@@ -123,20 +128,94 @@ export async function getResourceAllClbs({
|
||||
).lean();
|
||||
}
|
||||
|
||||
export async function getResourceClbsAndGroups({
|
||||
resourceId,
|
||||
resourceType,
|
||||
teamId,
|
||||
session
|
||||
}: {
|
||||
resourceId: ParentIdType;
|
||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||
teamId: string;
|
||||
session: ClientSession;
|
||||
}) {
|
||||
return MongoResourcePermission.find(
|
||||
{
|
||||
resourceId,
|
||||
resourceType,
|
||||
teamId
|
||||
},
|
||||
undefined,
|
||||
{ session }
|
||||
).lean();
|
||||
}
|
||||
|
||||
export const getClbsAndGroupsWithInfo = async ({
|
||||
resourceId,
|
||||
resourceType,
|
||||
teamId
|
||||
}: {
|
||||
resourceId: ParentIdType;
|
||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||
teamId: string;
|
||||
}) =>
|
||||
Promise.all([
|
||||
(await MongoResourcePermission.find({
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
tmbId: {
|
||||
$exists: true
|
||||
}
|
||||
}).populate({
|
||||
path: 'tmbId',
|
||||
select: 'name userId',
|
||||
populate: {
|
||||
path: 'userId',
|
||||
select: 'avatar'
|
||||
}
|
||||
})) as ResourcePerWithTmbWithUser[],
|
||||
(await MongoResourcePermission.find({
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
groupId: {
|
||||
$exists: true
|
||||
}
|
||||
}).populate({
|
||||
path: 'groupId',
|
||||
select: 'name avatar'
|
||||
})) as ResourcePerWithGroup[]
|
||||
]);
|
||||
|
||||
export const delResourcePermissionById = (id: string) => {
|
||||
return MongoResourcePermission.findByIdAndRemove(id);
|
||||
};
|
||||
export const delResourcePermission = ({
|
||||
session,
|
||||
tmbId,
|
||||
groupId,
|
||||
...props
|
||||
}: {
|
||||
resourceType: PerResourceTypeEnum;
|
||||
teamId: string;
|
||||
resourceId: string;
|
||||
tmbId: string;
|
||||
session?: ClientSession;
|
||||
tmbId?: string;
|
||||
groupId?: string;
|
||||
}) => {
|
||||
return MongoResourcePermission.deleteOne(props, { session });
|
||||
// tmbId or groupId only one and not both
|
||||
if (!!tmbId === !!groupId) {
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
}
|
||||
return MongoResourcePermission.deleteOne(
|
||||
{
|
||||
...(tmbId ? { tmbId } : {}),
|
||||
...(groupId ? { groupId } : {}),
|
||||
...props
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
};
|
||||
|
||||
/* 下面代码等迁移 */
|
||||
|
@@ -20,6 +20,7 @@ import { MongoDatasetData } from '../../../core/dataset/data/schema';
|
||||
import { AuthModeType, AuthResponseType } from '../type';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||
|
||||
export const authDatasetByTmbId = async ({
|
||||
tmbId,
|
||||
@@ -62,7 +63,7 @@ export const authDatasetByTmbId = async ({
|
||||
const isOwner = tmbPer.isOwner || String(dataset.tmbId) === String(tmbId);
|
||||
|
||||
// get dataset permission or inherit permission from parent folder.
|
||||
const { Per, defaultPermission } = await (async () => {
|
||||
const { Per } = await (async () => {
|
||||
if (
|
||||
dataset.type === DatasetTypeEnum.folder ||
|
||||
dataset.inheritPermission === false ||
|
||||
@@ -78,12 +79,11 @@ export const authDatasetByTmbId = async ({
|
||||
resourceType: PerResourceTypeEnum.dataset
|
||||
});
|
||||
const Per = new DatasetPermission({
|
||||
per: rp ?? dataset.defaultPermission,
|
||||
per: rp ?? DatasetDefaultPermissionVal,
|
||||
isOwner
|
||||
});
|
||||
return {
|
||||
Per,
|
||||
defaultPermission: dataset.defaultPermission
|
||||
Per
|
||||
};
|
||||
} else {
|
||||
// is not folder and inheritPermission is true and is not root folder.
|
||||
@@ -100,8 +100,7 @@ export const authDatasetByTmbId = async ({
|
||||
});
|
||||
|
||||
return {
|
||||
Per,
|
||||
defaultPermission: parent.defaultPermission
|
||||
Per
|
||||
};
|
||||
}
|
||||
})();
|
||||
@@ -112,7 +111,6 @@ export const authDatasetByTmbId = async ({
|
||||
|
||||
return {
|
||||
...dataset,
|
||||
defaultPermission,
|
||||
permission: Per
|
||||
};
|
||||
})();
|
||||
@@ -179,14 +177,15 @@ export async function authDatasetCollection({
|
||||
tmbId,
|
||||
datasetId: collection.datasetId._id,
|
||||
per,
|
||||
isRoot: isRootFromHeader || isRoot
|
||||
isRoot: isRootFromHeader
|
||||
});
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
collection,
|
||||
permission: dataset.permission
|
||||
permission: dataset.permission,
|
||||
isRoot: isRootFromHeader
|
||||
};
|
||||
}
|
||||
|
||||
@@ -231,7 +230,8 @@ export async function authDatasetFile({
|
||||
teamId,
|
||||
tmbId,
|
||||
file,
|
||||
permission
|
||||
permission,
|
||||
isRoot
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import { ClientSession, Model } from 'mongoose';
|
||||
import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { getResourceAllClbs } from './controller';
|
||||
import { getResourceClbsAndGroups } from './controller';
|
||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
|
||||
@@ -28,7 +28,6 @@ export async function syncChildrenPermission({
|
||||
resourceModel,
|
||||
session,
|
||||
|
||||
defaultPermission,
|
||||
collaborators
|
||||
}: {
|
||||
resource: SyncChildrenPermissionResourceType;
|
||||
@@ -42,7 +41,6 @@ export async function syncChildrenPermission({
|
||||
// should be provided when inheritPermission is true
|
||||
session: ClientSession;
|
||||
|
||||
defaultPermission?: PermissionValueType;
|
||||
collaborators?: UpdateCollaboratorItem[];
|
||||
}) {
|
||||
// only folder has permission
|
||||
@@ -76,19 +74,6 @@ export async function syncChildrenPermission({
|
||||
}
|
||||
if (!children.length) return;
|
||||
|
||||
// Sync default permission
|
||||
if (defaultPermission !== undefined) {
|
||||
await resourceModel.updateMany(
|
||||
{
|
||||
_id: { $in: children }
|
||||
},
|
||||
{
|
||||
defaultPermission
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
}
|
||||
|
||||
// sync the resource permission
|
||||
if (collaborators) {
|
||||
// Update the collaborators of all children
|
||||
@@ -124,28 +109,20 @@ export async function resumeInheritPermission({
|
||||
const isFolder = folderTypeList.includes(resource.type);
|
||||
|
||||
const fn = async (session: ClientSession) => {
|
||||
const parentResource = await resourceModel
|
||||
.findById(resource.parentId, 'defaultPermission')
|
||||
.lean<SyncChildrenPermissionResourceType & { defaultPermission: PermissionValueType }>()
|
||||
.session(session);
|
||||
|
||||
const parentDefaultPermissionVal = parentResource?.defaultPermission ?? NullPermission;
|
||||
|
||||
// update the resource permission
|
||||
await resourceModel.updateOne(
|
||||
{
|
||||
_id: resource._id
|
||||
},
|
||||
{
|
||||
inheritPermission: true,
|
||||
defaultPermission: parentDefaultPermissionVal
|
||||
inheritPermission: true
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
// Folder resource, need to sync children
|
||||
if (isFolder) {
|
||||
const parentClbs = await getResourceAllClbs({
|
||||
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||
resourceId: resource.parentId,
|
||||
teamId: resource.teamId,
|
||||
resourceType,
|
||||
@@ -155,7 +132,7 @@ export async function resumeInheritPermission({
|
||||
// sync self
|
||||
await syncCollaborators({
|
||||
resourceType,
|
||||
collaborators: parentClbs,
|
||||
collaborators: parentClbsAndGroups,
|
||||
teamId: resource.teamId,
|
||||
resourceId: resource._id,
|
||||
session
|
||||
@@ -169,8 +146,7 @@ export async function resumeInheritPermission({
|
||||
folderTypeList,
|
||||
resourceType,
|
||||
session,
|
||||
defaultPermission: parentDefaultPermissionVal,
|
||||
collaborators: parentClbs
|
||||
collaborators: parentClbsAndGroups
|
||||
});
|
||||
} else {
|
||||
// Not folder, delete all clb
|
||||
@@ -215,6 +191,7 @@ export async function syncCollaborators({
|
||||
resourceId,
|
||||
resourceType: resourceType,
|
||||
tmbId: item.tmbId,
|
||||
groupId: item.groupId,
|
||||
permission: item.permission
|
||||
})),
|
||||
{
|
||||
|
@@ -64,7 +64,7 @@ export const getGroupsByTmbId = async ({
|
||||
groupId: {
|
||||
$exists: true
|
||||
},
|
||||
role: role ? { $in: role } : undefined
|
||||
...(role ? { role: { $in: role } } : {})
|
||||
})
|
||||
.populate('groupId')
|
||||
.lean()
|
||||
|
@@ -28,5 +28,6 @@ export type AuthResponseType<T extends Permission = Permission> = {
|
||||
authType?: `${AuthUserTypeEnum}`;
|
||||
appId?: string;
|
||||
apikey?: string;
|
||||
isRoot: boolean;
|
||||
permission: T;
|
||||
};
|
||||
|
@@ -8,7 +8,7 @@ import { TeamPermission } from '@fastgpt/global/support/permission/user/controll
|
||||
|
||||
/* auth user role */
|
||||
export async function authUserPer(props: AuthModeType): Promise<
|
||||
AuthResponseType & {
|
||||
AuthResponseType<TeamPermission> & {
|
||||
tmb: TeamTmbItemType;
|
||||
}
|
||||
> {
|
||||
|
Reference in New Issue
Block a user