using System.Text.Json; namespace SGU.CredentialProvider; internal sealed class ProviderSettings { private const int MaximumSettingsBytes = 16 * 1024; public Uri BrokerEndpoint { get; init; } = new("https://sgu-auth.lci.lasalle.mx:8443/v1/authenticate"); public string DomainNetbios { get; init; } = "LCI"; public int TimeoutSeconds { get; init; } = 35; public string ClientCertificateThumbprint { get; init; } = string.Empty; public string ServerCertificateThumbprint { get; init; } = string.Empty; public static string DefaultPath => Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "SGU", "CredentialProvider", "settings.json"); public static ProviderSettings Load(string? path = null) { path ??= Environment.GetEnvironmentVariable("SGU_CREDENTIAL_PROVIDER_CONFIG") ?? DefaultPath; FileInfo file = new(path); if (!file.Exists || file.Length is <= 0 or > MaximumSettingsBytes) { throw new InvalidOperationException("Credential Provider settings are missing or invalid."); } using FileStream stream = new(path, FileMode.Open, FileAccess.Read, FileShare.Read); ProviderSettings settings = JsonSerializer.Deserialize(stream, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? throw new InvalidOperationException("Credential Provider settings could not be read."); settings.Validate(); return settings; } public void Validate() { if (!BrokerEndpoint.IsAbsoluteUri || BrokerEndpoint.Scheme != Uri.UriSchemeHttps || !string.IsNullOrEmpty(BrokerEndpoint.UserInfo)) { throw new InvalidOperationException("BrokerEndpoint must be an absolute HTTPS URL without user information."); } if (!string.Equals(BrokerEndpoint.AbsolutePath.TrimEnd('/'), "/v1/authenticate", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("BrokerEndpoint must target /v1/authenticate."); } if (string.IsNullOrWhiteSpace(DomainNetbios) || TimeoutSeconds is < 2 or > 60) { throw new InvalidOperationException("DomainNetbios or TimeoutSeconds is invalid."); } if (NormalizeThumbprint(ClientCertificateThumbprint).Length != 40 || NormalizeThumbprint(ServerCertificateThumbprint).Length != 40) { throw new InvalidOperationException("Client and server SHA-1 certificate thumbprints are required."); } } public static string NormalizeThumbprint(string value) => value.Replace(" ", string.Empty, StringComparison.Ordinal).ToUpperInvariant(); }