- Created expected JSON files for Go modules and workspaces. - Added go.mod and go.sum files for example projects. - Implemented private module structure with expected JSON output. - Introduced vendored dependencies with corresponding expected JSON. - Developed PostgresGraphJobStore for managing graph jobs. - Established SQL migration scripts for graph jobs schema. - Implemented GraphJobRepository for CRUD operations on graph jobs. - Created IGraphJobRepository interface for repository abstraction. - Added unit tests for GraphJobRepository to ensure functionality.
218 lines
9.4 KiB
C#
218 lines
9.4 KiB
C#
using System.Linq;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Auth.Abstractions;
|
|
using StellaOps.Auth.ServerIntegration;
|
|
using StellaOps.Plugin.DependencyInjection;
|
|
using StellaOps.Plugin.Hosting;
|
|
using StellaOps.Scheduler.WebService.Hosting;
|
|
using StellaOps.Scheduler.ImpactIndex;
|
|
using StellaOps.Scheduler.Storage.Postgres;
|
|
using StellaOps.Scheduler.Storage.Postgres.Repositories;
|
|
using StellaOps.Scheduler.WebService;
|
|
using StellaOps.Scheduler.WebService.Auth;
|
|
using StellaOps.Scheduler.WebService.EventWebhooks;
|
|
using StellaOps.Scheduler.WebService.GraphJobs;
|
|
using StellaOps.Scheduler.WebService.GraphJobs.Events;
|
|
using StellaOps.Scheduler.WebService.Schedules;
|
|
using StellaOps.Scheduler.WebService.Options;
|
|
using StellaOps.Scheduler.WebService.PolicyRuns;
|
|
using StellaOps.Scheduler.WebService.PolicySimulations;
|
|
using StellaOps.Scheduler.WebService.VulnerabilityResolverJobs;
|
|
using StellaOps.Scheduler.WebService.Runs;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
|
builder.Services.AddSingleton<StellaOps.Scheduler.WebService.ISystemClock, StellaOps.Scheduler.WebService.SystemClock>();
|
|
builder.Services.TryAddSingleton(TimeProvider.System);
|
|
|
|
var authorityOptions = new SchedulerAuthorityOptions();
|
|
builder.Configuration.GetSection("Scheduler:Authority").Bind(authorityOptions);
|
|
|
|
if (!authorityOptions.RequiredScopes.Any(scope => string.Equals(scope, StellaOpsScopes.GraphRead, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
authorityOptions.RequiredScopes.Add(StellaOpsScopes.GraphRead);
|
|
}
|
|
|
|
if (!authorityOptions.RequiredScopes.Any(scope => string.Equals(scope, StellaOpsScopes.GraphWrite, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
authorityOptions.RequiredScopes.Add(StellaOpsScopes.GraphWrite);
|
|
}
|
|
|
|
if (authorityOptions.Audiences.Count == 0)
|
|
{
|
|
authorityOptions.Audiences.Add("api://scheduler");
|
|
}
|
|
|
|
authorityOptions.Validate();
|
|
builder.Services.AddSingleton(authorityOptions);
|
|
|
|
builder.Services.AddOptions<SchedulerEventsOptions>()
|
|
.Bind(builder.Configuration.GetSection("Scheduler:Events"))
|
|
.PostConfigure(options =>
|
|
{
|
|
options.Webhooks ??= new SchedulerInboundWebhooksOptions();
|
|
options.Webhooks.Conselier ??= SchedulerWebhookOptions.CreateDefault("conselier");
|
|
options.Webhooks.Excitor ??= SchedulerWebhookOptions.CreateDefault("excitor");
|
|
|
|
options.Webhooks.Conselier.Name = string.IsNullOrWhiteSpace(options.Webhooks.Conselier.Name)
|
|
? "conselier"
|
|
: options.Webhooks.Conselier.Name;
|
|
options.Webhooks.Excitor.Name = string.IsNullOrWhiteSpace(options.Webhooks.Excitor.Name)
|
|
? "excitor"
|
|
: options.Webhooks.Excitor.Name;
|
|
|
|
options.Webhooks.Conselier.Validate();
|
|
options.Webhooks.Excitor.Validate();
|
|
});
|
|
|
|
builder.Services.AddMemoryCache();
|
|
builder.Services.AddSingleton<IWebhookRateLimiter, InMemoryWebhookRateLimiter>();
|
|
builder.Services.AddSingleton<IWebhookRequestAuthenticator, WebhookRequestAuthenticator>();
|
|
builder.Services.AddSingleton<IInboundExportEventSink, LoggingExportEventSink>();
|
|
builder.Services.AddSingleton<IRedisConnectionFactory, RedisConnectionFactory>();
|
|
|
|
var cartographerOptions = builder.Configuration.GetSection("Scheduler:Cartographer").Get<SchedulerCartographerOptions>() ?? new SchedulerCartographerOptions();
|
|
builder.Services.AddSingleton(cartographerOptions);
|
|
builder.Services.AddOptions<SchedulerCartographerOptions>()
|
|
.Bind(builder.Configuration.GetSection("Scheduler:Cartographer"));
|
|
|
|
var storageSection = builder.Configuration.GetSection("Scheduler:Storage");
|
|
if (storageSection.Exists())
|
|
{
|
|
builder.Services.AddSchedulerPostgresStorage(storageSection);
|
|
builder.Services.AddScoped<IGraphJobRepository, GraphJobRepository>();
|
|
builder.Services.AddSingleton<IGraphJobStore, PostgresGraphJobStore>();
|
|
builder.Services.AddSingleton<IPolicyRunService, PolicyRunService>();
|
|
builder.Services.AddSingleton<IPolicySimulationMetricsProvider, PolicySimulationMetricsProvider>();
|
|
builder.Services.AddSingleton<IPolicySimulationMetricsRecorder>(static sp => (IPolicySimulationMetricsRecorder)sp.GetRequiredService<IPolicySimulationMetricsProvider>());
|
|
}
|
|
else
|
|
{
|
|
builder.Services.AddSingleton<IGraphJobStore, InMemoryGraphJobStore>();
|
|
builder.Services.AddSingleton<IScheduleRepository, InMemoryScheduleRepository>();
|
|
builder.Services.AddSingleton<IRunRepository, InMemoryRunRepository>();
|
|
builder.Services.AddSingleton<IRunSummaryService, InMemoryRunSummaryService>();
|
|
builder.Services.AddSingleton<ISchedulerAuditService, InMemorySchedulerAuditService>();
|
|
builder.Services.AddSingleton<IPolicyRunService, InMemoryPolicyRunService>();
|
|
}
|
|
builder.Services.AddSingleton<IGraphJobCompletionPublisher, GraphJobEventPublisher>();
|
|
builder.Services.AddSingleton<IResolverJobService, InMemoryResolverJobService>();
|
|
if (cartographerOptions.Webhook.Enabled)
|
|
{
|
|
builder.Services.AddHttpClient<ICartographerWebhookClient, CartographerWebhookClient>((serviceProvider, client) =>
|
|
{
|
|
var options = serviceProvider.GetRequiredService<IOptionsMonitor<SchedulerCartographerOptions>>().CurrentValue;
|
|
client.Timeout = TimeSpan.FromSeconds(options.Webhook.TimeoutSeconds <= 0 ? 10 : options.Webhook.TimeoutSeconds);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
builder.Services.AddSingleton<ICartographerWebhookClient, NullCartographerWebhookClient>();
|
|
}
|
|
builder.Services.AddScoped<IGraphJobService, GraphJobService>();
|
|
builder.Services.AddImpactIndexStub();
|
|
builder.Services.AddResolverJobServices();
|
|
|
|
var schedulerOptions = builder.Configuration.GetSection("Scheduler").Get<SchedulerOptions>() ?? new SchedulerOptions();
|
|
schedulerOptions.Validate();
|
|
builder.Services.AddSingleton(schedulerOptions);
|
|
builder.Services.AddOptions<SchedulerOptions>()
|
|
.Bind(builder.Configuration.GetSection("Scheduler"))
|
|
.PostConfigure(options => options.Validate());
|
|
|
|
builder.Services.AddSingleton<IQueueLagSummaryProvider, QueueLagSummaryProvider>();
|
|
builder.Services.AddSingleton<IRunStreamCoordinator, RunStreamCoordinator>();
|
|
builder.Services.AddSingleton<IPolicySimulationStreamCoordinator, PolicySimulationStreamCoordinator>();
|
|
builder.Services.AddOptions<RunStreamOptions>()
|
|
.Bind(builder.Configuration.GetSection("Scheduler:RunStream"));
|
|
|
|
var pluginHostOptions = SchedulerPluginHostFactory.Build(schedulerOptions.Plugins, builder.Environment.ContentRootPath);
|
|
builder.Services.AddSingleton(pluginHostOptions);
|
|
builder.Services.RegisterPluginRoutines(builder.Configuration, pluginHostOptions);
|
|
|
|
if (authorityOptions.Enabled)
|
|
{
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddStellaOpsResourceServerAuthentication(
|
|
builder.Configuration,
|
|
configurationSection: null,
|
|
configure: resourceOptions =>
|
|
{
|
|
resourceOptions.Authority = authorityOptions.Issuer;
|
|
resourceOptions.RequireHttpsMetadata = authorityOptions.RequireHttpsMetadata;
|
|
resourceOptions.MetadataAddress = authorityOptions.MetadataAddress;
|
|
resourceOptions.BackchannelTimeout = TimeSpan.FromSeconds(authorityOptions.BackchannelTimeoutSeconds);
|
|
resourceOptions.TokenClockSkew = TimeSpan.FromSeconds(authorityOptions.TokenClockSkewSeconds);
|
|
|
|
foreach (var audience in authorityOptions.Audiences)
|
|
{
|
|
resourceOptions.Audiences.Add(audience);
|
|
}
|
|
|
|
foreach (var scope in authorityOptions.RequiredScopes)
|
|
{
|
|
resourceOptions.RequiredScopes.Add(scope);
|
|
}
|
|
|
|
foreach (var tenant in authorityOptions.RequiredTenants)
|
|
{
|
|
resourceOptions.RequiredTenants.Add(tenant);
|
|
}
|
|
|
|
foreach (var network in authorityOptions.BypassNetworks)
|
|
{
|
|
resourceOptions.BypassNetworks.Add(network);
|
|
}
|
|
});
|
|
|
|
builder.Services.AddAuthorization();
|
|
builder.Services.AddScoped<ITenantContextAccessor, ClaimsTenantContextAccessor>();
|
|
builder.Services.AddScoped<IScopeAuthorizer, TokenScopeAuthorizer>();
|
|
}
|
|
else
|
|
{
|
|
builder.Services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = "Anonymous";
|
|
options.DefaultChallengeScheme = "Anonymous";
|
|
}).AddScheme<AuthenticationSchemeOptions, AnonymousAuthenticationHandler>("Anonymous", static _ => { });
|
|
|
|
builder.Services.AddAuthorization();
|
|
builder.Services.AddScoped<ITenantContextAccessor, HeaderTenantContextAccessor>();
|
|
builder.Services.AddScoped<IScopeAuthorizer, HeaderScopeAuthorizer>();
|
|
}
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
if (!authorityOptions.Enabled)
|
|
{
|
|
app.Logger.LogWarning("Scheduler Authority authentication is disabled; relying on header-based development fallback.");
|
|
}
|
|
else if (authorityOptions.AllowAnonymousFallback)
|
|
{
|
|
app.Logger.LogWarning("Scheduler Authority authentication is enabled but anonymous fallback remains allowed. Disable fallback before production rollout.");
|
|
}
|
|
|
|
app.MapGet("/healthz", () => Results.Json(new { status = "ok" }));
|
|
app.MapGet("/readyz", () => Results.Json(new { status = "ready" }));
|
|
|
|
app.MapGraphJobEndpoints();
|
|
ResolverJobEndpointExtensions.MapResolverJobEndpoints(app);
|
|
app.MapScheduleEndpoints();
|
|
app.MapRunEndpoints();
|
|
app.MapPolicyRunEndpoints();
|
|
app.MapPolicySimulationEndpoints();
|
|
app.MapSchedulerEventWebhookEndpoints();
|
|
|
|
app.Run();
|
|
|
|
public partial class Program;
|