refactor: permission role & app read chat log permission (#5416)

* refactor: permission role

* refactor: permission type

* fix: permission manage

* fix: group owner cannot be deleted

* chore: common per map

* chore: openapi

* chore: rename

* fix: type error

* chore: app chat log permission

* chore: add initv4112
This commit is contained in:
Finley Ge
2025-08-11 10:51:44 +08:00
committed by GitHub
parent 29edf1ea5f
commit 57e1ef1176
52 changed files with 730 additions and 402 deletions

View File

@@ -1,20 +1,56 @@
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
import { type PermissionListType } from '../type';
import {
NullRoleVal,
CommonPerKeyEnum,
CommonRoleList,
CommonPerList,
CommonRolePerMap
} from '../constant';
import type { PermissionListType, PermissionValueType, RolePerMapType } from '../type';
import { type RoleListType } from '../type';
import { i18nT } from '../../../../web/i18n/utils';
export enum AppPermissionKeyEnum {}
export const AppPermissionList: PermissionListType = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
import { sumPer } from '../utils';
export enum AppPermissionKeyEnum {
ReadChatLog = 'readChatLog'
}
export const AppPerList: PermissionListType<AppPermissionKeyEnum> = {
...CommonPerList,
readChatLog: 0b1000
};
export const AppRoleList: RoleListType<AppPermissionKeyEnum> = {
[CommonPerKeyEnum.read]: {
...CommonRoleList[CommonPerKeyEnum.read],
description: i18nT('app:permission.des.read')
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
[CommonPerKeyEnum.write]: {
...CommonRoleList[CommonPerKeyEnum.write],
description: i18nT('app:permission.des.write')
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
[CommonPerKeyEnum.manage]: {
...CommonRoleList[CommonPerKeyEnum.manage],
description: i18nT('app:permission.des.manage')
},
[AppPermissionKeyEnum.ReadChatLog]: {
value: 0b1000,
checkBoxType: 'multiple',
name: i18nT('app:permission.name.readChatLog'),
description: i18nT('app:permission.des.readChatLog')
}
};
export const AppDefaultPermissionVal = NullPermission;
export const AppRolePerMap: RolePerMapType = new Map([
...CommonRolePerMap,
[
AppRoleList[AppPermissionKeyEnum.ReadChatLog].value,
sumPer(
CommonPerList[CommonPerKeyEnum.read],
AppPerList[AppPermissionKeyEnum.ReadChatLog]
) as PermissionValueType
]
]);
export const AppDefaultRoleVal = NullRoleVal;
export const AppReadChatLogPerVal = AppPerList[AppPermissionKeyEnum.ReadChatLog];
export const AppReadChatLogRoleVal = AppRoleList[AppPermissionKeyEnum.ReadChatLog].value;

View File

@@ -1,15 +1,25 @@
import { type PerConstructPros, Permission } from '../controller';
import { AppDefaultPermissionVal } from './constant';
import { AppDefaultRoleVal, AppPerList, AppRoleList, AppRolePerMap } from './constant';
export class AppPermission extends Permission {
hasReadChatLogPer: boolean = false;
hasReadChatLogRole: boolean = false;
constructor(props?: PerConstructPros) {
if (!props) {
props = {
per: AppDefaultPermissionVal
role: AppDefaultRoleVal
};
} else if (!props?.per) {
props.per = AppDefaultPermissionVal;
} else if (!props?.role) {
props.role = AppDefaultRoleVal;
}
props.roleList = AppRoleList;
props.rolePerMap = AppRolePerMap;
props.perList = AppPerList;
super(props);
this.setUpdatePermissionCallback(() => {
this.hasReadChatLogPer = this.checkPer(AppPerList.readChatLog);
this.hasReadChatLogRole = this.checkRole(AppRoleList.readChatLog.value);
});
}
}

View File

@@ -1,5 +1,7 @@
import { type PermissionListType } from './type';
import type { PermissionListType, PermissionValueType, RolePerMapType } from './type';
import { type RoleListType } from './type';
import { i18nT } from '../../../web/i18n/utils';
import { sumPer } from './utils';
export enum AuthUserTypeEnum {
token = 'token',
root = 'root',
@@ -15,6 +17,12 @@ export enum PermissionTypeEnum {
publicRead = 'publicRead',
publicWrite = 'publicWrite'
}
export const NullRoleVal = 0;
export const NullPermissionVal = 0;
export const OwnerRoleVal = ~0 >>> 0;
export const OwnerPermissionVal = ~0 >>> 0;
export const PermissionTypeMap = {
[PermissionTypeEnum.private]: {
iconLight: 'support/permission/privateLight',
@@ -45,34 +53,63 @@ export enum PerResourceTypeEnum {
}
/* new permission */
export enum PermissionKeyEnum {
export enum CommonPerKeyEnum {
owner = 'owner',
read = 'read',
write = 'write',
manage = 'manage'
}
export const PermissionList: PermissionListType = {
[PermissionKeyEnum.read]: {
export enum CommonRoleKeyEnum {
read = 'read',
write = 'write',
manage = 'manage'
}
export const CommonPerList: PermissionListType = {
[CommonPerKeyEnum.owner]: OwnerRoleVal,
[CommonPerKeyEnum.read]: 0b100,
[CommonPerKeyEnum.write]: 0b010,
[CommonPerKeyEnum.manage]: 0b001
} as const;
export const CommonRoleList: RoleListType = {
[CommonRoleKeyEnum.read]: {
name: i18nT('common:permission.read'),
description: '',
value: 0b100,
checkBoxType: 'single'
},
[PermissionKeyEnum.write]: {
[CommonRoleKeyEnum.write]: {
name: i18nT('common:permission.write'),
description: '',
value: 0b110,
value: 0b010,
checkBoxType: 'single'
},
[PermissionKeyEnum.manage]: {
[CommonRoleKeyEnum.manage]: {
name: i18nT('common:permission.manager'),
description: '',
value: 0b111,
value: 0b001,
checkBoxType: 'single'
}
};
} as const;
export const NullPermission = 0;
export const OwnerPermissionVal = ~0 >>> 0;
export const ReadPermissionVal = PermissionList['read'].value;
export const WritePermissionVal = PermissionList['write'].value;
export const ManagePermissionVal = PermissionList['manage'].value;
export const CommonRolePerMap: RolePerMapType = new Map([
[CommonRoleList['read'].value, CommonPerList.read],
[
CommonRoleList['write'].value,
sumPer(CommonPerList.write, CommonPerList.read) as PermissionValueType
],
[
CommonRoleList['manage'].value,
sumPer(CommonPerList.manage, CommonPerList.write, CommonPerList.read) as PermissionValueType
]
]);
export const ReadRoleVal = CommonRoleList['read'].value;
export const WriteRoleVal = CommonRoleList['write'].value;
export const ManageRoleVal = CommonRoleList['manage'].value;
export const ManagePermissionVal = CommonPerList.manage;
export const ReadPermissionVal = CommonPerList.read;
export const WritePermissionVal = CommonPerList.write;

View File

@@ -1,58 +1,86 @@
import { type PermissionListType, type PermissionValueType } from './type';
import { PermissionList, NullPermission, OwnerPermissionVal } from './constant';
import type {
RoleValueType,
RoleListType,
PermissionValueType,
PermissionListType,
RolePerMapType
} from './type';
import {
CommonPerList,
CommonRoleList,
CommonRolePerMap,
NullPermissionVal,
NullRoleVal,
OwnerPermissionVal,
OwnerRoleVal
} from './constant';
export type PerConstructPros = {
per?: PermissionValueType;
role?: RoleValueType;
isOwner?: boolean;
permissionList?: PermissionListType;
childUpdatePermissionCallback?: () => void;
roleList?: RoleListType;
perList?: PermissionListType;
rolePerMap?: RolePerMapType;
};
// the Permission helper class
/**
* the Permission helper class
*/
export class Permission {
value: PermissionValueType;
role: PermissionValueType;
private permission: PermissionValueType = NullRoleVal; // default role
isOwner: boolean = false;
hasManagePer: boolean = false;
hasWritePer: boolean = false;
hasReadPer: boolean = false;
_permissionList: PermissionListType;
hasManageRole: boolean = false;
hasWriteRole: boolean = false;
hasReadRole: boolean = false;
constructor(props?: PerConstructPros) {
const { per = NullPermission, isOwner = false, permissionList = PermissionList } = props || {};
readonly roleList: RoleListType;
readonly perList: PermissionListType;
readonly rolePerMap: RolePerMapType;
constructor({
role = NullRoleVal,
isOwner = false,
roleList = CommonRoleList,
perList = CommonPerList,
rolePerMap = CommonRolePerMap
}: PerConstructPros = {}) {
if (isOwner) {
this.value = OwnerPermissionVal;
this.role = OwnerRoleVal;
} else {
this.value = per;
this.role = role;
}
this._permissionList = permissionList;
this.roleList = roleList;
this.perList = perList;
this.rolePerMap = rolePerMap;
this.updatePermissions();
}
// add permission(s)
// it can be chaining called.
// @example
// const perm = new Permission(permission)
// perm.add(PermissionList['read'])
// perm.add(PermissionList['read'], PermissionList['write'])
// perm.add(PermissionList['read']).add(PermissionList['write'])
addPer(...perList: PermissionValueType[]) {
addRole(...roleList: RoleValueType[]) {
if (this.isOwner) {
return this;
}
for (const per of perList) {
this.value = this.value | per;
for (const per of roleList) {
this.role = this.role | per;
}
this.updatePermissions();
return this;
}
removePer(...perList: PermissionValueType[]) {
removeRole(...roleList: RoleValueType[]) {
if (this.isOwner) {
return this.value;
return this.role;
}
for (const per of perList) {
this.value = this.value & ~per;
for (const per of roleList) {
this.role = this.role & ~per;
}
this.updatePermissions();
return this;
@@ -61,9 +89,16 @@ export class Permission {
checkPer(perm: PermissionValueType): boolean {
// if the permission is owner permission, only owner has this permission.
if (perm === OwnerPermissionVal) {
return this.value === OwnerPermissionVal;
return this.permission === OwnerPermissionVal;
}
return (this.value & perm) === perm;
return (this.permission & perm) === perm;
}
checkRole(role: RoleValueType): boolean {
if (role === OwnerRoleVal) {
return this.role === OwnerRoleVal;
}
return (this.role & role) === role;
}
private updatePermissionCallback?: () => void;
@@ -72,15 +107,32 @@ export class Permission {
this.updatePermissionCallback = callback;
}
private updatePermissions() {
this.isOwner = this.value === OwnerPermissionVal;
this.hasManagePer = this.checkPer(this._permissionList['manage'].value);
this.hasWritePer = this.checkPer(this._permissionList['write'].value);
this.hasReadPer = this.checkPer(this._permissionList['read'].value);
this.updatePermissionCallback?.();
private calculatePer() {
if (this.role === OwnerRoleVal) {
this.permission = OwnerPermissionVal;
return;
}
let role = this.role;
this.permission = 0;
while (role > 0) {
// Binary Magic
this.permission |= this.rolePerMap.get(role & -role) ?? 0;
role &= role - 1;
}
}
toBinary() {
return this.value.toString(2);
private updatePermissions() {
this.calculatePer();
this.isOwner = this.permission === OwnerRoleVal;
this.hasManagePer = this.checkPer(this.roleList['manage'].value);
this.hasWritePer = this.checkPer(this.roleList['write'].value);
this.hasReadPer = this.checkPer(this.roleList['read'].value);
this.hasManageRole = this.checkRole(this.roleList['manage'].value);
this.hasWriteRole = this.checkRole(this.roleList['write'].value);
this.hasReadRole = this.checkRole(this.roleList['read'].value);
this.updatePermissionCallback?.();
}
}

View File

@@ -1,21 +1,30 @@
import { i18nT } from '../../../../web/i18n/utils';
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
import {
NullRoleVal,
CommonPerKeyEnum,
CommonRoleList,
CommonRolePerMap,
CommonPerList
} from '../constant';
import type { RolePerMapType } from '../type';
export enum DatasetPermissionKeyEnum {}
export const DatasetPermissionList = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
export const DatasetRoleList = {
[CommonPerKeyEnum.read]: {
...CommonRoleList[CommonPerKeyEnum.read],
description: i18nT('dataset:permission.des.read')
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
[CommonPerKeyEnum.write]: {
...CommonRoleList[CommonPerKeyEnum.write],
description: i18nT('dataset:permission.des.write')
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
[CommonPerKeyEnum.manage]: {
...CommonRoleList[CommonPerKeyEnum.manage],
description: i18nT('dataset:permission.des.manage')
}
};
export const DatasetDefaultPermissionVal = NullPermission;
export const DatasetRolePerMap: RolePerMapType = CommonRolePerMap;
export const DatasetPerList = CommonPerList;
export const DataSetDefaultRoleVal = NullRoleVal;

View File

@@ -1,14 +1,22 @@
import { NullPermission } from '../constant';
import { type PerConstructPros, Permission } from '../controller';
import {
DataSetDefaultRoleVal,
DatasetPerList,
DatasetRoleList,
DatasetRolePerMap
} from './constant';
export class DatasetPermission extends Permission {
constructor(props?: PerConstructPros) {
if (!props) {
props = {
per: NullPermission
role: DataSetDefaultRoleVal
};
} else if (!props?.per) {
props.per = NullPermission;
} else if (!props?.role) {
props.role = DataSetDefaultRoleVal;
}
props.roleList = DatasetRoleList;
props.rolePerMap = DatasetRolePerMap;
props.perList = DatasetPerList;
super(props);
}
}

View File

@@ -1,5 +1,5 @@
import { PermissionKeyEnum, PermissionList } from '../constant';
import { type PermissionListType } from '../type';
import { CommonPerKeyEnum, CommonRoleList } from '../constant';
import { type RoleListType } from '../type';
export enum GroupMemberRole {
owner = 'owner',
@@ -7,17 +7,17 @@ export enum GroupMemberRole {
member = 'member'
}
export const memberGroupPermissionList: PermissionListType = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
export const memberGroupPermissionList: RoleListType = {
[CommonPerKeyEnum.read]: {
...CommonRoleList[CommonPerKeyEnum.read],
value: 0b100
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
[CommonPerKeyEnum.write]: {
...CommonRoleList[CommonPerKeyEnum.write],
value: 0b010
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
[CommonPerKeyEnum.manage]: {
...CommonRoleList[CommonPerKeyEnum.manage],
value: 0b001
}
};

View File

@@ -3,25 +3,63 @@ import type { RequireOnlyOne } from '../../common/type/utils';
import type { TeamMemberSchema } from '../user/team/type';
import { MemberGroupSchemaType } from './memberGroup/type';
import type { TeamMemberWithUserSchema } from '../user/team/type';
import { AuthUserTypeEnum, type PermissionKeyEnum, type PerResourceTypeEnum } from './constant';
import type { CommonPerKeyEnum, CommonRoleKeyEnum } from './constant';
import { AuthUserTypeEnum, type CommonPerKeyEnum, type PerResourceTypeEnum } 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 RoleValueType = number;
export type ResourceType = `${PerResourceTypeEnum}`;
export type PermissionListType<T = {}> = Record<
T | PermissionKeyEnum,
{
name: string;
description: string;
value: PermissionValueType;
checkBoxType: 'single' | 'multiple';
}
/**
* Define the roles. Each role is a binary number, only one bit is set to 1.
*/
export type RoleListType<T = {}> = Readonly<
Record<
T | CommonRoleKeyEnum,
Readonly<{
name: string;
description: string;
value: RoleValueType;
checkBoxType: 'single' | 'multiple';
}>
>
>;
/**
* Define the permissions. Each permission is a binary number, only one bit is set to 1.
* @example
* CommonPerList = {
* read: 0b100,
* write: 0b010,
* manage: 0b001
* }
* @example_bad
* CommonPerList = {
* write: 0b110, // bad, should be 0b010
* }
*/
export type PermissionListType<T = {}> = Readonly<
Record<T | CommonPerKeyEnum, PermissionValueType>
>;
/**
* Define the role-permission map. Each role has a permission.
* @key: role (binary number), only one bit is set to 1.
* @value: permission (binary number), multiple bits are set to 1.
* @example
* CommonRolePerMap = {
* 0b100: 0b100,
* 0b010: 0b110,
* 0b001: 0b111
* }
*/
export type RolePerMapType = Readonly<Map<RoleValueType, PermissionValueType>>;
export type ResourcePermissionType = {
teamId: string;
resourceType: ResourceType;

View File

@@ -1,39 +1,59 @@
import { PermissionKeyEnum } from '../constant';
import { type PermissionListType } from '../type';
import { PermissionList } from '../constant';
import { CommonPerKeyEnum, CommonRolePerMap } from '../constant';
import type {
PermissionListType,
PermissionValueType,
RoleListType,
RolePerMapType
} from '../type';
import { CommonRoleList, CommonPerList } from '../constant';
import { i18nT } from '../../../../web/i18n/utils';
export enum TeamPermissionKeyEnum {
import { sumPer } from '../utils';
export enum TeamPerKeyEnum {
appCreate = 'appCreate',
datasetCreate = 'datasetCreate',
apikeyCreate = 'apikeyCreate'
}
export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
export enum TeamRoleKeyEnum {
appCreate = 'appCreate',
datasetCreate = 'datasetCreate',
apikeyCreate = 'apikeyCreate'
}
export const TeamPerList: PermissionListType<TeamPerKeyEnum> = {
...CommonPerList,
apikeyCreate: 0b100000,
appCreate: 0b001000,
datasetCreate: 0b010000
};
export const TeamRoleList: RoleListType<TeamRoleKeyEnum> = {
[CommonPerKeyEnum.read]: {
...CommonRoleList[CommonPerKeyEnum.read],
value: 0b000100
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
[CommonPerKeyEnum.write]: {
...CommonRoleList[CommonPerKeyEnum.write],
value: 0b000010
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
[CommonPerKeyEnum.manage]: {
...CommonRoleList[CommonPerKeyEnum.manage],
value: 0b000001
},
[TeamPermissionKeyEnum.appCreate]: {
[TeamRoleKeyEnum.appCreate]: {
checkBoxType: 'multiple',
description: '',
name: i18nT('account_team:permission_appCreate'),
value: 0b001000
},
[TeamPermissionKeyEnum.datasetCreate]: {
[TeamRoleKeyEnum.datasetCreate]: {
checkBoxType: 'multiple',
description: '',
name: i18nT('account_team:permission_datasetCreate'),
value: 0b010000
},
[TeamPermissionKeyEnum.apikeyCreate]: {
[TeamRoleKeyEnum.apikeyCreate]: {
checkBoxType: 'multiple',
description: '',
name: i18nT('account_team:permission_apikeyCreate'),
@@ -41,10 +61,38 @@ export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = {
}
};
export const TeamReadPermissionVal = TeamPermissionList['read'].value;
export const TeamWritePermissionVal = TeamPermissionList['write'].value;
export const TeamManagePermissionVal = TeamPermissionList['manage'].value;
export const TeamAppCreatePermissionVal = TeamPermissionList['appCreate'].value;
export const TeamDatasetCreatePermissionVal = TeamPermissionList['datasetCreate'].value;
export const TeamApikeyCreatePermissionVal = TeamPermissionList['apikeyCreate'].value;
export const TeamRolePerMap: RolePerMapType = new Map([
...CommonRolePerMap,
[
TeamRoleList['appCreate'].value,
sumPer(TeamPerList.appCreate, CommonPerList.read, CommonPerList.write) as PermissionValueType
],
[
TeamRoleList['datasetCreate'].value,
sumPer(
TeamPerList.datasetCreate,
CommonPerList.read,
CommonPerList.write
) as PermissionValueType
],
[
TeamRoleList['apikeyCreate'].value,
sumPer(TeamPerList.apikeyCreate, CommonPerList.read, CommonPerList.write) as PermissionValueType
]
]);
export const TeamReadRoleVal = TeamRoleList['read'].value;
export const TeamWriteRoleVal = TeamRoleList['write'].value;
export const TeamManageRoleVal = TeamRoleList['manage'].value;
export const TeamAppCreateRoleVal = TeamRoleList['appCreate'].value;
export const TeamDatasetCreateRoleVal = TeamRoleList['datasetCreate'].value;
export const TeamApikeyCreateRoleVal = TeamRoleList['apikeyCreate'].value;
export const TeamDefaultRoleVal = TeamReadRoleVal;
export const TeamReadPermissionVal = TeamPerList.read;
export const TeamWritePermissionVal = TeamPerList.write;
export const TeamManagePermissionVal = TeamPerList.manage;
export const TeamAppCreatePermissionVal = TeamPerList.appCreate;
export const TeamDatasetCreatePermissionVal = TeamPerList.datasetCreate;
export const TeamApikeyCreatePermissionVal = TeamPerList.apikeyCreate;
export const TeamDefaultPermissionVal = TeamReadPermissionVal;

View File

@@ -1,13 +1,18 @@
import { type PerConstructPros, Permission } from '../controller';
import {
TeamApikeyCreatePermissionVal,
TeamAppCreatePermissionVal,
TeamDatasetCreatePermissionVal,
TeamDefaultPermissionVal,
TeamPermissionList
TeamApikeyCreateRoleVal,
TeamAppCreateRoleVal,
TeamDatasetCreateRoleVal,
TeamDefaultRoleVal,
TeamPerList,
TeamRoleList,
TeamRolePerMap
} from './constant';
export class TeamPermission extends Permission {
hasAppCreateRole: boolean = false;
hasDatasetCreateRole: boolean = false;
hasApikeyCreateRole: boolean = false;
hasAppCreatePer: boolean = false;
hasDatasetCreatePer: boolean = false;
hasApikeyCreatePer: boolean = false;
@@ -15,18 +20,23 @@ export class TeamPermission extends Permission {
constructor(props?: PerConstructPros) {
if (!props) {
props = {
per: TeamDefaultPermissionVal
role: TeamDefaultRoleVal
};
} else if (!props?.per) {
props.per = TeamDefaultPermissionVal;
} else if (!props?.role) {
props.role = TeamDefaultRoleVal;
}
props.permissionList = TeamPermissionList;
props.roleList = TeamRoleList;
props.rolePerMap = TeamRolePerMap;
props.perList = TeamPerList;
super(props);
this.setUpdatePermissionCallback(() => {
this.hasAppCreatePer = this.checkPer(TeamAppCreatePermissionVal);
this.hasDatasetCreatePer = this.checkPer(TeamDatasetCreatePermissionVal);
this.hasApikeyCreatePer = this.checkPer(TeamApikeyCreatePermissionVal);
this.hasAppCreateRole = this.checkRole(TeamAppCreateRoleVal);
this.hasDatasetCreateRole = this.checkRole(TeamDatasetCreateRoleVal);
this.hasApikeyCreateRole = this.checkRole(TeamApikeyCreateRoleVal);
this.hasAppCreatePer = this.checkPer(TeamAppCreateRoleVal);
this.hasDatasetCreatePer = this.checkPer(TeamDatasetCreateRoleVal);
this.hasApikeyCreatePer = this.checkPer(TeamApikeyCreateRoleVal);
});
}
}

View File

@@ -1,5 +1,5 @@
import { type PermissionValueType } from './type';
import { NullPermission, PermissionTypeEnum } from './constant';
import { NullRoleVal, PermissionTypeEnum } from './constant';
import type { Permission } from './controller';
/* team public source, or owner source in team */
@@ -30,7 +30,7 @@ export function mongoOwnerPermission({ teamId, tmbId }: { teamId: string; tmbId:
}
// return permission-related schema to define the schema of resources
export function getPermissionSchema(defaultPermission: PermissionValueType = NullPermission) {
export function getPermissionSchema(defaultPermission: PermissionValueType = NullRoleVal) {
return {
defaultPermission: {
type: Number,
@@ -42,3 +42,11 @@ export function getPermissionSchema(defaultPermission: PermissionValueType = Nul
}
};
}
export const sumPer = (...per: PermissionValueType[]) => {
if (per.length === 0) {
// prevent sum 0 value, to fallback to default value
return undefined;
}
return per.reduce((acc, cur) => acc | cur, 0);
};

View File

@@ -16,17 +16,17 @@ export const filterDatasetsByTmbId = async ({
// First get all permissions
const permissions = await Promise.all(
datasetIds.map(async (datasetId) => {
const per = await getResourcePermission({
const role = await getResourcePermission({
teamId,
tmbId,
resourceId: datasetId,
resourceType: PerResourceTypeEnum.dataset
});
if (per === undefined) return false;
if (role === undefined) return false;
const datasetPer = new DatasetPermission({
per,
role,
isOwner: tmbPer.isOwner
});

View File

@@ -12,7 +12,6 @@ import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
import { type AuthModeType, type AuthResponseType } from '../type';
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils';
export const authPluginByTmbId = async ({
@@ -83,16 +82,16 @@ export const authAppByTmbId = async ({
app.inheritPermission === false ||
!app.parentId
) {
// 1. is a folder. (Folders have compeletely permission)
// 1. is a folder. (Folders have completely permission)
// 2. inheritPermission is false.
// 3. is root folder/app.
const rp = await getResourcePermission({
const role = await getResourcePermission({
teamId,
tmbId,
resourceId: appId,
resourceType: PerResourceTypeEnum.app
});
const Per = new AppPermission({ per: rp ?? AppDefaultPermissionVal, isOwner });
const Per = new AppPermission({ role, isOwner });
return {
Per
};
@@ -105,7 +104,7 @@ export const authAppByTmbId = async ({
});
const Per = new AppPermission({
per: parent.permission.value,
role: parent.permission.role,
isOwner
});
return {

View File

@@ -4,7 +4,7 @@ import { parseHeaderCert } from '../controller';
import { getFileById } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { OwnerPermissionVal, ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { OwnerPermissionVal, ReadRoleVal } from '@fastgpt/global/support/permission/constant';
import { Permission } from '@fastgpt/global/support/permission/controller';
export const authCollectionFile = async ({
@@ -32,7 +32,7 @@ export const authCollectionFile = async ({
}
const permission = new Permission({
per: ReadPermissionVal,
role: ReadRoleVal,
isOwner: file.metadata?.uid === tmbId || file.metadata?.tmbId === tmbId
});

View File

@@ -6,7 +6,6 @@ import { MongoOpenApi } from '../../openapi/schema';
import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import { authAppByTmbId } from '../app/auth';
import { Permission } from '@fastgpt/global/support/permission/controller';
export async function authOpenApiKeyCrud({
id,
@@ -49,9 +48,7 @@ export async function authOpenApiKeyCrud({
return {
openapi,
permission: new Permission({
per
})
permission: tmbPer
};
})();

View File

@@ -21,6 +21,7 @@ import { type TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import { type OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
import { getOrgIdSetWithParentByTmbId } from './org/controllers';
import { authUserSession } from '../user/session';
import { sumPer } from '@fastgpt/global/support/permission/utils';
/** get resource permission for a team member
* If there is no permission for the team member, it will return undefined
@@ -102,7 +103,7 @@ export const getResourcePermission = async ({
.then((perList) => perList.map((item) => item.permission))
]);
return concatPer([...groupPers, ...orgPers]);
return sumPer(...groupPers, ...orgPers);
};
export async function getResourceClbsAndGroups({
@@ -402,11 +403,3 @@ export const authFileToken = (token?: string) =>
});
});
});
export const concatPer = (perList: PermissionValueType[] = []) => {
if (perList.length === 0) {
return undefined;
}
return new Permission().addPer(...perList).value;
};

View File

@@ -7,7 +7,10 @@ import {
} from '@fastgpt/global/core/dataset/type';
import { getTmbInfoByTmbId } from '../../user/team/controller';
import { MongoDataset } from '../../../core/dataset/schema';
import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import {
NullPermissionVal,
PerResourceTypeEnum
} from '@fastgpt/global/support/permission/constant';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
import { getCollectionWithDataset } from '../../../core/dataset/controller';
@@ -15,7 +18,7 @@ import { MongoDatasetData } from '../../../core/dataset/data/schema';
import { type AuthModeType, type AuthResponseType } from '../type';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
import { getDatasetImagePreviewUrl } from '../../../core/dataset/image/utils';
import { i18nT } from '../../../../web/i18n/utils';
@@ -71,7 +74,7 @@ export const authDatasetByTmbId = async ({
dataset.inheritPermission === false ||
!dataset.parentId
) {
// 1. is a folder. (Folders have compeletely permission)
// 1. is a folder. (Folders have completely permission)
// 2. inheritPermission is false.
// 3. is root folder/dataset.
const rp = await getResourcePermission({
@@ -81,7 +84,7 @@ export const authDatasetByTmbId = async ({
resourceType: PerResourceTypeEnum.dataset
});
const Per = new DatasetPermission({
per: rp ?? DatasetDefaultPermissionVal,
role: rp,
isOwner
});
return {
@@ -97,7 +100,7 @@ export const authDatasetByTmbId = async ({
});
const Per = new DatasetPermission({
per: parent.permission.value,
role: parent.permission.role,
isOwner
});
@@ -158,7 +161,7 @@ export const authDataset = async ({
// the temporary solution for authDatasetCollection is getting the
export async function authDatasetCollection({
collectionId,
per = NullPermission,
per = NullPermissionVal,
isRoot = false,
...props
}: AuthModeType & {
@@ -242,7 +245,7 @@ export async function authDatasetCollection({
// }
// }
/*
/*
DatasetData permission is inherited from collection.
*/
export async function authDatasetData({

View File

@@ -3,7 +3,7 @@ import { parseHeaderCert } from '../controller';
import { getTmbInfoByTmbId } from '../../user/team/controller';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { type AuthModeType, type AuthResponseType } from '../type';
import { NullPermission } from '@fastgpt/global/support/permission/constant';
import { NullPermissionVal } from '@fastgpt/global/support/permission/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { authCert } from '../auth/common';
import { MongoUser } from '../../user/schema';
@@ -28,7 +28,7 @@ export async function authUserPer(props: AuthModeType): Promise<
tmb
};
}
if (!tmb.permission.checkPer(props.per ?? NullPermission)) {
if (!tmb.permission.checkPer(props.per ?? NullPermissionVal)) {
return Promise.reject(TeamErrEnum.unAuthTeam);
}

View File

@@ -11,7 +11,7 @@ import { type UpdateTeamProps } from '@fastgpt/global/support/user/team/controll
import { getResourcePermission } from '../../permission/controller';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { TeamDefaultRoleVal } from '@fastgpt/global/support/permission/user/constant';
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
@@ -25,11 +25,12 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
return Promise.reject('member not exist');
}
const Per = await getResourcePermission({
resourceType: PerResourceTypeEnum.team,
teamId: tmb.teamId,
tmbId: tmb._id
});
const role =
(await getResourcePermission({
resourceType: PerResourceTypeEnum.team,
teamId: tmb.teamId,
tmbId: tmb._id
})) ?? TeamDefaultRoleVal;
return {
userId: String(tmb.userId),
@@ -44,7 +45,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
role: tmb.role,
status: tmb.status,
permission: new TeamPermission({
per: Per ?? TeamDefaultPermissionVal,
role,
isOwner: tmb.role === TeamMemberRoleEnum.owner
}),
notificationAccount: tmb.team.notificationAccount,

View File

@@ -143,6 +143,8 @@
"permission.des.manage": "Based on write permissions, you can configure publishing channels, view conversation logs, and assign permissions to the application.",
"permission.des.read": "Use the app to have conversations",
"permission.des.write": "Can view and edit apps",
"permission.des.readChatLog": "Can view chat logs",
"permission.name.readChatLog": "View chat logs",
"plugin.Instructions": "Instructions",
"plugin_cost_by_token": "Charged based on token usage",
"plugin_cost_per_times": "{{cost}} points/time",

View File

@@ -64,6 +64,7 @@
"Parse": "Analysis",
"Permission": "Permission",
"Permission_tip": "Individual permissions are greater than group permissions",
"permission_other": "Other permissions (multiple)",
"Preview": "Preview",
"Remove": "Remove",
"Rename": "Rename",

View File

@@ -143,6 +143,8 @@
"permission.des.manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限",
"permission.des.read": "可使用该应用进行对话",
"permission.des.write": "可查看和编辑应用",
"permission.des.readChatLog": "可查看对话日志",
"permission.name.readChatLog": "查看对话日志",
"plugin.Instructions": "使用说明",
"plugin_cost_by_token": "依据 token 消耗计费",
"plugin_cost_per_times": "{{cost}} 积分/次",

View File

@@ -64,6 +64,7 @@
"Parse": "解析",
"Permission": "权限",
"Permission_tip": "个人权限大于群组权限",
"permission_other": "其他权限(多选)",
"Preview": "预览",
"Remove": "移除",
"Rename": "重命名",

View File

@@ -143,6 +143,8 @@
"permission.des.manage": "在寫入權限基礎上,可以設定發布通道、檢視對話紀錄、分配這個應用程式的權限",
"permission.des.read": "可以使用這個應用程式進行對話",
"permission.des.write": "可以檢視和編輯應用程式",
"permission.des.readChatLog": "可以檢視對話紀錄",
"permission.name.readChatLog": "檢視對話紀錄",
"plugin.Instructions": "使用說明",
"plugin_cost_by_token": "根據 token 消耗計費",
"plugin_cost_per_times": "{{cost}} 積分/次",

View File

@@ -64,6 +64,7 @@
"Parse": "解析",
"Permission": "權限",
"Permission_tip": "個人權限大於群組權限",
"permission_other": "其他權限(多選)",
"Preview": "預覽",
"Remove": "移除",
"Rename": "重新命名",

View File

@@ -11,8 +11,8 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import React from 'react';
import { useContextSelector } from 'use-context-selector';
import PermissionSelect from './PermissionSelect';
import PermissionTags from './PermissionTags';
import RoleSelect from './RoleSelect';
import RoleTags from './RoleTags';
import { CollaboratorContext } from './context';
export type ManageModalProps = {
onClose: () => void;
@@ -70,17 +70,17 @@ function ManageModal({ onClose }: ManageModalProps) {
</Flex>
</Td>
<Td border="none">
<PermissionTags permission={item.permission.value} />
<RoleTags permission={item.permission.role} />
</Td>
<Td border="none">
{/* Not self; Not owner and other manager */}
{item.tmbId !== userInfo?.team?.tmbId &&
(permission.isOwner || !item.permission.hasManagePer) && (
<PermissionSelect
<RoleSelect
Button={
<MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} />
}
value={item.permission.value}
value={item.permission.role}
onChange={(permission) => {
onUpdate({
members: item.tmbId ? [item.tmbId] : undefined,

View File

@@ -2,8 +2,8 @@ import React from 'react';
import { useTranslation } from 'next-i18next';
import { Box, Checkbox, HStack, VStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import PermissionTags from './PermissionTags';
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
import RoleTags from './RoleTags';
import type { RoleValueType } from '@fastgpt/global/support/permission/type';
import MyIcon from '@fastgpt/web/components/common/Icon';
import OrgTags from '../../user/team/OrgTags';
import Tag from '@fastgpt/web/components/common/Tag';
@@ -15,7 +15,7 @@ function MemberItemCard({
isChecked,
onDelete,
name,
permission,
role: permission,
orgs,
addOnly,
rightSlot
@@ -26,7 +26,7 @@ function MemberItemCard({
isChecked?: boolean;
onDelete?: () => void;
name: string;
permission?: PermissionValueType;
role?: RoleValueType;
addOnly?: boolean;
orgs?: string[];
rightSlot?: React.ReactNode;
@@ -61,7 +61,7 @@ function MemberItemCard({
</Box>
<Box lineHeight={1}>{orgs && orgs.length > 0 && <OrgTags orgs={orgs} />}</Box>
</Box>
{!isAdded && permission && <PermissionTags permission={permission} />}
{!isAdded && permission && <RoleTags permission={permission} />}
{isAdded && (
<Tag
mixBlendMode={'multiply'}

View File

@@ -27,7 +27,7 @@ import { useMemo, useRef, useState } from 'react';
import { useContextSelector } from 'use-context-selector';
import { CollaboratorContext } from './context';
import MemberItemCard from './MemberItemCard';
import PermissionSelect from './PermissionSelect';
import RoleSelect from './RoleSelect';
const HoverBoxStyle = {
bgColor: 'myGray.50',
@@ -98,15 +98,13 @@ function MemberModal({
>([]);
const [selectedGroupList, setSelectedGroupList] = useState<MemberGroupListItemType<false>[]>([]);
const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList);
const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList);
const [selectedPermission, setSelectedPermission] = useState<number | undefined>(
permissionList?.read?.value
);
const perLabel = useMemo(() => {
if (selectedPermission === undefined) return '';
return getPerLabelList(selectedPermission!).join('、');
}, [getPerLabelList, selectedPermission]);
const roleList = useContextSelector(CollaboratorContext, (v) => v.roleList);
const getRoleLabelList = useContextSelector(CollaboratorContext, (v) => v.getRoleLabelList);
const [selectedRole, setSelectedRole] = useState<number | undefined>(roleList?.read?.value);
const roleLabel = useMemo(() => {
if (selectedRole === undefined) return '';
return getRoleLabelList(selectedRole!).join('、');
}, [getRoleLabelList, selectedRole]);
const onUpdateCollaborators = useContextSelector(
CollaboratorContext,
@@ -119,7 +117,7 @@ function MemberModal({
members: selectedMemberList.map((item) => item.tmbId),
groups: selectedGroupList.map((item) => item._id),
orgs: selectedOrgList.map((item) => item._id),
permission: addOnly ? undefined : selectedPermission!
permission: addOnly ? undefined : selectedRole!
} as UpdateClbPermissionProps<ValueOf<typeof addOnly>>),
{
successToast: t('common:add_success'),
@@ -277,7 +275,7 @@ function MemberModal({
avatar={member.avatar}
key={member.tmbId}
name={member.memberName}
permission={collaborator?.permission.value}
role={collaborator?.permission.role}
onChange={onChange}
isChecked={!!selectedMemberList.find((v) => v.tmbId === member.tmbId)}
orgs={member.orgs}
@@ -316,7 +314,7 @@ function MemberModal({
name={org.name}
onChange={onChange}
addOnly={addOnly}
permission={collaborator?.permission.value}
role={collaborator?.permission.role}
isChecked={!!selectedOrgList.find((v) => String(v._id) === String(org._id))}
rightSlot={
org.total && (
@@ -365,8 +363,8 @@ function MemberModal({
});
}}
isChecked={isChecked}
permission={collaborator?.permission.value}
addOnly={addOnly && !!member.permission.value}
role={collaborator?.permission.role}
addOnly={addOnly && !!member.permission.role}
orgs={member.orgs}
/>
);
@@ -392,7 +390,7 @@ function MemberModal({
name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
}
permission={collaborator?.permission.value}
role={collaborator?.permission.role}
onChange={onChange}
isChecked={!!selectedGroupList.find((v) => v._id === group._id)}
addOnly={addOnly}
@@ -425,9 +423,9 @@ function MemberModal({
</Grid>
</ModalBody>
<ModalFooter>
{!addOnly && !!permissionList && (
<PermissionSelect
value={selectedPermission}
{!addOnly && !!roleList && (
<RoleSelect
value={selectedRole}
Button={
<Flex
alignItems={'center'}
@@ -438,11 +436,11 @@ function MemberModal({
borderRadius={'md'}
h={'32px'}
>
{t(perLabel as any)}
{roleLabel}
<ChevronDownIcon fontSize={'md'} />
</Flex>
}
onChange={(v) => setSelectedPermission(v)}
onChange={(v) => setSelectedRole(v)}
/>
)}
{addOnly && (

View File

@@ -7,11 +7,12 @@ import {
Radio,
useOutsideClick,
HStack,
MenuButton
MenuButton,
Checkbox
} from '@chakra-ui/react';
import React, { useMemo, useRef, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
import type { RoleValueType } from '@fastgpt/global/support/permission/type';
import { useContextSelector } from 'use-context-selector';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { CollaboratorContext } from './context';
@@ -19,8 +20,8 @@ import { useTranslation } from 'next-i18next';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
export type PermissionSelectProps = {
value?: PermissionValueType;
onChange: (value: PermissionValueType) => void;
value?: RoleValueType;
onChange: (value: RoleValueType) => void;
trigger?: 'hover' | 'click';
offset?: [number, number];
Button: React.ReactNode;
@@ -39,8 +40,8 @@ const MenuStyle = {
fontSize: 'sm'
};
function PermissionSelect({
value,
function RoleSelect({
value: role,
onChange,
trigger = 'click',
offset = [0, 5],
@@ -49,15 +50,18 @@ function PermissionSelect({
onDelete
}: PermissionSelectProps) {
const { t } = useTranslation();
const ref = useRef<HTMLButtonElement>(null);
const ref = useRef<HTMLDivElement>(null);
const closeTimer = useRef<NodeJS.Timeout>();
const { permission, permissionList } = useContextSelector(CollaboratorContext, (v) => v);
const { permission, roleList: permissionList } = useContextSelector(
CollaboratorContext,
(v) => v
);
const [isOpen, setIsOpen] = useState(false);
const permissionSelectList = useMemo(() => {
if (!permissionList) return { singleCheckBoxList: [], multipleCheckBoxList: [] };
const roleOptions = useMemo(() => {
if (!permissionList) return { singleOptions: [], checkboxList: [] };
const list = Object.entries(permissionList).map(([_, value]) => {
return {
@@ -69,44 +73,42 @@ function PermissionSelect({
});
return {
singleCheckBoxList: list
.filter((item) => item.checkBoxType === 'single')
.filter((item) => {
if (permission.isOwner) return true;
if (item.value === permissionList['manage'].value) return false;
return true;
}),
multipleCheckBoxList: list.filter((item) => item.checkBoxType === 'multiple')
singleOptions: list.filter(
(item) =>
item.checkBoxType === 'single' &&
(permission.isOwner || item.value !== permissionList['manage'].value)
),
checkboxList: list.filter((item) => item.checkBoxType === 'multiple')
};
}, [permission.isOwner, permissionList]);
const selectedSingleValue = useMemo(() => {
if (!permissionList) return undefined;
const per = new Permission({ per: value });
const per = new Permission({ role });
if (per.hasManagePer) return permissionList['manage'].value;
if (per.hasWritePer) return permissionList['write'].value;
return permissionList['read'].value;
}, [permissionList, value]);
// const selectedMultipleValues = useMemo(() => {
// const per = new Permission({ per: value });
//
// return permissionSelectList.multipleCheckBoxList
// .filter((item) => {
// return per.checkPer(item.value);
// })
// .map((item) => item.value);
// }, [permissionSelectList.multipleCheckBoxList, value]);
}, [permissionList, role]);
const selectedMultipleValues = useMemo(() => {
const per = new Permission({ role });
const onSelectPer = (per: PermissionValueType) => {
if (per === value) return;
onChange(per);
setIsOpen(false);
return roleOptions.checkboxList
.filter((item) => {
return per.checkRole(item.value);
})
.map((item) => item.value);
}, [role, roleOptions.checkboxList]);
const onSelectRole = (newRole: RoleValueType) => {
if (newRole === role) return;
onChange(newRole);
// setIsOpen(false);
};
useOutsideClick({
ref: ref,
ref,
handler: () => {
setIsOpen(false);
}
@@ -115,6 +117,7 @@ function PermissionSelect({
return selectedSingleValue !== undefined ? (
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'}>
<Box
ref={ref}
w="fit-content"
onMouseEnter={() => {
if (trigger === 'hover') {
@@ -131,7 +134,6 @@ function PermissionSelect({
}}
>
<MenuButton
ref={ref}
position={'relative'}
onClickCapture={() => {
if (trigger === 'click') {
@@ -153,12 +155,12 @@ function PermissionSelect({
whiteSpace={'pre-wrap'}
>
{/* The list of single select permissions */}
{permissionSelectList.singleCheckBoxList.map((item) => {
{roleOptions.singleOptions.map((item) => {
const change = () => {
const per = new Permission({ per: value });
per.removePer(selectedSingleValue);
per.addPer(item.value);
onSelectPer(per.value);
const per = new Permission({ role });
per.removeRole(selectedSingleValue);
per.addRole(item.value);
onSelectRole(per.role);
};
return (
@@ -184,50 +186,45 @@ function PermissionSelect({
);
})}
{/* <MyDivider my={3} />
<MyDivider />
{multipleValues.length > 0 && <Box m="4"></Box>} */}
{roleOptions.checkboxList.length > 0 && (
<Box pb="2" px="3" fontSize={'sm'} color={'myGray.900'}>
{t('common:permission_other')}
</Box>
)}
{/* The list of multiple select permissions */}
{/* {list
.filter((item) => item.type === 'multiple')
.map((item) => {
const change = () => {
if (checkPermission(valueState, item.value)) {
setValueState(new Permission(valueState).remove(item.value).value);
} else {
setValueState(new Permission(valueState).add(item.value).value);
}
};
return (
<Flex
key={item.value}
{...(checkPermission(valueState, item.value)
? {
color: 'primary.500',
bg: 'myWhite.300'
}
: {})}
whiteSpace="pre-wrap"
flexDirection="row"
justifyContent="start"
p="2"
_hover={{
bg: 'myGray.50'
}}
>
<Checkbox
size="lg"
isChecked={checkPermission(valueState, item.value)}
onChange={change}
/>
<Flex px="4" flexDirection="column" onClick={change}>
<Box fontWeight="500">{item.name}</Box>
<Box fontWeight="400">{item.description}</Box>
</Flex>
{roleOptions.checkboxList.map((item) => {
const change = () => {
const per = new Permission({ role });
if (per.checkRole(item.value)) {
per.removeRole(item.value);
} else {
per.addRole(item.value);
}
onSelectRole(per.role);
};
const isChecked = selectedMultipleValues.includes(item.value);
return (
<Flex
key={item.value}
{...(isChecked
? {
color: 'primary.600'
}
: {})}
{...MenuStyle}
>
<Checkbox size="lg" isChecked={isChecked} onChange={change} />
<Flex px="4" flexDirection="column" onClick={change}>
<Box>{t(item.name as any)}</Box>
<Box color={'myGray.500'} fontSize={'mini'}>
{t(item.description as any)}
</Box>
</Flex>
);
})}*/}
</Flex>
);
})}
{onDelete && (
<>
<MyDivider my={2} h={'2px'} borderColor={'myGray.200'} />
@@ -235,7 +232,7 @@ function PermissionSelect({
{...MenuStyle}
onClick={() => {
onDelete();
setIsOpen(false);
// setIsOpen(false);
}}
>
<MyIcon name="delete" w="20px" color="red.600" />
@@ -249,4 +246,4 @@ function PermissionSelect({
) : null;
}
export default React.memo(PermissionSelect);
export default React.memo(RoleSelect);

View File

@@ -10,17 +10,17 @@ export type PermissionTagsProp = {
permission?: PermissionValueType;
};
function PermissionTags({ permission }: PermissionTagsProp) {
const { getPerLabelList } = useContextSelector(CollaboratorContext, (v) => v);
function RoleTags({ permission }: PermissionTagsProp) {
const { getRoleLabelList } = useContextSelector(CollaboratorContext, (v) => v);
const { t } = useTranslation();
if (permission === undefined) return null;
const perTagList = getPerLabelList(permission);
const roleTagList = getRoleLabelList(permission);
return (
<Flex gap="2" alignItems="center">
{perTagList.map((item) => (
{roleTagList.map((item) => (
<Tag
mixBlendMode={'multiply'}
key={item}
@@ -37,4 +37,4 @@ function PermissionTags({ permission }: PermissionTagsProp) {
);
}
export default PermissionTags;
export default RoleTags;

View File

@@ -3,11 +3,11 @@ import type {
CollaboratorItemType,
UpdateClbPermissionProps
} from '@fastgpt/global/support/permission/collaborator';
import { PermissionList } from '@fastgpt/global/support/permission/constant';
import { Permission } from '@fastgpt/global/support/permission/controller';
import type {
PermissionListType,
PermissionValueType
PermissionValueType,
RoleListType,
RoleValueType
} from '@fastgpt/global/support/permission/type';
import { type ReactNode, useCallback } from 'react';
import { createContext } from 'use-context-selector';
@@ -19,6 +19,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { useTranslation } from 'next-i18next';
import { CommonRoleList } from '@fastgpt/global/support/permission/constant';
const MemberModal = dynamic(() => import('./MemberModal'));
const ManageModal = dynamic(() => import('./ManageModal'));
@@ -26,7 +27,7 @@ const ManageModal = dynamic(() => import('./ManageModal'));
export type MemberManagerInputPropsType = {
permission: Permission;
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
permissionList?: PermissionListType;
roleList?: RoleListType;
onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>;
onDelOneCollaborator: (
props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }>
@@ -38,7 +39,7 @@ export type MemberManagerPropsType = MemberManagerInputPropsType & {
collaboratorList: CollaboratorItemType[];
refetchCollaboratorList: () => void;
isFetchingCollaborator: boolean;
getPerLabelList: (per: PermissionValueType) => string[];
getRoleLabelList: (role: RoleValueType) => string[];
};
export type ChildrenProps = {
onOpenAddMember: () => void;
@@ -50,14 +51,14 @@ type CollaboratorContextType = MemberManagerPropsType & {};
export const CollaboratorContext = createContext<CollaboratorContextType>({
collaboratorList: [],
permissionList: PermissionList,
roleList: CommonRoleList,
onUpdateCollaborators: () => {
throw new Error('Function not implemented.');
},
onDelOneCollaborator: () => {
throw new Error('Function not implemented.');
},
getPerLabelList: (): string[] => {
getRoleLabelList: (): string[] => {
throw new Error('Function not implemented.');
},
refetchCollaboratorList: (): void => {
@@ -73,7 +74,7 @@ export const CollaboratorContext = createContext<CollaboratorContextType>({
const CollaboratorContextProvider = ({
permission,
onGetCollaboratorList,
permissionList,
roleList,
onUpdateCollaborators,
onDelOneCollaborator,
children,
@@ -115,7 +116,7 @@ const CollaboratorContextProvider = ({
return {
...item,
permission: new Permission({
per: item.permission.value
role: item.permission.role
})
};
});
@@ -128,32 +129,32 @@ const CollaboratorContextProvider = ({
}
);
const getPerLabelList = useCallback(
(per: PermissionValueType) => {
if (!permissionList) return [];
const getRoleLabelList = useCallback(
(role: PermissionValueType) => {
if (!roleList) return [];
const Per = new Permission({ per });
const Per = new Permission({ role });
const labels: string[] = [];
if (Per.hasManagePer) {
labels.push(permissionList['manage'].name);
labels.push(t(roleList['manage'].name as any));
} else if (Per.hasWritePer) {
labels.push(permissionList['write'].name);
labels.push(t(roleList['write'].name as any));
} else if (Per.hasReadPer) {
labels.push(permissionList['read'].name);
labels.push(t(roleList['read'].name as any));
}
Object.values(permissionList).forEach((item) => {
Object.values(roleList).forEach((item) => {
if (item.checkBoxType === 'multiple') {
if (Per.checkPer(item.value)) {
labels.push(item.name);
if (Per.checkRole(item.value)) {
labels.push(t(item.name as any));
}
}
});
return labels;
},
[permissionList]
[roleList]
);
const { ConfirmModal, openConfirm } = useConfirm({});
@@ -174,10 +175,10 @@ const CollaboratorContextProvider = ({
collaboratorList,
refetchCollaboratorList,
isFetchingCollaborator,
permissionList,
roleList,
onUpdateCollaborators: onUpdateCollaboratorsThen,
onDelOneCollaborator: onDelOneCollaboratorThen,
getPerLabelList
getRoleLabelList
};
const onOpenAddMemberModal = () => {

View File

@@ -1,34 +1,34 @@
import React, { useMemo } from 'react';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { type PermissionListType } from '@fastgpt/global/support/permission/type';
import { PermissionList } from '@fastgpt/global/support/permission/constant';
import type { RoleListType } from '@fastgpt/global/support/permission/type';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import { HStack } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { CommonRoleList } from '@fastgpt/global/support/permission/constant';
const PermissionTag = ({
permission,
permissionList
roleList: roleList
}: {
permission: Permission;
permissionList: PermissionListType;
roleList: RoleListType;
}) => {
const { t } = useTranslation();
const { commonLabel, otherLabels } = useMemo(() => {
const Per = new Permission({ per: permission.value });
const Per = new Permission({ role: permission.role });
const commonLabel = (() => {
if (permission.isOwner) return t('common:permission.Owner');
if (permission.hasManagePer) return t(PermissionList['manage'].name as any);
if (permission.hasWritePer) return t(PermissionList['write'].name as any);
if (permission.hasReadPer) return t(PermissionList['read'].name as any);
if (permission.hasManagePer) return t(CommonRoleList['manage'].name as any);
if (permission.hasWritePer) return t(CommonRoleList['write'].name as any);
if (permission.hasReadPer) return t(CommonRoleList['read'].name as any);
return;
})();
const otherLabels: string[] = [];
Object.values(permissionList).forEach((item) => {
Object.values(roleList).forEach((item) => {
if (item.checkBoxType === 'multiple') {
if (Per.checkPer(item.value)) {
otherLabels.push(item.name);
@@ -45,8 +45,8 @@ const PermissionTag = ({
permission.hasReadPer,
permission.hasWritePer,
permission.isOwner,
permission.value,
permissionList,
permission.role,
roleList,
t
]);
return (

View File

@@ -1,8 +1,8 @@
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
export const processUpdateAppCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new AppPermission({ per: permissionValue });
const role = parseInt(metadata.permission, 10);
const permission = new AppPermission({ role });
return {
...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘',

View File

@@ -1,8 +1,8 @@
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
export const processUpdateDatasetCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new DatasetPermission({ per: permissionValue });
const role = parseInt(metadata.permission, 10);
const permission = new DatasetPermission({ role });
return {
...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘',

View File

@@ -1,8 +1,10 @@
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
// TODO: replace any
export const processAssignPermissionSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ per: permissionValue });
// metadata.permission is a string, parseInt will convert it to number in decimal
const role = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ role });
return {
...metadata,

View File

@@ -124,7 +124,7 @@ function GroupEditModal({
const handleToggleSelect = (memberId: string) => {
if (
myRole === 'owner' &&
memberId === groupMembers.find((item) => item.role === 'owner')?.tmbId
memberId === groupMembers.find((item) => item.groupRole === 'owner')?.tmbId
) {
toast({
title: t('user:team.group.toast.can_not_delete_owner'),

View File

@@ -28,11 +28,14 @@ import MemberTag from '../../../../components/support/user/team/Info/MemberTag';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import {
TeamApikeyCreatePermissionVal,
TeamApikeyCreateRoleVal,
TeamAppCreatePermissionVal,
TeamAppCreateRoleVal,
TeamDatasetCreatePermissionVal,
TeamDatasetCreateRoleVal,
TeamManagePermissionVal,
TeamPermissionList,
TeamWritePermissionVal
TeamManageRoleVal,
TeamRoleList
} from '@fastgpt/global/support/permission/user/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { useToggle } from 'ahooks';
@@ -117,18 +120,18 @@ function PermissionManage({
if (!clb) return;
const permission = new TeamPermission({ per: clb.permission.value });
const permission = new TeamPermission({ role: clb.permission.role });
if (type === 'add') {
permission.addPer(per);
permission.addRole(per);
} else {
permission.removePer(per);
permission.removeRole(per);
}
return onUpdateCollaborators({
...(clb.tmbId && { members: [clb.tmbId] }),
...(clb.groupId && { groups: [clb.groupId] }),
...(clb.orgId && { orgs: [clb.orgId] }),
permission: permission.value
permission: permission.role
});
}
);
@@ -145,12 +148,12 @@ function PermissionManage({
function PermissionCheckBox({
isDisabled,
per,
role,
clbPer,
id
}: {
isDisabled: boolean;
per: PermissionValueType;
role: PermissionValueType;
clbPer: Permission;
id: string;
}) {
@@ -159,18 +162,18 @@ function PermissionManage({
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={isDisabled}
isChecked={clbPer.checkPer(per)}
isChecked={clbPer.checkRole(role)}
onChange={(e) =>
e.target.checked
? onUpdatePermission({
id,
type: 'add',
per
per: role
})
: onUpdatePermission({
id,
type: 'remove',
per
per: role
})
}
/>
@@ -266,25 +269,25 @@ function PermissionManage({
</Td>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal}
role={TeamAppCreateRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal}
role={TeamDatasetCreateRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal}
role={TeamApikeyCreateRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal}
role={TeamManageRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
@@ -323,25 +326,25 @@ function PermissionManage({
</Td>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal}
role={TeamAppCreatePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal}
role={TeamDatasetCreatePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal}
role={TeamApikeyCreatePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal}
role={TeamManagePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
@@ -385,25 +388,25 @@ function PermissionManage({
</Td>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal}
role={TeamAppCreatePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal}
role={TeamDatasetCreatePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal}
role={TeamApikeyCreatePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal}
role={TeamManagePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
@@ -434,7 +437,7 @@ export const Render = ({ Tabs }: { Tabs: React.ReactNode }) => {
return userInfo?.team ? (
<CollaboratorContextProvider
permission={userInfo?.team.permission}
permissionList={TeamPermissionList}
roleList={TeamRoleList}
onGetCollaboratorList={getTeamClbs}
onUpdateCollaborators={updateMemberPermission}
onDelOneCollaborator={deleteMemberPermission}

View File

@@ -20,7 +20,7 @@ import {
} from '@chakra-ui/react';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import type { AppSchema } from '@fastgpt/global/core/app/type.d';
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
import { AppRoleList } from '@fastgpt/global/support/permission/app/constant';
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -187,7 +187,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
<CollaboratorContextProvider
permission={appDetail.permission}
onGetCollaboratorList={() => getCollaboratorList(appDetail._id)}
permissionList={AppPermissionList}
roleList={AppRoleList}
onUpdateCollaborators={async (props) =>
onUpdateCollaborators({
permission: props.permission,

View File

@@ -36,7 +36,9 @@ const RouteTab = () => {
label: t('app:publish_channel'),
id: TabEnum.publish
},
{ label: t('app:chat_logs'), id: TabEnum.logs }
...(appDetail.permission.hasReadChatLogPer
? [{ label: t('app:chat_logs'), id: TabEnum.logs }]
: [])
]
: [])
],

View File

@@ -17,7 +17,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import dynamic from 'next/dynamic';
import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
import MyMenu, { type MenuItemType } from '@fastgpt/web/components/common/MyMenu';
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
import { AppRoleList } from '@fastgpt/global/support/permission/app/constant';
import {
deleteAppCollaborators,
getCollaboratorList,
@@ -427,7 +427,7 @@ const ListItem = () => {
managePer={{
permission: editPerApp.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerApp._id),
permissionList: AppPermissionList,
roleList: AppRoleList,
onUpdateCollaborators: (props: {
members?: string[];
groups?: string[];

View File

@@ -17,7 +17,7 @@ import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/co
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
import { DatasetRoleList } from '@fastgpt/global/support/permission/dataset/constant';
import MemberManager from '../../MemberManager';
import {
getCollaboratorList,
@@ -382,7 +382,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
managePer={{
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,
roleList: DatasetRoleList,
onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({
...body,

View File

@@ -17,7 +17,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import dynamic from 'next/dynamic';
import { useContextSelector } from 'use-context-selector';
import { DatasetsContext } from '../../../pages/dataset/list/context';
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
import { DatasetRoleList } from '@fastgpt/global/support/permission/dataset/constant';
import ConfigPerModal from '@/components/support/permission/ConfigPerModal';
import {
deleteDatasetCollaborators,
@@ -436,7 +436,7 @@ function List() {
managePer={{
permission: editPerDataset.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id),
permissionList: DatasetPermissionList,
roleList: DatasetRoleList,
onUpdateCollaborators: (props) =>
postUpdateDatasetCollaborators({
...props,

View File

@@ -0,0 +1,39 @@
import { NextAPI } from '@/service/middleware/entry';
import { AppReadChatLogRoleVal } from '@fastgpt/global/support/permission/app/constant';
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
import type { AnyBulkWriteOperation } from '@fastgpt/service/common/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { type NextApiRequest, type NextApiResponse } from 'next';
async function handler(req: NextApiRequest, _res: NextApiResponse) {
await authCert({ req, authRoot: true });
// 初始化 app 权限:所有有 write 的都加上 readChatLog role
const rps = await MongoResourcePermission.find({
resourceType: 'app'
}).lean();
const ops: AnyBulkWriteOperation<typeof MongoResourcePermission>[] = [];
for (const rp of rps) {
const per = new AppPermission({ role: rp.permission });
if (per.hasWritePer) {
per.addRole(AppReadChatLogRoleVal);
ops.push({
updateOne: {
filter: { _id: rp._id },
update: { $set: { permission: per.role } }
}
});
}
}
const result = await MongoResourcePermission.bulkWrite(ops);
return {
success: true,
result
};
}
export default NextAPI(handler);

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
{
$set: {
defaultPermission: DatasetDefaultPermissionVal
defaultPermission: DataSetDefaultRoleVal
}
}
);

View File

@@ -22,15 +22,15 @@ async function handler(req: NextApiRequest, _res: NextApiResponse) {
});
for await (const rp of rps) {
const per = new TeamPermission({ per: rp.permission });
console.log(per.hasWritePer, per.value);
if (per.hasWritePer) {
const newPer = per.addPer(
TeamAppCreatePermissionVal,
TeamDatasetCreatePermissionVal,
TeamApikeyCreatePermissionVal
);
rp.permission = newPer.value;
const per = rp.permission;
console.log(per);
if (per & 0b010) {
// has 0b010
rp.permission =
per |
TeamAppCreatePermissionVal |
TeamDatasetCreatePermissionVal |
TeamApikeyCreatePermissionVal;
try {
await retryFn(async () => {

View File

@@ -1,7 +1,6 @@
import type { NextApiResponse } from 'next';
import { responseWriteController } from '@fastgpt/service/common/response';
import { addDays } from 'date-fns';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { addLog } from '@fastgpt/service/common/system/log';
import dayjs from 'dayjs';
@@ -19,6 +18,7 @@ import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { sanitizeCsvField } from '@fastgpt/service/common/file/csv';
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
const formatJsonString = (data: any) => {
if (data == null) return '';
@@ -47,7 +47,7 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
throw new Error('缺少参数');
}
const { teamId } = await authApp({ req, authToken: true, appId, per: WritePermissionVal });
const { teamId } = await authApp({ req, authToken: true, appId, per: AppReadChatLogPerVal });
const teamMemberWithContact = await MongoTeamMember.aggregate([
{ $match: { teamId: new Types.ObjectId(teamId) } },

View File

@@ -7,7 +7,6 @@ import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { type PaginationResponse } from '@fastgpt/web/common/fetch/type';
@@ -16,6 +15,7 @@ import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
async function handler(
req: NextApiRequest,
@@ -41,7 +41,7 @@ async function handler(
req,
authToken: true,
appId,
per: WritePermissionVal
per: AppReadChatLogPerVal
});
const where = {

View File

@@ -11,15 +11,15 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
import { AppDefaultRoleVal } from '@fastgpt/global/support/permission/app/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { sumPer } from '@fastgpt/global/support/permission/utils';
export type ListAppBody = {
parentId?: ParentIdType;
@@ -63,7 +63,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
]);
// Get team all app permissions
const [perList, myGroupMap, myOrgSet] = await Promise.all([
const [roleList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.app,
teamId,
@@ -87,7 +87,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
})
]);
// Get my permissions
const myPerList = perList.filter(
const myPerList = roleList.filter(
(item) =>
String(item.tmbId) === String(tmbId) ||
myGroupMap.has(String(item.groupId)) ||
@@ -160,11 +160,11 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
.map((app) => {
const { Per, privateApp } = (() => {
const getPer = (appId: string) => {
const tmbPer = myPerList.find(
const tmbRole = myPerList.find(
(item) => String(item.resourceId) === appId && !!item.tmbId
)?.permission;
const groupPer = concatPer(
myPerList
const groupRole = sumPer(
...myPerList
.filter(
(item) => String(item.resourceId) === appId && (!!item.groupId || !!item.orgId)
)
@@ -172,13 +172,13 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
);
return new AppPermission({
per: tmbPer ?? groupPer ?? AppDefaultPermissionVal,
role: tmbRole ?? groupRole,
isOwner: String(app.tmbId) === String(tmbId) || teamPer.isOwner
});
};
const getClbCount = (appId: string) => {
return perList.filter((item) => String(item.resourceId) === String(appId)).length;
return roleList.filter((item) => String(item.resourceId) === String(appId)).length;
};
// Inherit app, check parent folder clb

View File

@@ -8,17 +8,17 @@ import {
ReadPermissionVal
} from '@fastgpt/global/support/permission/constant';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { getEmbeddingModel } from '@fastgpt/service/core/ai/model';
import { sumPer } from '@fastgpt/global/support/permission/utils';
export type GetDatasetListBody = {
parentId: ParentIdType;
@@ -51,7 +51,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
]);
// Get team all app permissions
const [perList, myGroupMap, myOrgSet] = await Promise.all([
const [roleList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.dataset,
teamId,
@@ -74,7 +74,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
tmbId
})
]);
const myPerList = perList.filter(
const myRoles = roleList.filter(
(item) =>
String(item.tmbId) === String(tmbId) ||
myGroupMap.has(String(item.groupId)) ||
@@ -83,7 +83,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
const findDatasetQuery = (() => {
// Filter apps by permission, if not owner, only get apps that I have permission to access
const idList = { _id: { $in: myPerList.map((item) => item.resourceId) } };
const idList = { _id: { $in: myRoles.map((item) => item.resourceId) } };
const datasetPerQuery = teamPer.isOwner
? {}
: parentId
@@ -127,23 +127,23 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
.map((dataset) => {
const { Per, privateDataset } = (() => {
const getPer = (datasetId: string) => {
const tmbPer = myPerList.find(
const tmbRole = myRoles.find(
(item) => String(item.resourceId) === datasetId && !!item.tmbId
)?.permission;
const groupPer = concatPer(
myPerList
const groupRole = sumPer(
...myRoles
.filter(
(item) => String(item.resourceId) === datasetId && (!!item.groupId || !!item.orgId)
)
.map((item) => item.permission)
);
return new DatasetPermission({
per: tmbPer ?? groupPer ?? DatasetDefaultPermissionVal,
role: tmbRole ?? groupRole,
isOwner: String(dataset.tmbId) === String(tmbId) || teamPer.isOwner
});
};
const getClbCount = (datasetId: string) => {
return perList.filter((item) => String(item.resourceId) === String(datasetId)).length;
return roleList.filter((item) => String(item.resourceId) === String(datasetId)).length;
};
// inherit

View File

@@ -17,7 +17,7 @@ import FolderPath from '@/components/common/folder/Path';
import { useRouter } from 'next/router';
import FolderSlideCard from '@/components/common/folder/SlideCard';
import { delAppById, resumeInheritPer } from '@/web/core/app/api';
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
import { AppRoleList } from '@fastgpt/global/support/permission/app/constant';
import {
deleteAppCollaborators,
getCollaboratorList,
@@ -282,7 +282,7 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
managePer={{
permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: AppPermissionList,
roleList: AppRoleList,
onUpdateCollaborators: (props) =>
postUpdateAppCollaborators({
...props,

View File

@@ -18,7 +18,7 @@ import { type EditFolderFormType } from '@fastgpt/web/components/common/MyModal/
import dynamic from 'next/dynamic';
import { postCreateDatasetFolder, resumeInheritPer } from '@/web/core/dataset/api';
import FolderSlideCard from '@/components/common/folder/SlideCard';
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
import { DatasetRoleList } from '@fastgpt/global/support/permission/dataset/constant';
import {
postUpdateDatasetCollaborators,
deleteDatasetCollaborators,
@@ -256,7 +256,7 @@ const Dataset = () => {
managePer={{
permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: DatasetPermissionList,
roleList: DatasetRoleList,
onUpdateCollaborators: (params) =>
postUpdateDatasetCollaborators({
...params,

View File

@@ -0,0 +1,28 @@
import { CommonPerList, CommonRoleList } from '@fastgpt/global/support/permission/constant';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { sumPer } from '@fastgpt/global/support/permission/utils';
import { describe, expect, it } from 'vitest';
describe('Permission Helper Class Test', () => {
it('Permission Helper Class Test', () => {
const permission = new Permission();
expect(permission.role).toBe(0);
permission.addRole(CommonRoleList.manage.value);
expect(permission.role).toBe(CommonRoleList.manage.value);
expect(permission.checkPer(CommonPerList.manage)).toBe(true);
permission.removeRole(CommonRoleList.read.value);
expect(permission.checkPer(CommonPerList.manage)).toBe(true);
});
});
describe('Tool Functions', () => {
it('sumPer', () => {
expect(sumPer(0b001, 0b010)).toBe(0b011);
expect(sumPer(0b000, 0b000)).toBe(0b000);
expect(sumPer(0b100, 0b001)).toBe(0b101);
expect(sumPer(0b111, 0b010)).toBe(0b111);
expect(sumPer(sumPer(0b001, 0b010), 0b100)).toBe(0b111);
expect(sumPer(0b10000000, 0b01000000)).toBe(0b11000000);
});
});