up
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
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
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
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -1,153 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace StellaOps.Signals.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Artifact storage configuration for Signals callgraph ingestion.
|
||||
/// </summary>
|
||||
public sealed class SignalsArtifactStorageOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage driver: "filesystem" (default) or "rustfs".
|
||||
/// </summary>
|
||||
public string Driver { get; set; } = SignalsStorageDrivers.FileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Root directory used to persist raw callgraph artifacts (filesystem driver).
|
||||
/// </summary>
|
||||
public string RootPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "callgraph-artifacts");
|
||||
|
||||
/// <summary>
|
||||
/// Bucket name for CAS storage (RustFS driver).
|
||||
/// Per CAS contract, signals uses "signals-data" bucket.
|
||||
/// </summary>
|
||||
public string BucketName { get; set; } = "signals-data";
|
||||
|
||||
/// <summary>
|
||||
/// Root prefix within the bucket for callgraph artifacts.
|
||||
/// </summary>
|
||||
public string RootPrefix { get; set; } = "callgraphs";
|
||||
|
||||
/// <summary>
|
||||
/// RustFS-specific options.
|
||||
/// </summary>
|
||||
public SignalsRustFsOptions RustFs { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Additional headers to include in storage requests.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the filesystem driver is configured.
|
||||
/// </summary>
|
||||
public bool IsFileSystemDriver()
|
||||
=> string.Equals(Driver, SignalsStorageDrivers.FileSystem, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the RustFS driver is configured.
|
||||
/// </summary>
|
||||
public bool IsRustFsDriver()
|
||||
=> string.Equals(Driver, SignalsStorageDrivers.RustFs, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configured values.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (!IsFileSystemDriver() && !IsRustFsDriver())
|
||||
{
|
||||
throw new InvalidOperationException($"Signals storage driver '{Driver}' is not supported. Use '{SignalsStorageDrivers.FileSystem}' or '{SignalsStorageDrivers.RustFs}'.");
|
||||
}
|
||||
|
||||
if (IsFileSystemDriver() && string.IsNullOrWhiteSpace(RootPath))
|
||||
{
|
||||
throw new InvalidOperationException("Signals artifact storage path must be configured for filesystem driver.");
|
||||
}
|
||||
|
||||
if (IsRustFsDriver())
|
||||
{
|
||||
RustFs ??= new SignalsRustFsOptions();
|
||||
RustFs.Validate();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BucketName))
|
||||
{
|
||||
throw new InvalidOperationException("Signals storage bucket name must be configured for RustFS driver.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RustFS-specific configuration options.
|
||||
/// </summary>
|
||||
public sealed class SignalsRustFsOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Base URL for the RustFS service (e.g., http://localhost:8180/api/v1).
|
||||
/// </summary>
|
||||
public string BaseUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Allow insecure TLS connections (development only).
|
||||
/// </summary>
|
||||
public bool AllowInsecureTls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// API key for authentication.
|
||||
/// </summary>
|
||||
public string? ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Header name for the API key (e.g., "X-API-Key").
|
||||
/// </summary>
|
||||
public string ApiKeyHeader { get; set; } = "X-API-Key";
|
||||
|
||||
/// <summary>
|
||||
/// HTTP request timeout.
|
||||
/// </summary>
|
||||
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(60);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configured values.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(BaseUrl))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must be configured.");
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(BaseUrl, UriKind.Absolute, out var uri))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must use HTTP or HTTPS.");
|
||||
}
|
||||
|
||||
if (Timeout <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("RustFS timeout must be greater than zero.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ApiKeyHeader) && string.IsNullOrWhiteSpace(ApiKey))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS API key header name requires a non-empty API key.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supported storage driver names.
|
||||
/// </summary>
|
||||
public static class SignalsStorageDrivers
|
||||
{
|
||||
public const string FileSystem = "filesystem";
|
||||
public const string RustFs = "rustfs";
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace StellaOps.Signals.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Artifact storage configuration for Signals callgraph ingestion.
|
||||
/// </summary>
|
||||
public sealed class SignalsArtifactStorageOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage driver: "filesystem" (default) or "rustfs".
|
||||
/// </summary>
|
||||
public string Driver { get; set; } = SignalsStorageDrivers.FileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Root directory used to persist raw callgraph artifacts (filesystem driver).
|
||||
/// </summary>
|
||||
public string RootPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "callgraph-artifacts");
|
||||
|
||||
/// <summary>
|
||||
/// Bucket name for CAS storage (RustFS driver).
|
||||
/// Per CAS contract, signals uses "signals-data" bucket.
|
||||
/// </summary>
|
||||
public string BucketName { get; set; } = "signals-data";
|
||||
|
||||
/// <summary>
|
||||
/// Root prefix within the bucket for callgraph artifacts.
|
||||
/// </summary>
|
||||
public string RootPrefix { get; set; } = "callgraphs";
|
||||
|
||||
/// <summary>
|
||||
/// RustFS-specific options.
|
||||
/// </summary>
|
||||
public SignalsRustFsOptions RustFs { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Additional headers to include in storage requests.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the filesystem driver is configured.
|
||||
/// </summary>
|
||||
public bool IsFileSystemDriver()
|
||||
=> string.Equals(Driver, SignalsStorageDrivers.FileSystem, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the RustFS driver is configured.
|
||||
/// </summary>
|
||||
public bool IsRustFsDriver()
|
||||
=> string.Equals(Driver, SignalsStorageDrivers.RustFs, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configured values.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (!IsFileSystemDriver() && !IsRustFsDriver())
|
||||
{
|
||||
throw new InvalidOperationException($"Signals storage driver '{Driver}' is not supported. Use '{SignalsStorageDrivers.FileSystem}' or '{SignalsStorageDrivers.RustFs}'.");
|
||||
}
|
||||
|
||||
if (IsFileSystemDriver() && string.IsNullOrWhiteSpace(RootPath))
|
||||
{
|
||||
throw new InvalidOperationException("Signals artifact storage path must be configured for filesystem driver.");
|
||||
}
|
||||
|
||||
if (IsRustFsDriver())
|
||||
{
|
||||
RustFs ??= new SignalsRustFsOptions();
|
||||
RustFs.Validate();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BucketName))
|
||||
{
|
||||
throw new InvalidOperationException("Signals storage bucket name must be configured for RustFS driver.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RustFS-specific configuration options.
|
||||
/// </summary>
|
||||
public sealed class SignalsRustFsOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Base URL for the RustFS service (e.g., http://localhost:8180/api/v1).
|
||||
/// </summary>
|
||||
public string BaseUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Allow insecure TLS connections (development only).
|
||||
/// </summary>
|
||||
public bool AllowInsecureTls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// API key for authentication.
|
||||
/// </summary>
|
||||
public string? ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Header name for the API key (e.g., "X-API-Key").
|
||||
/// </summary>
|
||||
public string ApiKeyHeader { get; set; } = "X-API-Key";
|
||||
|
||||
/// <summary>
|
||||
/// HTTP request timeout.
|
||||
/// </summary>
|
||||
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(60);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configured values.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(BaseUrl))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must be configured.");
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(BaseUrl, UriKind.Absolute, out var uri))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must use HTTP or HTTPS.");
|
||||
}
|
||||
|
||||
if (Timeout <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("RustFS timeout must be greater than zero.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ApiKeyHeader) && string.IsNullOrWhiteSpace(ApiKey))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS API key header name requires a non-empty API key.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supported storage driver names.
|
||||
/// </summary>
|
||||
public static class SignalsStorageDrivers
|
||||
{
|
||||
public const string FileSystem = "filesystem";
|
||||
public const string RustFs = "rustfs";
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.Signals.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Authority configuration for the Signals service.
|
||||
/// </summary>
|
||||
public sealed class SignalsAuthorityOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables Authority-backed authentication.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows header-based development fallback when Authority is disabled.
|
||||
/// </summary>
|
||||
public bool AllowAnonymousFallback { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Authority issuer URL.
|
||||
/// </summary>
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether HTTPS metadata is required.
|
||||
/// </summary>
|
||||
public bool RequireHttpsMetadata { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Optional metadata address override.
|
||||
/// </summary>
|
||||
public string? MetadataAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Back-channel timeout (seconds).
|
||||
/// </summary>
|
||||
public int BackchannelTimeoutSeconds { get; set; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Token clock skew allowance (seconds).
|
||||
/// </summary>
|
||||
public int TokenClockSkewSeconds { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
/// Accepted token audiences.
|
||||
/// </summary>
|
||||
public IList<string> Audiences { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Required scopes.
|
||||
/// </summary>
|
||||
public IList<string> RequiredScopes { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Required tenants.
|
||||
/// </summary>
|
||||
public IList<string> RequiredTenants { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Networks allowed to bypass scope enforcement.
|
||||
/// </summary>
|
||||
public IList<string> BypassNetworks { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configured options.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Issuer))
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority issuer must be configured when Authority integration is enabled.");
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(Issuer.Trim(), UriKind.Absolute, out var issuerUri))
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority issuer must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (RequireHttpsMetadata && !issuerUri.IsLoopback && !string.Equals(issuerUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority issuer must use HTTPS unless running on loopback.");
|
||||
}
|
||||
|
||||
if (BackchannelTimeoutSeconds <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority back-channel timeout must be greater than zero seconds.");
|
||||
}
|
||||
|
||||
if (TokenClockSkewSeconds < 0 || TokenClockSkewSeconds > 300)
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority token clock skew must be between 0 and 300 seconds.");
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.Signals.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Authority configuration for the Signals service.
|
||||
/// </summary>
|
||||
public sealed class SignalsAuthorityOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables Authority-backed authentication.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows header-based development fallback when Authority is disabled.
|
||||
/// </summary>
|
||||
public bool AllowAnonymousFallback { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Authority issuer URL.
|
||||
/// </summary>
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether HTTPS metadata is required.
|
||||
/// </summary>
|
||||
public bool RequireHttpsMetadata { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Optional metadata address override.
|
||||
/// </summary>
|
||||
public string? MetadataAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Back-channel timeout (seconds).
|
||||
/// </summary>
|
||||
public int BackchannelTimeoutSeconds { get; set; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Token clock skew allowance (seconds).
|
||||
/// </summary>
|
||||
public int TokenClockSkewSeconds { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
/// Accepted token audiences.
|
||||
/// </summary>
|
||||
public IList<string> Audiences { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Required scopes.
|
||||
/// </summary>
|
||||
public IList<string> RequiredScopes { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Required tenants.
|
||||
/// </summary>
|
||||
public IList<string> RequiredTenants { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Networks allowed to bypass scope enforcement.
|
||||
/// </summary>
|
||||
public IList<string> BypassNetworks { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configured options.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Issuer))
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority issuer must be configured when Authority integration is enabled.");
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(Issuer.Trim(), UriKind.Absolute, out var issuerUri))
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority issuer must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (RequireHttpsMetadata && !issuerUri.IsLoopback && !string.Equals(issuerUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority issuer must use HTTPS unless running on loopback.");
|
||||
}
|
||||
|
||||
if (BackchannelTimeoutSeconds <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority back-channel timeout must be greater than zero seconds.");
|
||||
}
|
||||
|
||||
if (TokenClockSkewSeconds < 0 || TokenClockSkewSeconds > 300)
|
||||
{
|
||||
throw new InvalidOperationException("Signals Authority token clock skew must be between 0 and 300 seconds.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using StellaOps.Signals.Routing;
|
||||
|
||||
namespace StellaOps.Signals.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Applies Signals-specific defaults to <see cref="SignalsAuthorityOptions"/>.
|
||||
/// </summary>
|
||||
internal static class SignalsAuthorityOptionsConfigurator
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures required defaults are populated.
|
||||
/// </summary>
|
||||
public static void ApplyDefaults(SignalsAuthorityOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
if (!options.Audiences.Any())
|
||||
{
|
||||
options.Audiences.Add("api://signals");
|
||||
}
|
||||
|
||||
EnsureScope(options, SignalsPolicies.Read);
|
||||
EnsureScope(options, SignalsPolicies.Write);
|
||||
EnsureScope(options, SignalsPolicies.Admin);
|
||||
}
|
||||
|
||||
private static void EnsureScope(SignalsAuthorityOptions options, string scope)
|
||||
{
|
||||
if (options.RequiredScopes.Any(existing => string.Equals(existing, scope, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options.RequiredScopes.Add(scope);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Linq;
|
||||
using StellaOps.Signals.Routing;
|
||||
|
||||
namespace StellaOps.Signals.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Applies Signals-specific defaults to <see cref="SignalsAuthorityOptions"/>.
|
||||
/// </summary>
|
||||
internal static class SignalsAuthorityOptionsConfigurator
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures required defaults are populated.
|
||||
/// </summary>
|
||||
public static void ApplyDefaults(SignalsAuthorityOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
if (!options.Audiences.Any())
|
||||
{
|
||||
options.Audiences.Add("api://signals");
|
||||
}
|
||||
|
||||
EnsureScope(options, SignalsPolicies.Read);
|
||||
EnsureScope(options, SignalsPolicies.Write);
|
||||
EnsureScope(options, SignalsPolicies.Admin);
|
||||
}
|
||||
|
||||
private static void EnsureScope(SignalsAuthorityOptions options, string scope)
|
||||
{
|
||||
if (options.RequiredScopes.Any(existing => string.Equals(existing, scope, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options.RequiredScopes.Add(scope);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user