Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
release-manifest-verify / verify (push) Has been cancelled
- Implement comprehensive tests for PackRunAttestationService, covering attestation generation, verification, and event emission. - Add tests for SealedInstallEnforcer to validate sealed install requirements and enforcement logic. - Introduce a MonacoLoaderService stub for testing purposes to prevent Monaco workers/styles from loading during Karma runs.
635 lines
28 KiB
C#
635 lines
28 KiB
C#
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Policy.Registry.Contracts;
|
|
|
|
namespace StellaOps.Policy.Registry;
|
|
|
|
/// <summary>
|
|
/// HTTP client implementation for Policy Registry API.
|
|
/// </summary>
|
|
public sealed class PolicyRegistryClient : IPolicyRegistryClient
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly JsonSerializerOptions _jsonOptions;
|
|
|
|
public PolicyRegistryClient(HttpClient httpClient, IOptions<PolicyRegistryClientOptions>? options = null)
|
|
{
|
|
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
|
_jsonOptions = new JsonSerializerOptions
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
|
PropertyNameCaseInsensitive = true
|
|
};
|
|
|
|
if (options?.Value?.BaseUrl is not null && _httpClient.BaseAddress is null)
|
|
{
|
|
_httpClient.BaseAddress = new Uri(options.Value.BaseUrl);
|
|
}
|
|
}
|
|
|
|
private static void AddTenantHeader(HttpRequestMessage request, Guid tenantId)
|
|
{
|
|
request.Headers.Add("X-Tenant-Id", tenantId.ToString());
|
|
}
|
|
|
|
private static string BuildQueryString(PaginationParams? pagination, params (string name, string? value)[] additional)
|
|
{
|
|
var parts = new List<string>();
|
|
|
|
if (pagination is not null)
|
|
{
|
|
if (pagination.PageSize != 20)
|
|
{
|
|
parts.Add($"page_size={pagination.PageSize}");
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(pagination.PageToken))
|
|
{
|
|
parts.Add($"page_token={Uri.EscapeDataString(pagination.PageToken)}");
|
|
}
|
|
}
|
|
|
|
foreach (var (name, value) in additional)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
parts.Add($"{name}={Uri.EscapeDataString(value)}");
|
|
}
|
|
}
|
|
|
|
return parts.Count > 0 ? "?" + string.Join("&", parts) : string.Empty;
|
|
}
|
|
|
|
// ============================================================
|
|
// VERIFICATION POLICY OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<VerificationPolicyList> ListVerificationPoliciesAsync(
|
|
Guid tenantId,
|
|
PaginationParams? pagination = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var query = BuildQueryString(pagination);
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/verification-policies{query}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<VerificationPolicyList>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<VerificationPolicy> CreateVerificationPolicyAsync(
|
|
Guid tenantId,
|
|
CreateVerificationPolicyRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/verification-policies");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<VerificationPolicy>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<VerificationPolicy> GetVerificationPolicyAsync(
|
|
Guid tenantId,
|
|
string policyId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/verification-policies/{Uri.EscapeDataString(policyId)}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<VerificationPolicy>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<VerificationPolicy> UpdateVerificationPolicyAsync(
|
|
Guid tenantId,
|
|
string policyId,
|
|
UpdateVerificationPolicyRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Put, $"/api/v1/policy/verification-policies/{Uri.EscapeDataString(policyId)}");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<VerificationPolicy>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task DeleteVerificationPolicyAsync(
|
|
Guid tenantId,
|
|
string policyId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/policy/verification-policies/{Uri.EscapeDataString(policyId)}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
// ============================================================
|
|
// POLICY PACK OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<PolicyPackList> ListPolicyPacksAsync(
|
|
Guid tenantId,
|
|
PolicyPackStatus? status = null,
|
|
PaginationParams? pagination = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var query = BuildQueryString(pagination, ("status", status?.ToString().ToLowerInvariant()));
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/packs{query}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<PolicyPackList>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<PolicyPack> CreatePolicyPackAsync(
|
|
Guid tenantId,
|
|
CreatePolicyPackRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/packs");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<PolicyPack>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<PolicyPack> GetPolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/packs/{packId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<PolicyPack>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<PolicyPack> UpdatePolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
UpdatePolicyPackRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Put, $"/api/v1/policy/packs/{packId}");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<PolicyPack>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task DeletePolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/policy/packs/{packId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
public async Task<CompilationResult> CompilePolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Post, $"/api/v1/policy/packs/{packId}/compile");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
// Note: 422 also returns CompilationResult, so we read regardless of status
|
|
return await response.Content.ReadFromJsonAsync<CompilationResult>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<SimulationResult> SimulatePolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
SimulationRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/api/v1/policy/packs/{packId}/simulate");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<SimulationResult>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<PolicyPack> PublishPolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
PublishRequest? request = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/api/v1/policy/packs/{packId}/publish");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
if (request is not null)
|
|
{
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
}
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<PolicyPack>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<PolicyPack> PromotePolicyPackAsync(
|
|
Guid tenantId,
|
|
Guid packId,
|
|
PromoteRequest? request = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/api/v1/policy/packs/{packId}/promote");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
if (request is not null)
|
|
{
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
}
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<PolicyPack>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
// ============================================================
|
|
// SNAPSHOT OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<SnapshotList> ListSnapshotsAsync(
|
|
Guid tenantId,
|
|
PaginationParams? pagination = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var query = BuildQueryString(pagination);
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/snapshots{query}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<SnapshotList>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<Snapshot> CreateSnapshotAsync(
|
|
Guid tenantId,
|
|
CreateSnapshotRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/snapshots");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Snapshot>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<Snapshot> GetSnapshotAsync(
|
|
Guid tenantId,
|
|
Guid snapshotId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/snapshots/{snapshotId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Snapshot>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task DeleteSnapshotAsync(
|
|
Guid tenantId,
|
|
Guid snapshotId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/policy/snapshots/{snapshotId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
public async Task<Snapshot> GetSnapshotByDigestAsync(
|
|
Guid tenantId,
|
|
string digest,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/snapshots/by-digest/{Uri.EscapeDataString(digest)}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Snapshot>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
// ============================================================
|
|
// VIOLATION OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<ViolationList> ListViolationsAsync(
|
|
Guid tenantId,
|
|
Severity? severity = null,
|
|
PaginationParams? pagination = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var query = BuildQueryString(pagination, ("severity", severity?.ToString().ToLowerInvariant()));
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/violations{query}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<ViolationList>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<Violation> AppendViolationAsync(
|
|
Guid tenantId,
|
|
CreateViolationRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/violations");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Violation>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<ViolationBatchResult> AppendViolationBatchAsync(
|
|
Guid tenantId,
|
|
ViolationBatchRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/violations/batch");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<ViolationBatchResult>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<Violation> GetViolationAsync(
|
|
Guid tenantId,
|
|
Guid violationId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/violations/{violationId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Violation>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
// ============================================================
|
|
// OVERRIDE OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<Override> CreateOverrideAsync(
|
|
Guid tenantId,
|
|
CreateOverrideRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/overrides");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Override>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<Override> GetOverrideAsync(
|
|
Guid tenantId,
|
|
Guid overrideId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/policy/overrides/{overrideId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Override>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task DeleteOverrideAsync(
|
|
Guid tenantId,
|
|
Guid overrideId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/policy/overrides/{overrideId}");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
public async Task<Override> ApproveOverrideAsync(
|
|
Guid tenantId,
|
|
Guid overrideId,
|
|
ApproveOverrideRequest? request = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"/api/v1/policy/overrides/{overrideId}:approve");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
if (request is not null)
|
|
{
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
}
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Override>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<Override> DisableOverrideAsync(
|
|
Guid tenantId,
|
|
Guid overrideId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Post, $"/api/v1/policy/overrides/{overrideId}:disable");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<Override>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
// ============================================================
|
|
// SEALED MODE OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<SealedModeStatus> GetSealedModeStatusAsync(
|
|
Guid tenantId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/policy/sealed-mode/status");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<SealedModeStatus>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<SealedModeStatus> SealAsync(
|
|
Guid tenantId,
|
|
SealRequest? request = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/sealed-mode/seal");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
if (request is not null)
|
|
{
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
}
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<SealedModeStatus>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<SealedModeStatus> UnsealAsync(
|
|
Guid tenantId,
|
|
UnsealRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/sealed-mode/unseal");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<SealedModeStatus>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<BundleVerificationResult> VerifyBundleAsync(
|
|
Guid tenantId,
|
|
VerifyBundleRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/sealed-mode/verify");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<BundleVerificationResult>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
// ============================================================
|
|
// STALENESS OPERATIONS
|
|
// ============================================================
|
|
|
|
public async Task<StalenessStatus> GetStalenessStatusAsync(
|
|
Guid tenantId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/policy/staleness/status");
|
|
AddTenantHeader(request, tenantId);
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<StalenessStatus>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
|
|
public async Task<StalenessEvaluation> EvaluateStalenessAsync(
|
|
Guid tenantId,
|
|
EvaluateStalenessRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/policy/staleness/evaluate");
|
|
AddTenantHeader(httpRequest, tenantId);
|
|
httpRequest.Content = JsonContent.Create(request, options: _jsonOptions);
|
|
|
|
var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<StalenessEvaluation>(_jsonOptions, cancellationToken).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException("Failed to deserialize response");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configuration options for Policy Registry client.
|
|
/// </summary>
|
|
public sealed class PolicyRegistryClientOptions
|
|
{
|
|
public string? BaseUrl { get; set; }
|
|
}
|