search and ai stabilization work, localization stablized.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -60,7 +61,7 @@ public sealed class AdvisoryAiRemoteInferenceOptions
|
||||
|
||||
if (_allowedProfiles.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Authority configuration requires at least one advisory AI remote inference profile when remote inference is enabled.");
|
||||
throw new InvalidOperationException(_t("config.authority.remote_inference_required"));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -38,29 +39,29 @@ public sealed class AdvisoryAiTenantRemoteInferenceOptions
|
||||
|
||||
if (remoteOptions is null || !remoteOptions.Enabled)
|
||||
{
|
||||
throw new InvalidOperationException("Tenant remote inference consent cannot be granted when remote inference is disabled.");
|
||||
throw new InvalidOperationException(_t("config.tenant.remote_inference_disabled"));
|
||||
}
|
||||
|
||||
if (ConsentVersion is { Length: > MaxConsentVersionLength })
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant remote inference consentVersion must be {MaxConsentVersionLength} characters or fewer.");
|
||||
throw new InvalidOperationException(_t("config.tenant.remote_inference_consent_version_length", MaxConsentVersionLength));
|
||||
}
|
||||
|
||||
if (ConsentedBy is { Length: > MaxConsentedByLength })
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant remote inference consentedBy must be {MaxConsentedByLength} characters or fewer.");
|
||||
throw new InvalidOperationException(_t("config.tenant.remote_inference_consented_by_length", MaxConsentedByLength));
|
||||
}
|
||||
|
||||
if (remoteOptions.RequireTenantConsent)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ConsentVersion))
|
||||
{
|
||||
throw new InvalidOperationException("Tenant remote inference consent requires consentVersion when consentGranted is true.");
|
||||
throw new InvalidOperationException(_t("config.tenant.remote_inference_consent_version_required"));
|
||||
}
|
||||
|
||||
if (!ConsentedAt.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("Tenant remote inference consent requires consentedAt when consentGranted is true.");
|
||||
throw new InvalidOperationException(_t("config.tenant.remote_inference_consented_at_required"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using StellaOps.Cryptography;
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -14,27 +15,27 @@ public sealed partial class AuthorityAckTokenOptions
|
||||
|
||||
if (string.IsNullOrWhiteSpace(PayloadType))
|
||||
{
|
||||
throw new InvalidOperationException("notifications.ackTokens.payloadType must be specified when ack tokens are enabled.");
|
||||
throw new InvalidOperationException(_t("config.ack_token.payload_type_required"));
|
||||
}
|
||||
|
||||
if (DefaultLifetime <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("notifications.ackTokens.defaultLifetime must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.ack_token.default_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (MaxLifetime <= TimeSpan.Zero || MaxLifetime < DefaultLifetime)
|
||||
{
|
||||
throw new InvalidOperationException("notifications.ackTokens.maxLifetime must be greater than zero and greater than or equal to defaultLifetime.");
|
||||
throw new InvalidOperationException(_t("config.ack_token.max_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ActiveKeyId))
|
||||
{
|
||||
throw new InvalidOperationException("notifications.ackTokens.activeKeyId must be provided when ack tokens are enabled.");
|
||||
throw new InvalidOperationException(_t("config.ack_token.key_id_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(KeyPath))
|
||||
{
|
||||
throw new InvalidOperationException("notifications.ackTokens.keyPath must be provided when ack tokens are enabled.");
|
||||
throw new InvalidOperationException(_t("config.ack_token.key_path_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(KeySource))
|
||||
@@ -59,7 +60,7 @@ public sealed partial class AuthorityAckTokenOptions
|
||||
|
||||
if (JwksCacheLifetime <= TimeSpan.Zero || JwksCacheLifetime > TimeSpan.FromHours(1))
|
||||
{
|
||||
throw new InvalidOperationException("notifications.ackTokens.jwksCacheLifetime must be between 00:00:01 and 01:00:00.");
|
||||
throw new InvalidOperationException(_t("config.ack_token.jwks_cache_range"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -58,7 +59,7 @@ public sealed class AuthorityLegacyAuthEndpointOptions
|
||||
|
||||
if (normalizedSunset <= normalizedDeprecation)
|
||||
{
|
||||
throw new InvalidOperationException("Legacy auth sunset date must be after the deprecation date.");
|
||||
throw new InvalidOperationException(_t("config.api_lifecycle.sunset_after_deprecation"));
|
||||
}
|
||||
|
||||
DeprecationDate = normalizedDeprecation;
|
||||
@@ -69,7 +70,7 @@ public sealed class AuthorityLegacyAuthEndpointOptions
|
||||
if (!Uri.TryCreate(DocumentationUrl, UriKind.Absolute, out var uri) ||
|
||||
(uri.Scheme != Uri.UriSchemeHttps && uri.Scheme != Uri.UriSchemeHttp))
|
||||
{
|
||||
throw new InvalidOperationException("Legacy auth documentation URL must be an absolute HTTP or HTTPS URL.");
|
||||
throw new InvalidOperationException(_t("config.api_lifecycle.docs_url_format"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -28,12 +29,12 @@ public sealed class AuthorityBootstrapOptions
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ApiKey))
|
||||
{
|
||||
throw new InvalidOperationException("Authority bootstrap configuration requires an API key when enabled.");
|
||||
throw new InvalidOperationException(_t("config.bootstrap.api_key_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(DefaultIdentityProvider))
|
||||
{
|
||||
throw new InvalidOperationException("Authority bootstrap configuration requires a default identity provider name when enabled.");
|
||||
throw new InvalidOperationException(_t("config.bootstrap.idp_required"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -44,7 +45,7 @@ public sealed class AuthorityDelegationOptions
|
||||
|
||||
if (!seenAccounts.Add(account.AccountId))
|
||||
{
|
||||
throw new InvalidOperationException($"Delegation configuration contains duplicate service account id '{account.AccountId}'.");
|
||||
throw new InvalidOperationException(_t("config.delegation.duplicate_account", account.AccountId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -10,7 +11,7 @@ public sealed class AuthorityDelegationQuotaOptions
|
||||
{
|
||||
if (MaxActiveTokens <= 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Authority delegation configuration requires {propertyName}.{nameof(MaxActiveTokens)} to be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.delegation.quota_max_tokens", $"{propertyName}.{nameof(MaxActiveTokens)}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -30,29 +31,29 @@ public sealed class AuthorityDpopNonceOptions
|
||||
{
|
||||
if (Ttl <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.Nonce.Ttl must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.nonce_ttl_invalid"));
|
||||
}
|
||||
|
||||
if (MaxIssuancePerMinute < 1)
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.Nonce.MaxIssuancePerMinute must be at least 1.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.nonce_max_issuance_invalid"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Store))
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.Nonce.Store must be specified.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.nonce_store_required"));
|
||||
}
|
||||
|
||||
Store = Store.Trim().ToLowerInvariant();
|
||||
|
||||
if (Store is not ("memory" or "redis"))
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.Nonce.Store must be either 'memory' or 'redis'.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.nonce_store_invalid"));
|
||||
}
|
||||
|
||||
if (Store == "redis" && string.IsNullOrWhiteSpace(RedisConnectionString))
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.Nonce.RedisConnectionString must be provided when using the 'redis' store.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.nonce_redis_required"));
|
||||
}
|
||||
|
||||
var normalizedAudiences = _requiredAudiences
|
||||
@@ -62,7 +63,7 @@ public sealed class AuthorityDpopNonceOptions
|
||||
|
||||
if (normalizedAudiences.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.Nonce.RequiredAudiences must include at least one audience.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.nonce_audiences_required"));
|
||||
}
|
||||
|
||||
_requiredAudiences.Clear();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -35,22 +36,22 @@ public sealed class AuthorityDpopOptions
|
||||
{
|
||||
if (ProofLifetime <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.ProofLifetime must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.proof_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (AllowedClockSkew < TimeSpan.Zero || AllowedClockSkew > TimeSpan.FromMinutes(5))
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.AllowedClockSkew must be between 0 and 5 minutes.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.clock_skew_invalid"));
|
||||
}
|
||||
|
||||
if (ReplayWindow < TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Dpop.ReplayWindow must be greater than or equal to zero.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.replay_window_invalid"));
|
||||
}
|
||||
|
||||
if (_allowedAlgorithms.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("At least one DPoP algorithm must be configured.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.algorithm_required"));
|
||||
}
|
||||
|
||||
NormalizedAlgorithms = _allowedAlgorithms
|
||||
@@ -60,7 +61,7 @@ public sealed class AuthorityDpopOptions
|
||||
|
||||
if (NormalizedAlgorithms.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Allowed DPoP algorithms cannot be empty after normalization.");
|
||||
throw new InvalidOperationException(_t("auth.dpop.algorithm_empty_after_normalization"));
|
||||
}
|
||||
|
||||
Nonce.Validate();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.RateLimiting;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -39,17 +40,17 @@ public sealed class AuthorityEndpointRateLimitOptions
|
||||
|
||||
if (PermitLimit <= 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Authority rate limiting '{name}' requires permitLimit to be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.rate_limit.permit_limit", name));
|
||||
}
|
||||
|
||||
if (QueueLimit < 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Authority rate limiting '{name}' queueLimit cannot be negative.");
|
||||
throw new InvalidOperationException(_t("config.rate_limit.queue_limit", name));
|
||||
}
|
||||
|
||||
if (Window <= TimeSpan.Zero || Window > TimeSpan.FromHours(1))
|
||||
{
|
||||
throw new InvalidOperationException($"Authority rate limiting '{name}' window must be greater than zero and no more than one hour.");
|
||||
throw new InvalidOperationException(_t("config.rate_limit.window", name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -21,7 +22,7 @@ public sealed class AuthorityEscalationOptions
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Scope))
|
||||
{
|
||||
throw new InvalidOperationException("notifications.escalation.scope must be specified.");
|
||||
throw new InvalidOperationException(_t("config.escalation.scope_required"));
|
||||
}
|
||||
|
||||
Scope = Scope.Trim().ToLowerInvariant();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -35,12 +36,12 @@ public sealed class AuthorityExceptionRoutingTemplateOptions
|
||||
{
|
||||
if (string.IsNullOrEmpty(Id))
|
||||
{
|
||||
throw new InvalidOperationException("Authority exception routing templates require an id.");
|
||||
throw new InvalidOperationException(_t("config.exceptions.template_id_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(AuthorityRouteId))
|
||||
{
|
||||
throw new InvalidOperationException($"Authority exception routing template '{Id}' requires authorityRouteId.");
|
||||
throw new InvalidOperationException(_t("config.exceptions.template_route_required", Id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -39,7 +40,7 @@ public sealed class AuthorityExceptionsOptions
|
||||
{
|
||||
if (templateOptions is null)
|
||||
{
|
||||
throw new InvalidOperationException("Authority exception routing template entries must not be null.");
|
||||
throw new InvalidOperationException(_t("config.exceptions.null_template"));
|
||||
}
|
||||
|
||||
templateOptions.Normalize();
|
||||
@@ -48,7 +49,7 @@ public sealed class AuthorityExceptionsOptions
|
||||
var template = templateOptions.ToImmutable();
|
||||
if (!normalized.TryAdd(template.Id, template))
|
||||
{
|
||||
throw new InvalidOperationException($"Authority exception routing template '{template.Id}' is configured more than once.");
|
||||
throw new InvalidOperationException(_t("config.exceptions.duplicate_template", template.Id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -42,7 +43,7 @@ public sealed class AuthorityMtlsOptions
|
||||
{
|
||||
if (RotationGrace < TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Mtls.RotationGrace must be non-negative.");
|
||||
throw new InvalidOperationException(_t("config.mtls.rotation_grace_negative"));
|
||||
}
|
||||
|
||||
NormalizedAudiences = _enforceForAudiences
|
||||
@@ -52,12 +53,12 @@ public sealed class AuthorityMtlsOptions
|
||||
|
||||
if (Enabled && NormalizedAudiences.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Mtls.EnforceForAudiences must include at least one audience when enabled.");
|
||||
throw new InvalidOperationException(_t("config.mtls.audiences_required"));
|
||||
}
|
||||
|
||||
if (AllowedCertificateAuthorities.Any(static path => string.IsNullOrWhiteSpace(path)))
|
||||
{
|
||||
throw new InvalidOperationException("Mtls.AllowedCertificateAuthorities entries must not be empty.");
|
||||
throw new InvalidOperationException(_t("config.mtls.ca_empty"));
|
||||
}
|
||||
|
||||
NormalizedSanTypes = _allowedSanTypes
|
||||
@@ -68,7 +69,7 @@ public sealed class AuthorityMtlsOptions
|
||||
|
||||
if (Enabled && NormalizedSanTypes.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Mtls.AllowedSanTypes must include at least one entry when enabled.");
|
||||
throw new InvalidOperationException(_t("config.mtls.san_types_required"));
|
||||
}
|
||||
|
||||
var compiledPatterns = new List<Regex>(AllowedSubjectPatterns.Count);
|
||||
@@ -77,7 +78,7 @@ public sealed class AuthorityMtlsOptions
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pattern))
|
||||
{
|
||||
throw new InvalidOperationException("Mtls.AllowedSubjectPatterns entries must not be empty.");
|
||||
throw new InvalidOperationException(_t("config.mtls.subject_patterns_empty"));
|
||||
}
|
||||
|
||||
try
|
||||
@@ -86,7 +87,7 @@ public sealed class AuthorityMtlsOptions
|
||||
}
|
||||
catch (RegexParseException ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Mtls.AllowedSubjectPatterns entry '{pattern}' is not a valid regular expression.", ex);
|
||||
throw new InvalidOperationException(_t("config.mtls.subject_pattern_invalid", pattern), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -30,7 +31,7 @@ public sealed class AuthorityPluginSettings
|
||||
{
|
||||
if (descriptor is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Authority plugin descriptor '{name}' is null.");
|
||||
throw new InvalidOperationException(_t("config.plugin.descriptor_null", name));
|
||||
}
|
||||
|
||||
descriptor.Normalize(name);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -41,17 +42,17 @@ public sealed class AuthoritySealedModeOptions
|
||||
|
||||
if (string.IsNullOrWhiteSpace(EvidencePath))
|
||||
{
|
||||
throw new InvalidOperationException("AirGap.SealedMode.EvidencePath must be provided when enforcement is enabled.");
|
||||
throw new InvalidOperationException(_t("config.sealed_mode.evidence_path_required"));
|
||||
}
|
||||
|
||||
if (MaxEvidenceAge <= TimeSpan.Zero || MaxEvidenceAge > TimeSpan.FromDays(7))
|
||||
{
|
||||
throw new InvalidOperationException("AirGap.SealedMode.MaxEvidenceAge must be between 00:00:01 and 7.00:00:00.");
|
||||
throw new InvalidOperationException(_t("config.sealed_mode.max_age_range"));
|
||||
}
|
||||
|
||||
if (CacheLifetime <= TimeSpan.Zero || CacheLifetime > MaxEvidenceAge)
|
||||
{
|
||||
throw new InvalidOperationException("AirGap.SealedMode.CacheLifetime must be greater than zero and less than or equal to AirGap.SealedMode.MaxEvidenceAge.");
|
||||
throw new InvalidOperationException(_t("config.sealed_mode.cache_lifetime_range"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -18,27 +19,27 @@ public sealed partial class AuthorityServiceAccountSeedOptions
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(AccountId))
|
||||
{
|
||||
throw new InvalidOperationException("Delegation service account seeds require an accountId.");
|
||||
throw new InvalidOperationException(_t("config.service_account.id_required"));
|
||||
}
|
||||
|
||||
if (!_accountIdRegex.IsMatch(AccountId))
|
||||
{
|
||||
throw new InvalidOperationException($"Service account id '{AccountId}' must contain lowercase letters, digits, colon, underscore, or hyphen.");
|
||||
throw new InvalidOperationException(_t("config.service_account.id_format", AccountId));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Tenant))
|
||||
{
|
||||
throw new InvalidOperationException($"Service account '{AccountId}' requires a tenant assignment.");
|
||||
throw new InvalidOperationException(_t("config.service_account.tenant_required", AccountId));
|
||||
}
|
||||
|
||||
if (tenantIds.Count > 0 && !tenantIds.Contains(Tenant))
|
||||
{
|
||||
throw new InvalidOperationException($"Service account '{AccountId}' references unknown tenant '{Tenant}'.");
|
||||
throw new InvalidOperationException(_t("config.service_account.tenant_unknown", AccountId, Tenant));
|
||||
}
|
||||
|
||||
if (AllowedScopes.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Service account '{AccountId}' must specify at least one allowed scope.");
|
||||
throw new InvalidOperationException(_t("config.service_account.scope_required", AccountId));
|
||||
}
|
||||
|
||||
if (Attributes.Count > 0)
|
||||
@@ -47,7 +48,7 @@ public sealed partial class AuthorityServiceAccountSeedOptions
|
||||
{
|
||||
if (!_allowedAttributeKeys.Contains(attributeName))
|
||||
{
|
||||
throw new InvalidOperationException($"Service account '{AccountId}' defines unsupported attribute '{attributeName}'. Allowed attributes: env, owner, business_tier.");
|
||||
throw new InvalidOperationException(_t("config.service_account.unsupported_attribute", AccountId, attributeName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -14,12 +15,12 @@ public sealed class AuthoritySigningAdditionalKeyOptions
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(KeyId))
|
||||
{
|
||||
throw new InvalidOperationException("Additional signing keys require a keyId.");
|
||||
throw new InvalidOperationException(_t("config.signing.additional_key_id_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Path))
|
||||
{
|
||||
throw new InvalidOperationException($"Signing key '{KeyId}' requires a path.");
|
||||
throw new InvalidOperationException(_t("config.signing.additional_key_path_required", KeyId));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Source))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using StellaOps.Cryptography;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -62,12 +63,12 @@ public sealed class AuthoritySigningOptions
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ActiveKeyId))
|
||||
{
|
||||
throw new InvalidOperationException("Authority signing configuration requires signing.activeKeyId.");
|
||||
throw new InvalidOperationException(_t("config.signing.key_id_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(KeyPath))
|
||||
{
|
||||
throw new InvalidOperationException("Authority signing configuration requires signing.keyPath.");
|
||||
throw new InvalidOperationException(_t("config.signing.key_path_required"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Algorithm))
|
||||
@@ -87,7 +88,7 @@ public sealed class AuthoritySigningOptions
|
||||
|
||||
if (JwksCacheLifetime <= TimeSpan.Zero || JwksCacheLifetime > TimeSpan.FromHours(1))
|
||||
{
|
||||
throw new InvalidOperationException("Authority signing configuration requires signing.jwksCacheLifetime to be between 00:00:01 and 01:00:00.");
|
||||
throw new InvalidOperationException(_t("config.signing.jwks_cache_range"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -23,12 +24,12 @@ public sealed class AuthorityStorageOptions
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ConnectionString))
|
||||
{
|
||||
throw new InvalidOperationException("Authority storage requires a connection string.");
|
||||
throw new InvalidOperationException(_t("config.storage.connection_required"));
|
||||
}
|
||||
|
||||
if (CommandTimeout <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Authority storage command timeout must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.storage.timeout_invalid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -17,7 +18,7 @@ public sealed class AuthorityTenantDelegationOptions
|
||||
|
||||
if (MaxActiveTokens is { } value && value <= 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant '{tenantId}' delegation maxActiveTokens must be greater than zero when specified.");
|
||||
throw new InvalidOperationException(_t("config.tenant.delegation_max_tokens", tenantId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -12,12 +13,12 @@ public sealed partial class AuthorityTenantOptions
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Id))
|
||||
{
|
||||
throw new InvalidOperationException("Each tenant requires an id (slug).");
|
||||
throw new InvalidOperationException(_t("config.tenant.id_required"));
|
||||
}
|
||||
|
||||
if (!_tenantSlugRegex.IsMatch(Id))
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant id '{Id}' must contain only lowercase letters, digits, and hyphen.");
|
||||
throw new InvalidOperationException(_t("config.tenant.id_format", Id));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(DisplayName))
|
||||
@@ -31,7 +32,7 @@ public sealed partial class AuthorityTenantOptions
|
||||
{
|
||||
if (!_projectSlugRegex.IsMatch(project))
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant '{Id}' defines project '{project}' which must contain only lowercase letters, digits, and hyphen.");
|
||||
throw new InvalidOperationException(_t("config.tenant.project_format", Id, project));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +46,7 @@ public sealed partial class AuthorityTenantOptions
|
||||
{
|
||||
if (roleOptions is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant '{Id}' defines role '{roleName}' without configuration.");
|
||||
throw new InvalidOperationException(_t("config.tenant.role_missing_config", Id, roleName));
|
||||
}
|
||||
|
||||
roleOptions.Validate(Id, roleName);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -17,14 +18,14 @@ public sealed partial class AuthorityTenantRoleOptions
|
||||
{
|
||||
if (Scopes.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant '{tenantId}' role '{roleName}' must specify at least one scope.");
|
||||
throw new InvalidOperationException(_t("config.tenant.role_scope_required", tenantId, roleName));
|
||||
}
|
||||
|
||||
foreach (var scope in Scopes)
|
||||
{
|
||||
if (!StellaOpsScopes.IsKnown(scope))
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant '{tenantId}' role '{roleName}' references unknown scope '{scope}'.");
|
||||
throw new InvalidOperationException(_t("config.tenant.role_unknown_scope", tenantId, roleName, scope));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +35,7 @@ public sealed partial class AuthorityTenantRoleOptions
|
||||
{
|
||||
if (!_allowedAttributeKeys.Contains(attributeName))
|
||||
{
|
||||
throw new InvalidOperationException($"Tenant '{tenantId}' role '{roleName}' defines unsupported attribute '{attributeName}'. Allowed attributes: env, owner, business_tier.");
|
||||
throw new InvalidOperationException(_t("config.tenant.role_unsupported_attribute", tenantId, roleName, attributeName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -46,27 +47,27 @@ public sealed class AuthorityVulnAntiForgeryOptions
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Audience))
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.workflow.antiForgery.audience must be specified when anti-forgery tokens are enabled.");
|
||||
throw new InvalidOperationException(_t("config.anti_forgery.audience_required"));
|
||||
}
|
||||
|
||||
if (DefaultLifetime <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.workflow.antiForgery.defaultLifetime must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.anti_forgery.default_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (MaxLifetime <= TimeSpan.Zero || MaxLifetime < DefaultLifetime)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.workflow.antiForgery.maxLifetime must be greater than zero and greater than or equal to defaultLifetime.");
|
||||
throw new InvalidOperationException(_t("config.anti_forgery.max_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (MaxContextEntries < 0)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.workflow.antiForgery.maxContextEntries must be non-negative.");
|
||||
throw new InvalidOperationException(_t("config.anti_forgery.max_context_entries"));
|
||||
}
|
||||
|
||||
if (MaxContextValueLength <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.workflow.antiForgery.maxContextValueLength must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.anti_forgery.max_context_value_length"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -46,27 +47,27 @@ public sealed class AuthorityVulnAttachmentOptions
|
||||
|
||||
if (DefaultLifetime <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.attachments.defaultLifetime must be greater than zero when attachment tokens are enabled.");
|
||||
throw new InvalidOperationException(_t("config.attachment.default_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (MaxLifetime <= TimeSpan.Zero || MaxLifetime < DefaultLifetime)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.attachments.maxLifetime must be greater than zero and greater than or equal to defaultLifetime.");
|
||||
throw new InvalidOperationException(_t("config.attachment.max_lifetime_invalid"));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(PayloadType))
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.attachments.payloadType must be specified when attachment tokens are enabled.");
|
||||
throw new InvalidOperationException(_t("config.attachment.payload_type_required"));
|
||||
}
|
||||
|
||||
if (MaxMetadataEntries < 0)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.attachments.maxMetadataEntries must be non-negative.");
|
||||
throw new InvalidOperationException(_t("config.attachment.max_metadata_entries"));
|
||||
}
|
||||
|
||||
if (MaxMetadataValueLength <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("vulnerabilityExplorer.attachments.maxMetadataValueLength must be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.attachment.max_metadata_value_length"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -35,7 +36,7 @@ public sealed class AuthorityWebhookAllowlistOptions
|
||||
|
||||
if (_allowedHosts.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("notifications.webhooks.allowedHosts must include at least one host when enabled.");
|
||||
throw new InvalidOperationException(_t("config.webhook.hosts_required"));
|
||||
}
|
||||
|
||||
NormalizeList(_allowedHosts);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<ProjectReference Include="..\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Cryptography.Plugin.Pkcs11Gost\StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Cryptography.DependencyInjection\StellaOps.Cryptography.DependencyInjection.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Localization\StellaOps.Localization.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(StellaOpsEnableCryptoPro)' == 'true'">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -9,12 +10,12 @@ public sealed partial class StellaOpsAuthorityOptions
|
||||
{
|
||||
if (value <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"Authority configuration requires {propertyName} to be greater than zero.");
|
||||
throw new InvalidOperationException(_t("config.authority.property_greater_than_zero", propertyName));
|
||||
}
|
||||
|
||||
if (value > maximum)
|
||||
{
|
||||
throw new InvalidOperationException($"Authority configuration requires {propertyName} to be less than or equal to {maximum}.");
|
||||
throw new InvalidOperationException(_t("config.authority.property_max", propertyName, maximum));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Configuration;
|
||||
|
||||
@@ -13,22 +14,22 @@ public sealed partial class StellaOpsAuthorityOptions
|
||||
{
|
||||
if (SchemaVersion <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Authority configuration requires a positive schemaVersion.");
|
||||
throw new InvalidOperationException(_t("config.authority.schema_version_required"));
|
||||
}
|
||||
|
||||
if (Issuer is null)
|
||||
{
|
||||
throw new InvalidOperationException("Authority configuration requires an issuer URL.");
|
||||
throw new InvalidOperationException(_t("config.authority.issuer_required"));
|
||||
}
|
||||
|
||||
if (!Issuer.IsAbsoluteUri)
|
||||
{
|
||||
throw new InvalidOperationException("Authority issuer must be an absolute URI.");
|
||||
throw new InvalidOperationException(_t("config.authority.issuer_absolute"));
|
||||
}
|
||||
|
||||
if (string.Equals(Issuer.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && !Issuer.IsLoopback)
|
||||
{
|
||||
throw new InvalidOperationException("Authority issuer must use HTTPS unless running on a loopback interface.");
|
||||
throw new InvalidOperationException(_t("config.authority.issuer_https"));
|
||||
}
|
||||
|
||||
ValidateLifetime(AccessTokenLifetime, nameof(AccessTokenLifetime), TimeSpan.FromHours(24));
|
||||
@@ -63,7 +64,7 @@ public sealed partial class StellaOpsAuthorityOptions
|
||||
tenant.Validate(AdvisoryAi, Delegation);
|
||||
if (!identifiers.Add(tenant.Id))
|
||||
{
|
||||
throw new InvalidOperationException($"Authority configuration contains duplicate tenant identifier '{tenant.Id}'.");
|
||||
throw new InvalidOperationException(_t("config.authority.duplicate_tenant", tenant.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user