From 65ad573ed6d1bfe5ff98554ecf5d461f2fc8d4c2 Mon Sep 17 00:00:00 2001 From: twwu Date: Thu, 21 May 2026 17:21:04 +0800 Subject: [PATCH] feat: enhance access rule management with view functionality and permission modal updates --- .../components/access-rules-editor/index.tsx | 3 ++ .../components/app/access-config/index.tsx | 10 +++- .../datasets/access-config/index.tsx | 10 +++- .../datasets/documents/components/list.tsx | 2 +- .../access-rule-row-menu.tsx | 51 ++++++++++++++----- .../access-rules-page/access-rule-row.tsx | 4 ++ .../access-rules-page/access-rule-section.tsx | 3 ++ .../app-access-rule-section.tsx | 13 +++++ .../dataset-access-rule-section.tsx | 13 +++++ .../permission-set-modal/index.tsx | 30 +++++++---- 10 files changed, 111 insertions(+), 28 deletions(-) diff --git a/web/app/components/access-rules-editor/index.tsx b/web/app/components/access-rules-editor/index.tsx index 1e1249ccab..63bda79447 100644 --- a/web/app/components/access-rules-editor/index.tsx +++ b/web/app/components/access-rules-editor/index.tsx @@ -12,11 +12,13 @@ import { useUpdateDatasetAccessRuleBindings } from '@/service/access-control/use export type AccessRulesEditorProps = { rules: AccessPolicyWithBindings[] + canManage: boolean className?: string } const AccessRulesEditor = ({ rules, + canManage, className, }: AccessRulesEditorProps) => { const { appId } = useParams() as { appId: string } @@ -102,6 +104,7 @@ const AccessRulesEditor = ({ { const { data: appAccessRulesResponse } = useAppAccessRules(appId) + const appPermissionKeys = useAppStore(state => state.appDetail?.permission_keys) + const appACLCapabilities = useMemo( + () => getAppACLCapabilities(appPermissionKeys), + [appPermissionKeys], + ) const appAccessRules = appAccessRulesResponse?.items || [] @@ -21,7 +29,7 @@ const AppAccessConfigPage = ({ appId }: AppAccessConfigPageProps) => {

Access Config

