Implement governed function assignment workflows

This commit is contained in:
2026-07-31 19:40:27 +02:00
parent f025b0c25b
commit c14719d55a
16 changed files with 4079 additions and 27 deletions
+117
View File
@@ -99,10 +99,90 @@ export type OrganizationFunctionAssignmentPayload = {
is_active?: boolean;
settings?: Record<string, unknown>;
change_request_id?: string | null;
governance_override_reason?: string | null;
governance_override_evidence?: string[];
};
export type IdmSettingsPayload = Partial<Pick<IdmSettings, "require_assignment_change_requests" | "audit_detail_level" | "change_retention_days" | "settings">>;
export type FunctionAssignmentChangeKind = "request" | "grant";
export type FunctionAssignmentChangeAction = "approve" | "reject" | "accept" | "request_changes" | "respond" | "withdraw" | "recover";
export type FunctionAssignmentChangeEvent = {
id: string;
sequence: number;
action: string;
from_state?: string | null;
to_state: string;
actor_account_id?: string | null;
actor_identity_id?: string | null;
comment?: string | null;
evidence: string[];
policy_decision: Record<string, unknown>;
workflow_step_id?: string | null;
details: Record<string, unknown>;
created_at: string;
};
export type FunctionAssignmentChange = {
id: string;
tenant_id: string;
kind: FunctionAssignmentChangeKind;
state: string;
profile: string;
function_id: string;
organization_unit_id: string;
candidate_identity_id: string;
candidate_account_id?: string | null;
initiator_account_id: string;
initiator_identity_id?: string | null;
justification: string;
evidence: string[];
requested_valid_from?: string | null;
requested_valid_until?: string | null;
required_steps: string[];
completed_steps: string[];
policy_decision: Record<string, unknown>;
workflow_definition_revision?: number | null;
workflow_definition_hash?: string | null;
workflow_instance_id?: string | null;
resulting_assignment_id?: string | null;
expires_at?: string | null;
outcome_reason?: string | null;
resource_revision: number;
etag: string;
metadata: Record<string, unknown>;
events: FunctionAssignmentChangeEvent[];
available_actions: FunctionAssignmentChangeAction[];
availability_reason?: string | null;
created_at: string;
updated_at: string;
};
export type FunctionAssignmentChangeList = {
changes: FunctionAssignmentChange[];
total: number;
page: number;
page_size: number;
pages: number;
};
export type FunctionAssignmentChangePayload = {
kind: FunctionAssignmentChangeKind;
function_id: string;
candidate_identity_id: string;
candidate_account_id?: string | null;
justification: string;
evidence?: string[];
requested_valid_from?: string | null;
requested_valid_until?: string | null;
applies_to_subunits?: boolean;
assignment_source?: "governance" | "delegated";
represented_assignment_id?: string | null;
idempotency_key: string;
metadata?: Record<string, unknown>;
};
function post<T, P extends Record<string, unknown>>(settings: ApiSettings, path: string, payload: P): Promise<T> {
return apiFetch<T>(settings, path, { method: "POST", body: JSON.stringify(payload) });
}
@@ -168,3 +248,40 @@ export function patchOrganizationFunctionAssignment(
): Promise<OrganizationFunctionAssignmentItem> {
return patch(settings, `/api/v1/idm/organization-function-assignments/${encodeURIComponent(id)}`, payload);
}
export function getFunctionAssignmentChanges(settings: ApiSettings): Promise<FunctionAssignmentChangeList> {
return apiFetch<FunctionAssignmentChangeList>(settings, "/api/v1/idm/function-assignment-changes?page_size=200");
}
export function getFunctionAssignmentChange(settings: ApiSettings, id: string): Promise<FunctionAssignmentChange> {
return apiFetch<FunctionAssignmentChange>(settings, `/api/v1/idm/function-assignment-changes/${encodeURIComponent(id)}`);
}
export function createFunctionAssignmentChange(
settings: ApiSettings,
payload: FunctionAssignmentChangePayload
): Promise<FunctionAssignmentChange> {
return post(settings, "/api/v1/idm/function-assignment-changes", payload);
}
export function actOnFunctionAssignmentChange(
settings: ApiSettings,
change: FunctionAssignmentChange,
action: FunctionAssignmentChangeAction,
comment?: string
): Promise<FunctionAssignmentChange> {
return apiFetch<FunctionAssignmentChange>(
settings,
`/api/v1/idm/function-assignment-changes/${encodeURIComponent(change.id)}/actions`,
{
method: "POST",
headers: { "If-Match": change.etag },
body: JSON.stringify({
action,
base_revision: change.resource_revision,
comment: comment?.trim() || null,
evidence: []
})
}
);
}