Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(member-group): Team #2616

Merged
merged 12 commits into from
Sep 13, 2024
17 changes: 16 additions & 1 deletion packages/global/common/error/code/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export enum TeamErrEnum {
appAmountNotEnough = 'appAmountNotEnough',
pluginAmountNotEnough = 'pluginAmountNotEnough',
websiteSyncNotEnough = 'websiteSyncNotEnough',
reRankNotEnough = 'reRankNotEnough'
reRankNotEnough = 'reRankNotEnough',
groupNameEmpty = 'groupNameEmpty',
groupNotExist = 'groupNotExist',
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup'
}

const teamErr = [
Expand Down Expand Up @@ -46,6 +49,18 @@ const teamErr = [
{
statusText: TeamErrEnum.reRankNotEnough,
message: i18nT('common:code_error.team_error.re_rank_not_enough')
},
{
statusText: TeamErrEnum.groupNameEmpty,
message: i18nT('common:code_error.team_error.group_name_empty')
},
{
statusText: TeamErrEnum.groupNotExist,
message: i18nT('common:code_error.team_error.group_not_exist')
},
{
statusText: TeamErrEnum.cannotDeleteDefaultGroup,
message: i18nT('common:code_error.team_error.cannot_delete_default_group')
}
];

Expand Down
5 changes: 5 additions & 0 deletions packages/global/common/file/image/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum MongoImageTypeEnum {
datasetAvatar = 'datasetAvatar',
userAvatar = 'userAvatar',
teamAvatar = 'teamAvatar',
groupAvatar = 'groupAvatar',

chatImage = 'chatImage',
collectionImage = 'collectionImage'
Expand Down Expand Up @@ -36,6 +37,10 @@ export const mongoImageTypeMap = {
label: 'teamAvatar',
unique: true
},
[MongoImageTypeEnum.groupAvatar]: {
label: 'groupAvatar',
unique: true
},

[MongoImageTypeEnum.chatImage]: {
label: 'chatImage',
Expand Down
9 changes: 8 additions & 1 deletion packages/global/support/permission/collaborator.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RequireAtLeastOne, RequireOnlyOne } from '../../common/type/utils';
import { Permission } from './controller';
import { PermissionValueType } from './type';

Expand All @@ -10,6 +11,12 @@ export type CollaboratorItemType = {
};

export type UpdateClbPermissionProps = {
tmbIds: string[];
members?: string[];
groups?: string[];
permission: PermissionValueType;
};

export type DeleteClbPermissionProps = RequireOnlyOne<{
tmbId: string;
groupId: string;
}>;
5 changes: 5 additions & 0 deletions packages/global/support/permission/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export enum PerResourceTypeEnum {
dataset = 'dataset'
}

export enum SubjectTypeEnum {
tmb = 'tmb',
group = 'group'
}

/* new permission */
export enum PermissionKeyEnum {
read = 'read',
Expand Down
21 changes: 21 additions & 0 deletions packages/global/support/permission/memberGroup/type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TeamMemberItemType } from 'support/user/team/type';
import { TeamPermission } from '../user/controller';

type MemberGroupSchemaType = {
_id: string;
teamId: string;
name: string;
avatar: string;
};

type GroupMemberSchemaType = {
groupId: string;
tmbId: string;
};

type MemberGroupType = MemberGroupSchemaType & {
members: string[]; // we can get tmb's info from other api. there is no need but only need to get tmb's id
permission: TeamPermission;
};

type MemberGroupListType = MemberGroupType[];
15 changes: 12 additions & 3 deletions packages/global/support/permission/type.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { RequireOnlyOne } from '../../common/type/utils';
import { TeamMemberWithUserSchema } from '../user/team/type';
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
import {
AuthUserTypeEnum,
PermissionKeyEnum,
PerResourceTypeEnum,
SubjectTypeEnum
} from './constant';

// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
// It is spired by the permission system in Linux.
// The lowest 3 bits present the permission of reading, writing and managing.
// The higher bits are advanced permissions or extended permissions, which could be customized.
export type PermissionValueType = number;
export type ResourceType = `${PerResourceTypeEnum}`;
export type SubjectType = `${SubjectTypeEnum}`;

export type PermissionListType<T = {}> = Record<
T | PermissionKeyEnum,
Expand All @@ -20,11 +27,13 @@ export type PermissionListType<T = {}> = Record<

export type ResourcePermissionType = {
teamId: string;
tmbId: string;
resourceType: ResourceType;
permission: PermissionValueType;
resourceId: string;
};
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
}>;

export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
tmbId: TeamMemberWithUserSchema;
Expand Down
1 change: 0 additions & 1 deletion packages/global/support/user/team/controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export type UpdateTeamMemberProps = {
export type InviteMemberProps = {
teamId: string;
usernames: string[];
permission: PermissionValueType;
};
export type UpdateInviteProps = {
tmbId: string;
Expand Down
12 changes: 12 additions & 0 deletions packages/global/support/user/team/group/api.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type postCreateGroupData = {
name: string;
avatar?: string;
memberIdList?: string[];
};

export type putUpdateGroupData = {
groupId: string;
name?: string;
avatar?: string;
memberIdList?: string[];
};
4 changes: 3 additions & 1 deletion packages/global/support/user/team/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export type TeamSchema = {
lastWebsiteSyncTime: Date;
};
lafAccount: LafAccountType;
defaultPermission: PermissionValueType;
notificationAccount?: string;
};

export type tagsType = {
label: string;
key: string;
};

export type TeamTagSchema = TeamTagItemType & {
_id: string;
teamId: string;
Expand All @@ -45,9 +45,11 @@ export type TeamMemberSchema = {
export type TeamMemberWithUserSchema = Omit<TeamMemberSchema, 'userId'> & {
userId: UserModelSchema;
};

export type TeamMemberWithTeamSchema = Omit<TeamMemberSchema, 'teamId'> & {
teamId: TeamSchema;
};

export type TeamMemberWithTeamAndUserSchema = Omit<TeamMemberWithTeamSchema, 'userId'> & {
userId: UserModelSchema;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/service/support/permission/app/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const authAppByTmbId = async ({
resourceId: appId,
resourceType: PerResourceTypeEnum.app
});
const Per = new AppPermission({ per: rp?.permission ?? app.defaultPermission, isOwner });
const Per = new AppPermission({ per: rp ?? app.defaultPermission, isOwner });
return {
Per,
defaultPermission: app.defaultPermission
Expand Down
114 changes: 95 additions & 19 deletions packages/service/support/permission/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,121 @@ import { authOpenApiKey } from '../openapi/auth';
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
import { MongoResourcePermission } from './schema';
import { ClientSession } from 'mongoose';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
import {
PermissionValueType,
ResourcePermissionType
} from '@fastgpt/global/support/permission/type';
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
import { addMinutes } from 'date-fns';
import { getGroupsByTmbId } from './memberGroup/controllers';

/** get resource permission for a team member
* If there is no permission for the team member, it will return undefined
* @param resourceType: PerResourceTypeEnum
* @param teamId
* @param tmbId
* @param resourceId
* @returns PermissionValueType | undefined
*/
export const getResourcePermission = async ({
resourceType,
teamId,
tmbId,
resourceId
}: {
resourceType: PerResourceTypeEnum;
teamId: string;
tmbId: string;
resourceId?: string;
}) => {
const per = await MongoResourcePermission.findOne({
tmbId,
teamId,
resourceType,
resourceId
});
} & (
| {
resourceType: 'team';
resourceId?: undefined;
}
| {
resourceType: Omit<PerResourceTypeEnum, 'team'>;
resourceId: string;
}
)): Promise<PermissionValueType | undefined> => {
const tmbPer = (
await MongoResourcePermission.findOne(
{
tmbId,
teamId,
resourceType,
resourceId
},
'permission'
).lean()
)?.permission;

if (!per) {
return null;
if (tmbPer !== undefined) {
// could be 0
return tmbPer;
}
return per;

const groupIdList = await getGroupsByTmbId(tmbId);
if (!groupIdList || !groupIdList.length) {
return tmbPer; // could be undefined
}

const groupPer = await (async () => {
// get the maximum permission of the group
if (!groupIdList || !groupIdList.length) {
return undefined;
}
const pers = (
await MongoResourcePermission.find(
{
teamId,
resourceType,
groupId: {
$in: groupIdList
},
resourceId
},
'permission'
)
).map((item) => item.permission);

return getMaxGroupPer(pers);
})();

return groupPer ?? undefined;
};

export async function getResourceAllClbs({
resourceId,
teamId,
resourceType,
session
}: {
resourceId: ParentIdType;
teamId: string;
resourceType: PerResourceTypeEnum;
// resourceType: T;
session?: ClientSession;
}): Promise<ResourcePermissionType[]> {
if (!resourceId) return [];
// resourceId?: T extends 'team' ? undefined : ParentIdType;
} & (
| {
resourceType: 'team';
resourceId?: undefined;
}
| {
resourceType: Omit<PerResourceTypeEnum, 'team'>;
resourceId?: string | null;
}
)): Promise<ResourcePermissionType[]> {
return MongoResourcePermission.find(
{
resourceId,
resourceType: resourceType,
teamId: teamId
teamId: teamId,
groupId: null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为啥要加个 null,,undefined 岂不是找不到了

},
null,
{
session
}
).lean();
}

export const delResourcePermissionById = (id: string) => {
return MongoResourcePermission.findByIdAndRemove(id);
};
Expand Down Expand Up @@ -301,3 +364,16 @@ export const authFileToken = (token?: string) =>
});
});
});

