sprint14-19: ABAC UI rule editor + permission templates + bulk share + analytics + delegation + resolution strategies + migrations 0056-0058

This commit is contained in:
Agent Zero
2026-07-29 02:47:03 +02:00
parent e0003b9384
commit ddf73ee42e
21 changed files with 2399 additions and 4 deletions
+124
View File
@@ -0,0 +1,124 @@
/**
* ABAC Policy API client.
*
* All requests use the shared `apiClient` (`baseURL: '/api/v1'`) and target
* the Policies routes under `/policies/...`.
*/
import { apiDelete, apiGet, apiPost, apiPut } from './client';
// ─── Types ─────────────────────────────────────────────────────────────────
export type PrincipalType = 'user' | 'group' | 'role';
export type ConditionOperator =
| 'eq'
| 'neq'
| 'in'
| 'gt'
| 'gte'
| 'lt'
| 'lte'
| 'contains'
| 'starts_with'
| 'is_null';
export type ConditionGroupLogic = 'AND' | 'OR';
export interface Condition {
id?: string;
field: string;
operator: ConditionOperator;
value: string;
}
export interface ConditionGroup {
id?: string;
logic: ConditionGroupLogic;
conditions: Condition[];
groups?: ConditionGroup[];
}
export interface ABACPolicy {
id: string;
name: string;
entity_type: string;
principal_type: PrincipalType;
principal_id: string;
principal_name?: string | null;
effect: 'allow' | 'deny';
conditions: ConditionGroup | null;
priority: number;
enabled: boolean;
created_at?: string | null;
updated_at?: string | null;
}
export interface PolicyListResponse {
items: ABACPolicy[];
total: number;
}
export interface CreatePolicyPayload {
name: string;
principal_type: PrincipalType;
principal_id: string;
effect: 'allow' | 'deny';
conditions: ConditionGroup | null;
priority: number;
enabled: boolean;
}
export interface UpdatePolicyPayload {
name?: string;
principal_type?: PrincipalType;
principal_id?: string;
effect?: 'allow' | 'deny';
conditions?: ConditionGroup | null;
priority?: number;
enabled?: boolean;
}
// ─── API Functions ─────────────────────────────────────────────────────────
/**
* Fetch all policies for a given entity type.
*/
export function fetchPolicies(entityType: string): Promise<PolicyListResponse> {
return apiGet<PolicyListResponse>(`/policies/${entityType}`);
}
/**
* Fetch a single policy by ID.
*/
export function fetchPolicy(entityType: string, policyId: string): Promise<ABACPolicy> {
return apiGet<ABACPolicy>(`/policies/${entityType}/${policyId}`);
}
/**
* Create a new policy for the given entity type.
*/
export function createPolicy(
entityType: string,
payload: CreatePolicyPayload
): Promise<ABACPolicy> {
return apiPost<ABACPolicy>(`/policies/${entityType}`, payload);
}
/**
* Update an existing policy.
*/
export function updatePolicy(
entityType: string,
policyId: string,
payload: UpdatePolicyPayload
): Promise<ABACPolicy> {
return apiPut<ABACPolicy>(`/policies/${entityType}/${policyId}`, payload);
}
/**
* Delete a policy.
*/
export function deletePolicy(entityType: string, policyId: string): Promise<void> {
return apiDelete<void>(`/policies/${entityType}/${policyId}`);
}
+92
View File
@@ -0,0 +1,92 @@
/**
* React Query hooks for the ABAC Policy API.
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import {
fetchPolicies,
fetchPolicy,
createPolicy,
updatePolicy,
deletePolicy,
type CreatePolicyPayload,
type UpdatePolicyPayload,
} from './policies';
// ─── Query Key Factory ─────────────────────────────────────────────────────
export const policyKeys = {
all: ['policies'] as const,
list: (entityType: string) => [...policyKeys.all, 'list', entityType] as const,
detail: (entityType: string, policyId: string) =>
[...policyKeys.all, 'detail', entityType, policyId] as const,
};
// ─── Hooks ─────────────────────────────────────────────────────────────────
/**
* Fetch all policies for a given entity type.
*/
export function usePolicies(entityType: string) {
return useQuery({
queryKey: policyKeys.list(entityType),
queryFn: () => fetchPolicies(entityType),
enabled: !!entityType,
});
}
/**
* Fetch a single policy by ID.
*/
export function usePolicy(entityType: string, policyId: string | null) {
return useQuery({
queryKey: policyKeys.detail(entityType, policyId!),
queryFn: () => fetchPolicy(entityType, policyId!),
enabled: !!entityType && !!policyId,
});
}
/**
* Create a new policy.
*/
export function useCreatePolicy(entityType: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (payload: CreatePolicyPayload) => createPolicy(entityType, payload),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: policyKeys.list(entityType) });
},
});
}
/**
* Update an existing policy.
*/
export function useUpdatePolicy(entityType: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
policyId,
data,
}: {
policyId: string;
data: UpdatePolicyPayload;
}) => updatePolicy(entityType, policyId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: policyKeys.list(entityType) });
},
});
}
/**
* Delete a policy.
*/
export function useDeletePolicy(entityType: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (policyId: string) => deletePolicy(entityType, policyId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: policyKeys.list(entityType) });
},
});
}