using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using StellaOps.Scanner.Surface.Env; using StellaOps.Scanner.Surface.Secrets.Providers; namespace StellaOps.Scanner.Surface.Secrets; public static class ServiceCollectionExtensions { public static IServiceCollection AddSurfaceSecrets( this IServiceCollection services, Action? configure = null) { if (services is null) { throw new ArgumentNullException(nameof(services)); } services.AddOptions(); if (configure is not null) { services.Configure(configure); } services.TryAddSingleton(sp => { var env = sp.GetRequiredService(); var options = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService().CreateLogger("SurfaceSecrets"); return CreateProvider(env.Settings.Secrets, logger); }); return services; } private static ISurfaceSecretProvider CreateProvider(SurfaceSecretsConfiguration configuration, ILogger logger) { var providers = new List(); switch (configuration.Provider.ToLowerInvariant()) { case "kubernetes": providers.Add(new KubernetesSurfaceSecretProvider(configuration, logger)); break; case "file": providers.Add(new FileSurfaceSecretProvider(configuration.Root ?? throw new ArgumentException("Secrets root is required for file provider."))); break; case "inline": providers.Add(new InlineSurfaceSecretProvider(configuration)); break; default: logger.LogWarning("Unknown surface secret provider '{Provider}'. Falling back to inline provider.", configuration.Provider); providers.Add(new InlineSurfaceSecretProvider(configuration)); break; } if (!string.IsNullOrWhiteSpace(configuration.FallbackProvider)) { providers.Add(new InlineSurfaceSecretProvider(configuration with { Provider = configuration.FallbackProvider })); } return providers.Count == 1 ? providers[0] : new CompositeSurfaceSecretProvider(providers); } }