- +
diff --git a/web/app/components/datasets/access-config/index.tsx b/web/app/components/datasets/access-config/index.tsx index 34786dbd4e..d9516234b2 100644 --- a/web/app/components/datasets/access-config/index.tsx +++ b/web/app/components/datasets/access-config/index.tsx @@ -1,8 +1,11 @@ 'use client' import { ScrollArea } from '@langgenius/dify-ui/scroll-area' +import { useMemo } from 'react' import AccessRulesEditor from '@/app/components/access-rules-editor' +import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useDatasetAccessRules } from '@/service/access-control/use-dataset-access-config' +import { getDatasetACLCapabilities } from '@/utils/permission' type DatasetAccessConfigPageProps = { datasetId: string @@ -10,6 +13,11 @@ type DatasetAccessConfigPageProps = { const DatasetAccessConfigPage = ({ datasetId }: DatasetAccessConfigPageProps) => { const { data: datasetAccessRulesResponse } = useDatasetAccessRules(datasetId) + const datasetPermissionKeys = useDatasetDetailContextWithSelector(state => state.dataset?.permission_keys) + const datasetACLCapabilities = useMemo( + () => getDatasetACLCapabilities(datasetPermissionKeys), + [datasetPermissionKeys], + ) const datasetAccessRules = datasetAccessRulesResponse?.items || [] @@ -21,7 +29,7 @@ const DatasetAccessConfigPage = ({ datasetId }: DatasetAccessConfigPageProps) =>

Access Config

- +
diff --git a/web/app/components/datasets/documents/components/list.tsx b/web/app/components/datasets/documents/components/list.tsx index eb9498ed21..b4382646e1 100644 --- a/web/app/components/datasets/documents/components/list.tsx +++ b/web/app/components/datasets/documents/components/list.tsx @@ -50,7 +50,7 @@ const DocumentList = ({ }: DocumentListProps) => { const { t } = useTranslation() const datasetConfig = useDatasetDetailContext(s => s.dataset) - const datasetACLCapabilities = React.useMemo(() => getDatasetACLCapabilities(datasetConfig?.permission_keys), [datasetConfig?.permission_keys]) + const datasetACLCapabilities = useMemo(() => getDatasetACLCapabilities(datasetConfig?.permission_keys), [datasetConfig?.permission_keys]) const chunkingMode = datasetConfig?.doc_form const isGeneralMode = chunkingMode !== ChunkingMode.parentChild const isQAMode = chunkingMode === ChunkingMode.qa diff --git a/web/app/components/header/account-setting/access-rules-page/access-rule-row-menu.tsx b/web/app/components/header/account-setting/access-rules-page/access-rule-row-menu.tsx index b39e644eb9..b4978a560e 100644 --- a/web/app/components/header/account-setting/access-rules-page/access-rule-row-menu.tsx +++ b/web/app/components/header/account-setting/access-rules-page/access-rule-row-menu.tsx @@ -24,11 +24,13 @@ import { useCopyAccessRule, useDeleteAccessRule } from '@/service/access-control export type AccessRuleRowMenuProps = { rule: AccessPolicy + onView?: () => void onEdit?: () => void } const AccessRuleRowMenu = ({ rule, + onView, onEdit, }: AccessRuleRowMenuProps) => { const [open, setOpen] = useState(false) @@ -37,6 +39,10 @@ const AccessRuleRowMenu = ({ const { mutateAsync: copyAccessRule } = useCopyAccessRule(rule.resource_type) const { mutateAsync: deleteAccessRule, isPending: isDeletingAccessRule } = useDeleteAccessRule(rule.resource_type) + const handleView = useCallback(() => { + onView?.() + }, [onView]) + const handleCopyRules = useCallback(() => { copyAccessRule(rule.id, { onSuccess: () => { @@ -60,6 +66,8 @@ const AccessRuleRowMenu = ({ }) }, [deleteAccessRule, rule.id]) + const isBuiltIn = rule.is_builtin + return ( <> @@ -79,26 +87,41 @@ const AccessRuleRowMenu = ({ sideOffset={4} popupClassName="min-w-[140px]" > - - Edit - + {isBuiltIn + ? ( + + View + + ) + : ( + + Edit + + )} Copy - - - Delete - + {!isBuiltIn && ( + <> + + + Delete + + + )} !open && setShowDeleteConfirm(false)}> diff --git a/web/app/components/header/account-setting/access-rules-page/access-rule-row.tsx b/web/app/components/header/account-setting/access-rules-page/access-rule-row.tsx index 6a72573fd6..5015240cb0 100644 --- a/web/app/components/header/account-setting/access-rules-page/access-rule-row.tsx +++ b/web/app/components/header/account-setting/access-rules-page/access-rule-row.tsx @@ -11,6 +11,7 @@ export type AccessRuleRowProps = { canManage: boolean className?: string showMenu?: boolean + onView?: (rule: AccessPolicyWithBindings) => void onEdit?: (rule: AccessPolicyWithBindings) => void onAddRole?: (rule: AccessPolicyWithBindings) => void onRemove?: (payload: RemoveBindingPayload) => void @@ -21,6 +22,7 @@ const AccessRuleRow = ({ canManage, className, showMenu = true, + onView, onEdit, onAddRole, onRemove, @@ -28,6 +30,7 @@ const AccessRuleRow = ({ const { policy, roles, accounts } = rule const { id: policyId, resource_type } = policy + const handleView = useCallback(() => onView?.(rule), [onView, rule]) const handleEdit = useCallback(() => onEdit?.(rule), [onEdit, rule]) const handleAddRole = useCallback(() => onAddRole?.(rule), [onAddRole, rule]) @@ -95,6 +98,7 @@ const AccessRuleRow = ({ {showMenu && canManage && ( diff --git a/web/app/components/header/account-setting/access-rules-page/access-rule-section.tsx b/web/app/components/header/account-setting/access-rules-page/access-rule-section.tsx index 9a30e32577..e1fc8f24af 100644 --- a/web/app/components/header/account-setting/access-rules-page/access-rule-section.tsx +++ b/web/app/components/header/account-setting/access-rules-page/access-rule-section.tsx @@ -13,6 +13,7 @@ type AccessRuleSectionProps = { rules: AccessPolicyWithBindings[] isLoadingRules: boolean onCreate?: () => void + onViewRule?: (rule: AccessPolicyWithBindings) => void onEditRule?: (rule: AccessPolicyWithBindings) => void onAddRole?: (rule: AccessPolicyWithBindings) => void onRemoveBinding?: (payload: RemoveBindingPayload) => void @@ -24,6 +25,7 @@ const AccessRuleSection = ({ rules, isLoadingRules, onCreate, + onViewRule, onEditRule, onAddRole, onRemoveBinding, @@ -57,6 +59,7 @@ const AccessRuleSection = ({ rule={rule} canManage={canManage} className={cn(index > 0 && 'border-t border-divider-subtle')} + onView={onViewRule} onEdit={onEditRule} onAddRole={onAddRole} onRemove={onRemoveBinding} diff --git a/web/app/components/header/account-setting/access-rules-page/app-access-rule-section.tsx b/web/app/components/header/account-setting/access-rules-page/app-access-rule-section.tsx index 6e4d7e1bdb..b6f2bfa950 100644 --- a/web/app/components/header/account-setting/access-rules-page/app-access-rule-section.tsx +++ b/web/app/components/header/account-setting/access-rules-page/app-access-rule-section.tsx @@ -50,6 +50,18 @@ const AppAccessRuleSection = ({ setPermissionSetModalState({ mode: 'create' }) }, []) + const handleView = useCallback((rule: AccessPolicyWithBindings) => { + const { policy } = rule + setPermissionSetModalState({ + mode: 'view', + initialValues: { + name: policy.name, + description: policy.description, + permissionKeys: policy.permission_keys, + }, + }) + }, []) + const handleEdit = useCallback((rule: AccessPolicyWithBindings) => { const { policy } = rule setPermissionSetModalState({ @@ -136,6 +148,7 @@ const AppAccessRuleSection = ({ rules={appAccessRules} isLoadingRules={isLoading} onCreate={handleCreate} + onViewRule={handleView} onEditRule={handleEdit} onAddRole={handleAddRole} onRemoveBinding={handleRemoveBinding} diff --git a/web/app/components/header/account-setting/access-rules-page/dataset-access-rule-section.tsx b/web/app/components/header/account-setting/access-rules-page/dataset-access-rule-section.tsx index 4c6f51279c..47891abba0 100644 --- a/web/app/components/header/account-setting/access-rules-page/dataset-access-rule-section.tsx +++ b/web/app/components/header/account-setting/access-rules-page/dataset-access-rule-section.tsx @@ -50,6 +50,18 @@ const DatasetAccessRuleSection = ({ setPermissionSetModalState({ mode: 'create' }) }, []) + const handleView = useCallback((rule: AccessPolicyWithBindings) => { + const { policy } = rule + setPermissionSetModalState({ + mode: 'view', + initialValues: { + name: policy.name, + description: policy.description, + permissionKeys: policy.permission_keys, + }, + }) + }, []) + const handleEdit = useCallback((rule: AccessPolicyWithBindings) => { const { policy } = rule setPermissionSetModalState({ @@ -136,6 +148,7 @@ const DatasetAccessRuleSection = ({ rules={datasetAccessRules} isLoadingRules={isLoading} onCreate={handleCreate} + onViewRule={handleView} onEditRule={handleEdit} onAddRole={handleAddRole} onRemoveBinding={handleRemoveBinding} diff --git a/web/app/components/header/account-setting/access-rules-page/permission-set-modal/index.tsx b/web/app/components/header/account-setting/access-rules-page/permission-set-modal/index.tsx index e87a211e97..cd49638f15 100644 --- a/web/app/components/header/account-setting/access-rules-page/permission-set-modal/index.tsx +++ b/web/app/components/header/account-setting/access-rules-page/permission-set-modal/index.tsx @@ -14,7 +14,7 @@ import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' import PermissionPicker from './permission-picker' -export type PermissionSetModalMode = 'create' | 'edit' +export type PermissionSetModalMode = 'create' | 'edit' | 'view' export type PermissionSetFormValues = { name: string @@ -37,11 +37,13 @@ const RESOURCE_LABEL: Record = { } const buildTitle = (mode: PermissionSetModalMode, resource: AccessPolicyResourceType): string => { - const verb = mode === 'create' ? 'Create' : 'Edit' + const verb = mode === 'create' ? 'Create' : mode === 'edit' ? 'Edit' : 'View' return `${verb} ${RESOURCE_LABEL[resource]} permission set` } const buildDescription = (mode: PermissionSetModalMode, resource: AccessPolicyResourceType): string => { + if (mode === 'view') + return 'View the name, description, and permissions granted for this permission set.' if (mode === 'edit') return 'Modify the name, description, and permissions granted for this permission set.' if (resource === 'app') @@ -63,10 +65,11 @@ const PermissionSetModalBody = ({ const [permissionKeys, setPermissionKeys] = useState(initialValues?.permissionKeys ?? []) const trimmedName = name.trim() + const readonly = mode === 'view' const canSubmit = trimmedName.length > 0 const handleConfirm = () => { - if (!canSubmit) + if (readonly || !canSubmit) return onSubmit({ name: trimmedName, @@ -106,6 +109,7 @@ const PermissionSetModalBody = ({ value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Can export DSL" + disabled={readonly} /> @@ -119,6 +123,7 @@ const PermissionSetModalBody = ({ onChange={e => setDescription(e.target.value)} placeholder="Describe what this permission set grants" className="min-h-20 resize-none" + disabled={readonly} /> @@ -128,6 +133,7 @@ const PermissionSetModalBody = ({ resourceType={resourceType} value={permissionKeys} onChange={setPermissionKeys} + readonly={readonly} /> @@ -144,15 +150,17 @@ const PermissionSetModalBody = ({
- + {!readonly && ( + + )}