Add SecretAuthorityService + endpoints so the setup wizard and integrations hub can stage secret bundles and bind authref URIs directly from the UI, instead of requiring out-of-band Vault seeding. Wire the new service behind IntegrationPolicies, expose SecretAuthorityDtos on the contracts library, and register an UpsertSecretBundle audit action for the emission library. Closes BOOTSTRAP-006 from SPRINT_20260413_004. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
189 lines
7.3 KiB
C#
189 lines
7.3 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using StellaOps.Auth.Abstractions;
|
|
using StellaOps.Auth.ServerIntegration;
|
|
using StellaOps.Integrations.Persistence;
|
|
using StellaOps.Integrations.Plugin.GitHubApp;
|
|
using StellaOps.Integrations.Plugin.Harbor;
|
|
using StellaOps.Integrations.Plugin.InMemory;
|
|
using StellaOps.Integrations.Plugin.Gitea;
|
|
using StellaOps.Integrations.Plugin.Jenkins;
|
|
using StellaOps.Integrations.Plugin.Nexus;
|
|
using StellaOps.Integrations.Plugin.DockerRegistry;
|
|
using StellaOps.Integrations.Plugin.GitLab;
|
|
using StellaOps.Integrations.Plugin.Vault;
|
|
using StellaOps.Integrations.Plugin.Consul;
|
|
using StellaOps.Integrations.Plugin.EbpfAgent;
|
|
using StellaOps.Integrations.WebService;
|
|
using StellaOps.Integrations.WebService.AiCodeGuard;
|
|
using StellaOps.Integrations.WebService.Infrastructure;
|
|
using StellaOps.Integrations.WebService.Security;
|
|
|
|
using StellaOps.Audit.Emission;
|
|
using StellaOps.Auth.ServerIntegration.Tenancy;
|
|
using StellaOps.Infrastructure.Postgres.Migrations;
|
|
using StellaOps.Infrastructure.Postgres.Options;
|
|
using StellaOps.Localization;
|
|
using StellaOps.Router.AspNet;
|
|
using System.Net.Http.Headers;
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add services
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen(options =>
|
|
{
|
|
options.SwaggerDoc("v1", new() { Title = "StellaOps Integration Catalog API", Version = "v1" });
|
|
});
|
|
|
|
// Database
|
|
var connectionString = builder.Configuration.GetConnectionString("IntegrationsDb")
|
|
?? builder.Configuration.GetConnectionString("Default")
|
|
?? "Host=localhost;Database=stellaops_integrations;Username=postgres;Password=postgres";
|
|
|
|
builder.Services.Configure<PostgresOptions>(options =>
|
|
{
|
|
options.ConnectionString = connectionString;
|
|
options.SchemaName = IntegrationDbContext.DefaultSchemaName;
|
|
});
|
|
|
|
builder.Services.AddDbContext<IntegrationDbContext>(options =>
|
|
options.UseNpgsql(connectionString));
|
|
|
|
builder.Services.AddStartupMigrations(
|
|
IntegrationDbContext.DefaultSchemaName,
|
|
"Integrations.Persistence",
|
|
typeof(IntegrationDbContext).Assembly);
|
|
|
|
// Repository
|
|
builder.Services.AddScoped<IIntegrationRepository, PostgresIntegrationRepository>();
|
|
|
|
// HttpClient factory (used by AuthRef resolver for Vault)
|
|
builder.Services.AddHttpClient(VaultAuthRefResolver.HttpClientName, client =>
|
|
{
|
|
var vaultAddr = builder.Configuration["VAULT_ADDR"] ?? "http://vault.stella-ops.local:8200";
|
|
client.BaseAddress = new Uri(vaultAddr.TrimEnd('/') + "/");
|
|
client.Timeout = TimeSpan.FromSeconds(15);
|
|
});
|
|
builder.Services.AddHttpClient(S3CompatibleConnectorPlugin.HttpClientName, client =>
|
|
{
|
|
client.Timeout = TimeSpan.FromSeconds(30);
|
|
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("StellaOps", "1.0"));
|
|
});
|
|
builder.Services.AddHttpClient(FeedMirrorConnectorPluginBase.HttpClientName, client =>
|
|
{
|
|
client.Timeout = TimeSpan.FromSeconds(30);
|
|
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("StellaOps", "1.0"));
|
|
});
|
|
|
|
// Plugin loader
|
|
builder.Services.AddSingleton<IntegrationPluginLoader>(sp =>
|
|
{
|
|
var logger = sp.GetRequiredService<ILogger<IntegrationPluginLoader>>();
|
|
var loader = new IntegrationPluginLoader(logger, sp);
|
|
|
|
// Load from plugins directory
|
|
var pluginsDir = builder.Configuration.GetValue<string>("Integrations:PluginsDirectory")
|
|
?? Path.Combine(AppContext.BaseDirectory, "plugins");
|
|
|
|
if (Directory.Exists(pluginsDir))
|
|
{
|
|
loader.LoadFromDirectory(pluginsDir);
|
|
}
|
|
|
|
// Also load from current assembly (for built-in plugins)
|
|
loader.LoadFromAssemblies(
|
|
[
|
|
typeof(Program).Assembly,
|
|
typeof(GitHubAppConnectorPlugin).Assembly,
|
|
typeof(HarborConnectorPlugin).Assembly,
|
|
typeof(InMemoryConnectorPlugin).Assembly,
|
|
typeof(GiteaConnectorPlugin).Assembly,
|
|
typeof(JenkinsConnectorPlugin).Assembly,
|
|
typeof(NexusConnectorPlugin).Assembly,
|
|
typeof(DockerRegistryConnectorPlugin).Assembly,
|
|
typeof(GitLabConnectorPlugin).Assembly,
|
|
typeof(VaultConnectorPlugin).Assembly,
|
|
typeof(ConsulConnectorPlugin).Assembly,
|
|
typeof(EbpfAgentConnectorPlugin).Assembly
|
|
]);
|
|
|
|
return loader;
|
|
});
|
|
|
|
builder.Services.AddSingleton(TimeProvider.System);
|
|
|
|
// Infrastructure
|
|
builder.Services.AddScoped<IIntegrationEventPublisher, LoggingEventPublisher>();
|
|
builder.Services.AddScoped<IIntegrationAuditLogger, LoggingAuditLogger>();
|
|
builder.Services.AddScoped<IAuthRefResolver, VaultAuthRefResolver>();
|
|
|
|
// Core service
|
|
builder.Services.AddScoped<IntegrationService>();
|
|
builder.Services.AddScoped<SecretAuthorityService>();
|
|
builder.Services.AddSingleton<IAiCodeGuardPipelineConfigLoader, AiCodeGuardPipelineConfigLoader>();
|
|
builder.Services.AddScoped<IAiCodeGuardRunService, AiCodeGuardRunService>();
|
|
|
|
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
|
builder.Services.AddStellaOpsLocalization(builder.Configuration);
|
|
builder.Services.AddTranslationBundle(System.Reflection.Assembly.GetExecutingAssembly());
|
|
|
|
// Authentication and authorization
|
|
builder.Services.AddStellaOpsResourceServerAuthentication(builder.Configuration);
|
|
builder.Services.AddAuthorization(options =>
|
|
{
|
|
options.AddStellaOpsScopePolicy(IntegrationPolicies.Read, StellaOpsScopes.IntegrationRead);
|
|
options.AddStellaOpsScopePolicy(IntegrationPolicies.Write, StellaOpsScopes.IntegrationWrite);
|
|
options.AddStellaOpsScopePolicy(IntegrationPolicies.Operate, StellaOpsScopes.IntegrationOperate);
|
|
options.AddStellaOpsScopePolicy(IntegrationPolicies.SecretAuthorityRead, StellaOpsScopes.IntegrationRead);
|
|
options.AddStellaOpsScopePolicy(IntegrationPolicies.SecretAuthorityWrite, StellaOpsScopes.IntegrationWrite);
|
|
});
|
|
|
|
// Unified audit emission (posts audit events to Timeline service)
|
|
builder.Services.AddAuditEmission(builder.Configuration);
|
|
|
|
// Stella Router integration
|
|
var routerEnabled = builder.Services.AddRouterMicroservice(
|
|
builder.Configuration,
|
|
serviceName: "integrations",
|
|
version: System.Reflection.CustomAttributeExtensions.GetCustomAttribute<System.Reflection.AssemblyInformationalVersionAttribute>(System.Reflection.Assembly.GetExecutingAssembly())?.InformationalVersion ?? "1.0.0",
|
|
routerOptionsSection: "Router");
|
|
builder.Services.AddStellaOpsTenantServices();
|
|
builder.TryAddStellaOpsLocalBinding("integrations");
|
|
var app = builder.Build();
|
|
app.LogStellaOpsLocalHostname("integrations");
|
|
|
|
// Configure pipeline
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
}
|
|
|
|
app.UseStellaOpsCors();
|
|
app.UseStellaOpsLocalization();
|
|
app.UseIdentityEnvelopeAuthentication();
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
app.UseStellaOpsTenantMiddleware();
|
|
app.TryUseStellaRouter(routerEnabled);
|
|
|
|
// Map endpoints
|
|
app.MapIntegrationEndpoints();
|
|
app.MapSecretAuthorityEndpoints();
|
|
|
|
// Health endpoint
|
|
app.MapGet("/health", () => Results.Ok(new { Status = "Healthy", Timestamp = DateTimeOffset.UtcNow }))
|
|
.WithTags("Health")
|
|
.WithName("HealthCheck")
|
|
.WithDescription("Returns the liveness status and current UTC timestamp for the Integration Catalog service. Used by the Router gateway and container orchestrator for health polling.")
|
|
.AllowAnonymous();
|
|
|
|
// Database schema created by SQL migrations (001_initial_schema.sql)
|
|
// Run via: stella-ops migration run Integrations --category startup
|
|
|
|
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
|
await app.LoadTranslationsAsync();
|
|
app.Run();
|
|
|
|
public partial class Program { }
|
|
|