feat: enhance access rule management with account bindings and role handling

This commit is contained in:
twwu 2026-05-11 17:15:59 +08:00
parent e44252c242
commit d878a29c43
6 changed files with 64 additions and 33 deletions

View File

@ -1,10 +1,13 @@
'use client'
import type { AccessPolicyWithBindings } from '@/models/access-control'
import type { AccessPolicyWithBindings, BindingType } from '@/models/access-control'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { memo, useCallback } from 'react'
import { useUpdateAppAccessRuleBindings, useUpdateDatasetAccessRuleBindings } from '@/service/access-control/use-workspace-access-rules'
import {
useUpdateAppAccessRuleBindings,
useUpdateDatasetAccessRuleBindings,
} from '@/service/access-control/use-workspace-access-rules'
import AccessRuleRowMenu from './access-rule-row-menu'
import RoleTag from './role-tag'
@ -23,7 +26,8 @@ const AccessRuleRow = ({
onEdit,
onAddRole,
}: AccessRuleRowProps) => {
const { policy, role_ids } = rule
const { policy, role_ids, account_ids } = rule
const { id: policyId, resource_type } = policy
const handleEdit = useCallback(() => onEdit?.(rule), [onEdit, rule])
const handleAddRole = useCallback(() => onAddRole?.(rule), [onAddRole, rule])
@ -31,27 +35,33 @@ const AccessRuleRow = ({
const { mutateAsync: updateAppAccessRuleBindings } = useUpdateAppAccessRuleBindings()
const { mutateAsync: updateDatasetAccessRuleBindings } = useUpdateDatasetAccessRuleBindings()
const handleRemoveRole = useCallback((roleId: string) => {
const handleRemoveRole = useCallback((id: string, type: BindingType) => {
const payload = {
id: policy.id,
role_ids: role_ids.filter(id => id !== roleId),
account_ids: [],
id: policyId,
role_ids: role_ids.map(role => role.id),
account_ids: account_ids.map(account => account.id),
}
if (policy.resource_type === 'app') {
if (type === 'role') {
payload.role_ids = payload.role_ids.filter(roleId => roleId !== id)
}
else if (type === 'account') {
payload.account_ids = payload.account_ids.filter(accountId => accountId !== id)
}
if (resource_type === 'app') {
updateAppAccessRuleBindings(payload, {
onSuccess: () => {
toast.success('Access rule updated successfully')
},
})
}
else if (policy.resource_type === 'dataset') {
else if (resource_type === 'dataset') {
updateDatasetAccessRuleBindings(payload, {
onSuccess: () => {
toast.success('Access rule updated successfully')
},
})
}
}, [policy.id, policy.resource_type, role_ids, updateAppAccessRuleBindings, updateDatasetAccessRuleBindings])
}, [account_ids, policyId, resource_type, role_ids, updateAppAccessRuleBindings, updateDatasetAccessRuleBindings])
return (
<div className={cn('flex items-start gap-2 py-3.5', className)}>
@ -65,9 +75,19 @@ const AccessRuleRow = ({
<div className="mt-2 flex flex-wrap items-center gap-1.5">
{role_ids.map(role => (
<RoleTag
key={role}
id={role}
label={role}
key={role.id}
id={role.id}
label={role.name}
type="role"
onRemove={handleRemoveRole}
/>
))}
{account_ids.map(account => (
<RoleTag
key={account.id}
id={account.id}
label={account.name}
type="account"
onRemove={handleRemoveRole}
/>
))}

View File

@ -35,9 +35,7 @@ type AddRuleTargetsModalBaseProps = {
onSubmit: (selection: { roleIds: string[], memberIds: string[] }) => void
}
export type AddRuleTargetsModalProps = AddRuleTargetsModalBaseProps & {
open: boolean
}
export type AddRuleTargetsModalProps = AddRuleTargetsModalBaseProps
const TABS: Array<{ key: TabKey, label: string }> = [
{ key: 'roles', label: 'ROLES' },
@ -237,11 +235,9 @@ const AddRuleTargetsModalBody = ({
<div className="system-sm-semibold text-text-secondary">
{role.name}
</div>
{role.description && (
<div className="mt-0.5 system-xs-regular text-text-tertiary">
{role.description}
</div>
)}
<div className="mt-0.5 system-xs-regular text-text-tertiary">
{role.description || 'No description'}
</div>
</div>
</div>
</li>
@ -330,7 +326,6 @@ const AddRuleTargetsModalBody = ({
}
const AddRuleTargetsModal = ({
open,
ruleName,
initialRoleIds,
initialMemberIds,
@ -339,7 +334,7 @@ const AddRuleTargetsModal = ({
}: AddRuleTargetsModalProps) => {
return (
<Dialog
open={open}
open
onOpenChange={(nextOpen) => {
if (!nextOpen)
onClose()

View File

@ -130,10 +130,9 @@ const AccessRulesPage = () => {
</div>
{addingRule && (
<AddRuleTargetsModal
open
ruleName={addingRule.policy.name}
initialRoleIds={addingRule.role_ids}
initialMemberIds={[]}
initialRoleIds={addingRule.role_ids.map(role => role.id)}
initialMemberIds={addingRule.account_ids.map(account => account.id)}
onClose={closeAddModal}
onSubmit={handleAddSubmit}
/>

View File

@ -1,18 +1,21 @@
'use client'
import type { BindingType } from '@/models/access-control'
import { cn } from '@langgenius/dify-ui/cn'
import { memo } from 'react'
export type RoleTagProps = {
id: string
label: string
onRemove?: (id: string) => void
type: BindingType
onRemove?: (id: string, type: BindingType) => void
className?: string
}
const RoleTag = ({
id,
label,
type,
onRemove,
className,
}: RoleTagProps) => {
@ -31,7 +34,7 @@ const RoleTag = ({
aria-label={`Remove ${label}`}
onClick={(e) => {
e.stopPropagation()
onRemove(id)
onRemove(id, type)
}}
className="flex h-4 w-4 items-center justify-center rounded text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
>

View File

@ -128,7 +128,20 @@ export type UpdateAccessPolicyRequest = {
permission_keys?: PermissionKey[]
}
export type BindingType = 'role' | 'account'
export type Bindings = {
role_ids: Array<{
id: string
name: string
}>
account_ids: Array<{
id: string
name: string
}>
}
export type BindingsPayload = {
role_ids: string[]
account_ids: string[]
}

View File

@ -1,7 +1,8 @@
import type {
AccessPolicy,
AccessPolicyResourceType,
Bindings,
AccessPolicyWithBindings,
BindingsPayload,
CreateAccessPolicyRequest,
GetAppAccessPoliciesResponse,
GetDatasetAccessPoliciesResponse,
@ -107,9 +108,9 @@ export const useUpdateAppAccessRuleBindings = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'update-app-bindings'],
mutationFn: (data: Bindings & { id: string }) => {
mutationFn: (data: BindingsPayload & { id: string }) => {
const { id, ...rest } = data
return put(`/workspaces/current/rbac/workspace/apps/access-policies/${id}/bindings`, {
return put<AccessPolicyWithBindings>(`/workspaces/current/rbac/workspace/apps/access-policies/${id}/bindings`, {
body: {
...rest,
},
@ -126,9 +127,9 @@ export const useUpdateDatasetAccessRuleBindings = () => {
return useMutation({
mutationKey: [NAME_SPACE, 'update-dataset-bindings'],
mutationFn: (data: Bindings & { id: string }) => {
mutationFn: (data: BindingsPayload & { id: string }) => {
const { id, ...rest } = data
return put(`/workspaces/current/rbac/workspace/datasets/access-policies/${id}/bindings`, {
return put<AccessPolicyWithBindings>(`/workspaces/current/rbac/workspace/datasets/access-policies/${id}/bindings`, {
body: {
...rest,
},