export const getMaxGroupPer = (groups?: PermissionValueType[]) => {
if (!groups || !groups.length) {
return undefined;
}
return groups.reduce((prev, cur) => {
FinleyGe marked this conversation as resolved.
Show resolved Hide resolved
if (cur) {
return Math.max(prev, cur);
} else {
return prev;
}
});
};
2 changes: 1 addition & 1 deletion packages/service/support/permission/dataset/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const authDatasetByTmbId = async ({
resourceType: PerResourceTypeEnum.dataset
});
const Per = new DatasetPermission({
per: rp?.permission ?? dataset.defaultPermission,
per: rp ?? dataset.defaultPermission,
isOwner
});
return {
Expand Down
7 changes: 5 additions & 2 deletions packages/service/support/permission/inheritPermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { MongoResourcePermission } from './schema';
import { ClientSession, Model } from 'mongoose';
import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { getResourceAllClbs } from './controller';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';

export type SyncChildrenPermissionResourceType = {
_id: string;
Expand All @@ -14,8 +15,10 @@ export type SyncChildrenPermissionResourceType = {
};
export type UpdateCollaboratorItem = {
permission: PermissionValueType;
} & RequireOnlyOne<{
tmbId: string;
};
groupId: string;
}>;

// sync the permission to all children folders.
export async function syncChildrenPermission({
Expand Down
Loading
Loading