using System.Text; using adas_core.Application.Repositories.Interfaces; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.MongoModels; using adas_core.Domain.Models.Responses; using adas_core.Domain.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MongoDB.Bson; using Newtonsoft.Json; namespace adas_core.Application.Services; /// /// Provides an implementation of the interface, offering /// authentication-related services to consuming components. /// public class AuthService : IAuthService { private readonly IAuthorityRepository _authorityRepository; private readonly ILogger _logger; private readonly RecordingSettings _recordingSettings; //private LoginResponse? _loginResponse; public AuthService(IOptions recordingSettings, ILogger logger, IAuthorityRepository authorityRepository) { _recordingSettings = recordingSettings.Value ?? throw new Exception("RecordingSettings must be defined on appSettings"); _logger = logger; _authorityRepository = authorityRepository; _ = InstanceAuthUtils(); } /// /// Asynchronously obtains a login token from the recording API using the configured client credentials and caches it for reuse via . /// /// A containing the authentication token when the request succeeds and the response is valid; otherwise, null if the API URL is not configured, the request fails, the returned token is empty, or an exception is caught and logged. public async Task GetLoginResponse() { try { if (_recordingSettings.RecordingApiUrl.IsEmpty()) { _logger.LogInformation("Url not defined to get token on AuthUtils..."); return null; } var user = new { Username = _recordingSettings.RecordingOrApiClientId, Password = _recordingSettings.RecordingOrApiClientSecret }; var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true }; var client = new HttpClient(handler); var json = JsonConvert.SerializeObject(user); var data = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync($"{_recordingSettings.RecordingApiUrl}/users/login", data); var respToken = response.IsSuccessStatusCode ? await response.Content.ReadAsStringAsync() : null; if (respToken != null) { var ton = JsonConvert.DeserializeObject(respToken); if (ton != null && !ton.Token.IsEmpty()) { AuthUtils.Instance.LoginResponse = ton; return ton; } } return null; } catch (Exception e) { _logger.LogError("Error trying to get token: exception: {eMessage}", e.Message); return null; } } /// /// Retrieves an authentication token, returning the cached token if it is not empty or expired. /// Otherwise, attempts a fresh login and returns the new token, falling back to an empty string when no token is obtained. /// /// A task that resolves to the authentication token, or an empty string if the token could not be obtained. public async Task GetToken() { var loginResponse = AuthUtils.Instance.GetLoginResponse(); if (!loginResponse.Token.IsEmptyOrWhiteSpace() && !loginResponse.IsExpired()) return loginResponse.Token; var resp = await GetLoginResponse(); if (resp != null) return resp.Token; _logger.LogWarning("Failed Getting token check recordingSettings for user and pass"); return ""; } /// /// Retrieves a list of authorizations associated with the specified unit identifier by delegating to the authority repository. /// /// The unique identifier of the unit whose authorizations are being retrieved. /// A task that represents the asynchronous operation, containing a list of objects for the specified unit. public async Task> GetByUnitId(ObjectId unitId) { return await _authorityRepository.GetByUnitId(unitId); } /// /// Retrieves the list of authorizations associated with the specified user identifier. /// /// The unique identifier of the user whose authorities are being requested. /// A task that represents the asynchronous operation, containing a list of entries for the user. public async Task> GetUserAuthorities(ObjectId id) { return await _authorityRepository.GetUserAuthorities(id); } /// /// Deletes all authorities associated with the specified unit identifier by delegating to the authority repository. /// /// The identifier of the unit whose authorities are to be removed. /// A task that represents the asynchronous operation. The task result is true if the deletion was successful; otherwise, false. public async Task DeleteByUnitId(ObjectId unitId) { return await _authorityRepository.DeleteAllAuthoritiesByUnit(unitId); } /// /// Deletes all authorities associated with the specified display identifier by delegating to the authority repository. /// /// The unique identifier of the display whose related authorities should be removed. /// A task that resolves to true if authorities were successfully deleted; otherwise, false. public async Task DeleteByDisplayId(ObjectId displayId) { return await _authorityRepository.DeleteAllAuthoritiesByDisplay(displayId); } /// /// Ensures the authentication utilities are initialized with a valid login token. Returns early if the recording API URL is not configured; otherwise, requests a new token when the current one is missing or expired, and updates the shared instance with the refreshed response when successful. /// private async Task InstanceAuthUtils() { if (_recordingSettings.RecordingApiUrl.IsEmpty()) { _logger.LogInformation( "RecordingOrApiUrl not defined on appsettings RecordingSettings AuthUtils not instanciated"); return; } if (AuthUtils.Instance.GetLoginResponse().Token.IsEmpty() || AuthUtils.Instance.GetLoginResponse().IsExpired()) { _logger.LogInformation("Token is not generated or is expired sending request for new token"); var newLoginResponse = await GetLoginResponse(); if (newLoginResponse == null) { _logger.LogError("Token request is null check recordingSettings for user, pass and authorities"); } else { _logger.LogInformation("New Token generated at UTC DATE: {DateTime} exires at: {newLoginResponse}", DateTime.UtcNow, newLoginResponse.Expiration); AuthUtils.Instance.LoginResponse = newLoginResponse; } } } }