using System; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace FrameworkBLL.Entitlement { public class EntitlementGateClient : IEntitlementGateClient { private readonly IHttpClientFactory _HttpClientFactory; private readonly ILogger _Logger; public EntitlementGateClient(IHttpClientFactory httpClientFactory, ILogger logger) { _HttpClientFactory = httpClientFactory; _Logger = logger; } public async Task IsFeatureEnabledAsync(int clientId, int userId, string featureCode, CancellationToken ct = default) { try { var client = _HttpClientFactory.CreateClient("entitlement-internal"); var url = $"lic/Entitlement.svc/IsFeatureEnabled?ClientId={clientId}&UserId={userId}&FeatureCode={Uri.EscapeDataString(featureCode)}"; using var response = await client.GetAsync(url, ct).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { _Logger.LogWarning( "Entitlement check HTTP {StatusCode} for FeatureCode {FeatureCode} — failing open.", (int)response.StatusCode, featureCode); return true; } var json = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false); using var doc = JsonDocument.Parse(json); if (doc.RootElement.TryGetProperty("Body", out var bodyElement) && bodyElement.ValueKind is JsonValueKind.True or JsonValueKind.False) { return bodyElement.GetBoolean(); } _Logger.LogWarning( "Entitlement check returned an unexpected response shape for FeatureCode {FeatureCode} — failing open.", featureCode); return true; } catch (Exception ex) { // Fail open: an unreachable Entitlement service must not hide an entire // Standard portlet-type/dashboard list from every client. _Logger.LogError(ex, "Entitlement check failed for FeatureCode {FeatureCode}, ClientId {ClientId} — failing open.", featureCode, clientId); return true; } } } }