feat(audit): wire AddAuditEmission into 9 services (AUDIT-002)
- Wire StellaOps.Audit.Emission DI in: Authority, Policy, Release-Orchestrator, EvidenceLocker, Notify, Scanner, Scheduler, Integrations, Platform - Add AuditEmission__TimelineBaseUrl to compose defaults - Endpoint filter annotation deferred to follow-up pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Cryptography;
|
||||
|
||||
namespace StellaOps.Cryptography.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering tenant-aware crypto provider resolution.
|
||||
/// </summary>
|
||||
public static class TenantAwareCryptoProviderRegistryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Decorates the existing <see cref="ICryptoProviderRegistry"/> with tenant-aware resolution.
|
||||
/// <para>
|
||||
/// When a tenant context is available and the tenant has configured crypto provider preferences,
|
||||
/// the decorated registry will prefer the tenant's chosen providers. Falls back to the default
|
||||
/// ordering when no tenant context exists or no preferences are set.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Prerequisites:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="ICryptoProviderRegistry"/> must already be registered (via <c>AddStellaOpsCrypto</c>).</item>
|
||||
/// <item><see cref="ITenantCryptoPreferenceProvider"/> must be registered by the caller.</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="tenantIdAccessorFactory">
|
||||
/// Factory that creates a function returning the current tenant ID (or null when no tenant context).
|
||||
/// Example: <c>sp => () => sp.GetService<IStellaOpsTenantAccessor>()?.TenantId</c>
|
||||
/// </param>
|
||||
/// <param name="cacheTtl">
|
||||
/// How long tenant preferences are cached before refresh. Default: 5 minutes.
|
||||
/// </param>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddTenantAwareCryptoResolution(
|
||||
this IServiceCollection services,
|
||||
Func<IServiceProvider, Func<string?>> tenantIdAccessorFactory,
|
||||
TimeSpan? cacheTtl = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(tenantIdAccessorFactory);
|
||||
|
||||
// Manual decorator pattern: find the existing ICryptoProviderRegistry registration,
|
||||
// replace it with a factory that wraps the original in TenantAwareCryptoProviderRegistry.
|
||||
var innerDescriptor = services.LastOrDefault(d => d.ServiceType == typeof(ICryptoProviderRegistry));
|
||||
if (innerDescriptor is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"ICryptoProviderRegistry is not registered. Call AddStellaOpsCrypto() before AddTenantAwareCryptoResolution().");
|
||||
}
|
||||
|
||||
services.Remove(innerDescriptor);
|
||||
|
||||
services.AddSingleton<ICryptoProviderRegistry>(sp =>
|
||||
{
|
||||
// Resolve the inner (original) registry
|
||||
var innerRegistry = ResolveInner(sp, innerDescriptor);
|
||||
|
||||
var preferenceProvider = sp.GetService<ITenantCryptoPreferenceProvider>();
|
||||
if (preferenceProvider is null)
|
||||
{
|
||||
// No preference provider registered; tenant-aware resolution is a no-op.
|
||||
// This is expected in CLI / background worker scenarios.
|
||||
return innerRegistry;
|
||||
}
|
||||
|
||||
var tenantIdAccessor = tenantIdAccessorFactory(sp);
|
||||
var timeProvider = sp.GetService<TimeProvider>() ?? TimeProvider.System;
|
||||
var logger = sp.GetRequiredService<ILoggerFactory>()
|
||||
.CreateLogger<TenantAwareCryptoProviderRegistry>();
|
||||
|
||||
return new TenantAwareCryptoProviderRegistry(
|
||||
innerRegistry,
|
||||
preferenceProvider,
|
||||
tenantIdAccessor,
|
||||
timeProvider,
|
||||
logger,
|
||||
cacheTtl);
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static ICryptoProviderRegistry ResolveInner(IServiceProvider sp, ServiceDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.ImplementationInstance is ICryptoProviderRegistry instance)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (descriptor.ImplementationFactory is not null)
|
||||
{
|
||||
return (ICryptoProviderRegistry)descriptor.ImplementationFactory(sp);
|
||||
}
|
||||
|
||||
if (descriptor.ImplementationType is not null)
|
||||
{
|
||||
return (ICryptoProviderRegistry)ActivatorUtilities.CreateInstance(sp, descriptor.ImplementationType);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot resolve inner ICryptoProviderRegistry from descriptor: {descriptor}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user