sprint14-19: ABAC UI rule editor + permission templates + bulk share + analytics + delegation + resolution strategies + migrations 0056-0058
This commit is contained in:
@@ -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}`);
|
||||
}
|
||||
@@ -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) });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user