dify/web/utils/permission.ts
Wu Tianwei 33edf97f81
feat: RBAC (#37107)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: 盐粒 Yanli <yanli@dify.ai>
Co-authored-by: Charles Yao <chongbinyao33@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: yunlu.wen <yunlu.wen@dify.ai>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: Jingyi <jingyi.qi@dify.ai>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
Co-authored-by: gigglewang <gigglewang@dify.ai>
Co-authored-by: chariri <w@chariri.moe>
Co-authored-by: Evan <2869018789@qq.com>
Co-authored-by: zyssyz123 <916125788@qq.com>
2026-06-18 16:35:29 +00:00

158 lines
6.4 KiB
TypeScript

import type { PermissionKey } from '@/models/access-control'
import { DatasetPermission } from '@/models/datasets'
export const AppACLPermission = {
ViewLayout: 'app.acl.view_layout',
TestAndRun: 'app.acl.test_and_run',
Edit: 'app.acl.edit',
ImportExportDSL: 'app.acl.import_export_dsl',
Delete: 'app.acl.delete',
ReleaseAndVersion: 'app.acl.release_and_version',
Monitor: 'app.acl.monitor',
AccessConfig: 'app.acl.access_config',
} as const
export const DatasetACLPermission = {
Readonly: 'dataset.acl.readonly',
Edit: 'dataset.acl.edit',
ImportExportDSL: 'dataset.acl.import_export_dsl',
PipelineTest: 'dataset.acl.pipeline_test',
DocumentDownload: 'dataset.acl.document_download',
RetrievalRecall: 'dataset.acl.retrieval_recall',
Use: 'dataset.acl.use',
DeleteFile: 'dataset.acl.delete_file',
PipelineRelease: 'dataset.acl.pipeline_release',
Delete: 'dataset.acl.delete',
AccessConfig: 'dataset.acl.access_config',
} as const
export const BillingPermission = {
View: 'billing.view',
Manage: 'billing.manage',
SubscriptionManage: 'billing.subscription.manage',
} as const
export type ResourceMaintainerPermissionOptions = {
currentUserId?: string | null
resourceMaintainer?: string | null
workspacePermissionKeys?: readonly PermissionKey[] | null
}
type AppACLCapabilities = {
canViewLayout: boolean
canTestAndRun: boolean
canEdit: boolean
canAccessLayout: boolean
canComment: boolean
canPreviewApp: boolean
canImportExportDSL: boolean
canDelete: boolean
canReleaseAndVersion: boolean
canMonitor: boolean
canAccessConfig: boolean
}
type DatasetACLCapabilities = {
canReadonly: boolean
canEdit: boolean
canImportExportDSL: boolean
canPipelineTest: boolean
canDocumentDownload: boolean
canRetrievalRecall: boolean
canUse: boolean
canDeleteFile: boolean
canPipelineRelease: boolean
canDelete: boolean
canAccessConfig: boolean
}
type DatasetConfig = {
createdBy: string
partialMemberList: string[]
permission: DatasetPermission
}
export const hasEditPermissionForDataset = (userId: string, datasetConfig: DatasetConfig) => {
const { createdBy, partialMemberList, permission } = datasetConfig
if (permission === DatasetPermission.onlyMe)
return userId === createdBy
if (permission === DatasetPermission.allTeamMembers)
return true
if (permission === DatasetPermission.partialMembers)
return partialMemberList.includes(userId)
return false
}
export const hasPermission = (permissionKeys: readonly PermissionKey[] | null | undefined, permissionKeySet: PermissionKey | PermissionKey[]) => {
if (!permissionKeys)
return false
if (Array.isArray(permissionKeySet)) {
return permissionKeySet.some(key => permissionKeys.includes(key))
}
const singlePermissionKey = permissionKeySet
return permissionKeys.includes(singlePermissionKey)
}
const shouldGrantMaintainerPermissions = (
options: ResourceMaintainerPermissionOptions | undefined,
createPermissionKey: PermissionKey,
) => {
if (!options?.currentUserId || !options?.resourceMaintainer)
return false
return options.currentUserId === options.resourceMaintainer
&& hasPermission(options.workspacePermissionKeys, createPermissionKey)
}
const hasResourcePermission = (
permissionKeys: readonly PermissionKey[] | null | undefined,
permissionKey: PermissionKey,
hasMaintainerPermissions: boolean,
) => hasMaintainerPermissions || hasPermission(permissionKeys, permissionKey)
export const getAppACLCapabilities = (
permissionKeys: readonly PermissionKey[] | null | undefined,
options?: ResourceMaintainerPermissionOptions,
): AppACLCapabilities => {
const hasMaintainerPermissions = shouldGrantMaintainerPermissions(options, 'app.create_and_management')
const canViewLayout = hasResourcePermission(permissionKeys, AppACLPermission.ViewLayout, hasMaintainerPermissions)
const canTestAndRun = hasResourcePermission(permissionKeys, AppACLPermission.TestAndRun, hasMaintainerPermissions)
const canEdit = hasResourcePermission(permissionKeys, AppACLPermission.Edit, hasMaintainerPermissions)
return {
canViewLayout,
canTestAndRun,
canEdit,
canAccessLayout: canViewLayout || canTestAndRun || canEdit,
canComment: canViewLayout || canEdit,
canPreviewApp: canViewLayout || canTestAndRun,
canImportExportDSL: hasResourcePermission(permissionKeys, AppACLPermission.ImportExportDSL, hasMaintainerPermissions),
canDelete: hasResourcePermission(permissionKeys, AppACLPermission.Delete, hasMaintainerPermissions),
canReleaseAndVersion: hasResourcePermission(permissionKeys, AppACLPermission.ReleaseAndVersion, hasMaintainerPermissions),
canMonitor: hasResourcePermission(permissionKeys, AppACLPermission.Monitor, hasMaintainerPermissions),
canAccessConfig: hasResourcePermission(permissionKeys, AppACLPermission.AccessConfig, hasMaintainerPermissions),
}
}
export const getDatasetACLCapabilities = (
permissionKeys: readonly PermissionKey[] | null | undefined,
options?: ResourceMaintainerPermissionOptions,
): DatasetACLCapabilities => {
const hasMaintainerPermissions = shouldGrantMaintainerPermissions(options, 'dataset.create_and_management')
return {
canReadonly: hasResourcePermission(permissionKeys, DatasetACLPermission.Readonly, hasMaintainerPermissions),
canEdit: hasResourcePermission(permissionKeys, DatasetACLPermission.Edit, hasMaintainerPermissions),
canImportExportDSL: hasResourcePermission(permissionKeys, DatasetACLPermission.ImportExportDSL, hasMaintainerPermissions),
canPipelineTest: hasResourcePermission(permissionKeys, DatasetACLPermission.PipelineTest, hasMaintainerPermissions),
canDocumentDownload: hasResourcePermission(permissionKeys, DatasetACLPermission.DocumentDownload, hasMaintainerPermissions),
canRetrievalRecall: hasResourcePermission(permissionKeys, DatasetACLPermission.RetrievalRecall, hasMaintainerPermissions),
canUse: hasResourcePermission(permissionKeys, DatasetACLPermission.Use, hasMaintainerPermissions),
canDeleteFile: hasResourcePermission(permissionKeys, DatasetACLPermission.DeleteFile, hasMaintainerPermissions),
canPipelineRelease: hasResourcePermission(permissionKeys, DatasetACLPermission.PipelineRelease, hasMaintainerPermissions),
canDelete: hasResourcePermission(permissionKeys, DatasetACLPermission.Delete, hasMaintainerPermissions),
canAccessConfig: hasResourcePermission(permissionKeys, DatasetACLPermission.AccessConfig, hasMaintainerPermissions),
}
}