using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using RestSharp; using RestSharp.Authenticators; using xCommons.Constants; using xCommons.Extensions; using xCommons.Providers; using xExceptions.Constants; using xHttpService.Constants; using xIdentityModels.Constants; using xIdentityModels.Extensions; using xIdentityModels.Models; using xIdentityService.Constants; using xIdentityService.Interfaces; using xIdentityService.Models; namespace xAiService.Base { public interface IXBaseIdentityHttpProvider { string BaseUrl { get; } ILogger Logger { get; } string XPoweredValue { get; } int DefaultClientTimeout { get; } XValidationProvider ValidationProvider { get; } HttpClient GetHttpClient(); RestClient GetRestClient(XTokenResponse tokens = null); RestRequest GetRestRequet( Enum endpoint, bool addXPoweredValue = true, IDictionary @params = null, IDictionary headers = null, IDictionary queryStrings = null ); Task RunRestRequest( RestClient client, RestRequest request, XHttpMethod method, bool supportRefreshingTokens = true, CancellationToken cancellationToken = default ); Task> RunRestRequestByResponse( RestClient client, RestRequest request, XHttpMethod method, bool supportRefreshingTokens = true, CancellationToken cancellationToken = default ); string GetUserSelectByParam( XActionRequest model, bool forceNotNull = true, ICollection excludes = null ); } public abstract class XBaseIdentityHttpProvider : IXBaseIdentityHttpProvider { public string BaseUrl { get; } public ILogger Logger { get; } public string XPoweredValue { get; } public int DefaultClientTimeout { get; } public XValidationProvider ValidationProvider { get; } private readonly IXTokenProvider tokenProvider; private readonly IXIdentityProvider identityProvider; public XBaseIdentityHttpProvider( string baseUrl, ILogger logger, string poweredValue, int defaultClientTimeout, IXTokenProvider tokenProvider, IXIdentityProvider identityProvider, XValidationProvider validationProvider ) { // Logger = logger; BaseUrl = baseUrl; XPoweredValue = poweredValue; this.tokenProvider = tokenProvider; ValidationProvider = validationProvider; this.identityProvider = identityProvider; DefaultClientTimeout = defaultClientTimeout; } // #region Tools ... /// /// Get an Instance of Http Client /// /// public HttpClient GetHttpClient() { // HttpClient httpClient = null; // // httpClient = new HttpClient (); var httpClientHandler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { // Logger.LogInformation( $"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" ); // return true; }, ClientCertificateOptions = ClientCertificateOption.Manual, }; httpClient = new HttpClient(httpClientHandler); // // Set Timeout ... // TODO: Fix this ... // httpClient.Timeout = TimeSpan // .FromSeconds (identityConfiguration.DefaultClientTimeout); // return httpClient; } /// /// Get an Instance of RestClient /// /// public RestClient GetRestClient(XTokenResponse tokens = null) { // var client = new RestClient(BaseUrl); // // Set Authenticator ... if (!tokens.IsNull() && !tokens.AccessToken.IsNullOrEmpty()) { client.Authenticator = new JwtAuthenticator(tokens.AccessToken); } // // Refresh Token ... if (!tokens.IsNull() && !tokens.RefreshToken.IsNullOrEmpty()) { client.AddDefaultHeader(XAuthorization.RefreshToken, tokens.RefreshToken); } // // ExpiresAt ... if (!tokens.IsNull() && tokens.ExpiresAt > 0) { client.AddDefaultHeader(XAuthorization.ExpiresAt, tokens.ExpiresAt.AsHttpParamString()); } // // Set Default Timeout ... client.Timeout = DefaultClientTimeout; // // SSL Validation Handler ... client.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => { // Logger.LogInformation( $"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" ); // return true; }; // return client; } /// /// Get an Instance of RestRequest /// public RestRequest GetRestRequet( Enum endpoint, bool addXPoweredValue = true, IDictionary @params = null, IDictionary headers = null, IDictionary queryStrings = null ) { // var url = endpoint.GetStringValue(); // // Add Route Payloads ... if (!@params.IsNull() && @params.Count > 0) { // var enumerator = @params.GetEnumerator(); while (enumerator.MoveNext()) { // var item = enumerator.Current; // url = url.Replace(item.Key, item.Value.ToUrlEncoded()); } } // // Add Query Strings ... if (!queryStrings.IsNull() && queryStrings.Count > 0) { // url = $"{url}?"; var enumerator = queryStrings.GetEnumerator(); while (enumerator.MoveNext()) { // var item = enumerator.Current; // url += $"{item.Key}={item.Value.ToUrlEncoded()}&"; } // // remove lates & ... if (url.EndsWith("&")) { url = url.Substring(0, url.Length - 1); } } // // Create request ... var request = new RestRequest(url, DataFormat.Json); // if (addXPoweredValue) { request.AddHeader(XAuthorization.XPoweredBy, XPoweredValue); } // // Add Headers to request ... if (!headers.IsNull() && headers.Count > 0) { // var enumerator = headers.GetEnumerator(); while (enumerator.MoveNext()) { // var item = enumerator.Current; request.AddHeader(item.Key, item.Value); } } // return request; } /// /// Handle Call Specific Request and retrieve Response /// /// /// /// /// /// public async Task RunRestRequest( RestClient client, RestRequest request, XHttpMethod method, bool supportRefreshingTokens = true, CancellationToken cancellationToken = default ) { // var response = await Task.Run(async () => { // IRestResponse resp = null; // // Call Endpoint Service using Specified Method ... switch (method) { // // Get ... case XHttpMethod.GET: resp = client.Get(request); break; // // Put ... case XHttpMethod.PUT: resp = client.Put(request); break; // // Post ... case XHttpMethod.POST: resp = client.Post(request); break; // // Delete ... case XHttpMethod.DELETE: resp = client.Delete(request); break; } // if (!resp.IsSuccessful) { // // Log Exception ... Logger.LogError($"Error: {resp.ErrorMessage}"); Logger.LogError($"Exception: {resp.ErrorException.ToJSON()}"); if (!resp.ErrorMessage.IsNullOrEmpty() && resp.ErrorMessage.Contains("timed out")) { XException.Timeout.Throw(); } // // TODO: Check UnAuthorized Status here for // Refresh Tokens or Issue Correct Exception ... if (supportRefreshingTokens && resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { // var isRefreshed = request.Parameters.Any(rp => rp.Name == XParam.XIsRefreshed.GetStringValue() && rp.Value.ToString().ToNormalString() == true.ToJSON()); // Logger.LogInformation($"isRefreshed: {isRefreshed}"); // // Get Access Token ... XTokenResponse tokens = null; var accessToken = request.Parameters.FirstOrDefault(rp => rp.Name.ToNormalString() == XAuthorization.Header.ToNormalString()).Value.ToString(); if (!accessToken.IsNullOrEmpty()) { // accessToken = accessToken.Replace(XAuthorization.TokenIdentifier, ""); // if (!accessToken.IsNullOrEmpty()) { tokens = await tokenProvider.RetrieveToken(accessToken); } } // // Refresh Token and then re do request ... if (!isRefreshed && !accessToken.IsNullOrEmpty() && !tokens.IsNull() && tokens.IsRefreshable()) { // request.AddHeader(XParam.XIsRefreshed.GetStringValue(), true.ToJSON()); // // Refreshe Tokens ... var refreshedTokens = await identityProvider .RefreshTokens(tokens); // // Renew Rest Client with Refreshed Tokens ... var timeout = client.Timeout; client = GetRestClient(refreshedTokens); client.Timeout = Timeout.Infinite; // // Do Request Again using renew Client ... return await RunRestRequest( client: client, request: request, method: method); } } // var ex = resp.Content.ToXError(); if (!ex.IsNull()) { throw ex.ToException(); } // // UnAuthorized ... if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { XException.NotAuthorized.Throw(); } // // NotFound ... if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) { XException.NotFound.Throw(); } // XException.ActionFailed.Throw(); } // var result = resp.Content.FromJSON(); return result; }, cancellationToken: cancellationToken); // return response; } public async Task> RunRestRequestByResponse( RestClient client, RestRequest request, XHttpMethod method, bool supportRefreshingTokens = true, CancellationToken cancellationToken = default ) { // var response = await Task.Run(async () => { // IRestResponse resp = null; // // Call Endpoint Service using Specified Method ... switch (method) { // // Get ... case XHttpMethod.GET: resp = client.Get(request); break; // // Put ... case XHttpMethod.PUT: resp = client.Put(request); break; // // Post ... case XHttpMethod.POST: resp = client.Post(request); break; // // Delete ... case XHttpMethod.DELETE: resp = client.Delete(request); break; } // if (!resp.IsSuccessful) { // // Log Exception ... Logger.LogError($"Error: {resp.ErrorMessage}"); Logger.LogError($"Exception: {resp.ErrorException.ToJSON()}"); if (!resp.ErrorMessage.IsNullOrEmpty() && resp.ErrorMessage.Contains("timed out")) { XException.Timeout.Throw(); } // // TODO: Check UnAuthorized Status here for // Refresh Tokens or Issue Correct Exception ... if (supportRefreshingTokens && resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { // var isRefreshed = request.Parameters.Any(rp => rp.Name == XParam.XIsRefreshed.GetStringValue() && rp.Value.ToString().ToNormalString() == true.ToJSON()); // Logger.LogInformation($"isRefreshed: {isRefreshed}"); // // Get Access Token ... XTokenResponse tokens = null; var accessToken = request.Parameters.FirstOrDefault(rp => rp.Name.ToNormalString() == XAuthorization.Header.ToNormalString()).Value.ToString(); if (!accessToken.IsNullOrEmpty()) { // accessToken = accessToken.Replace(XAuthorization.TokenIdentifier, ""); // if (!accessToken.IsNullOrEmpty()) { tokens = await tokenProvider.RetrieveToken(accessToken); } } // // Refresh Token and then re do request ... if (!isRefreshed && !accessToken.IsNullOrEmpty() && !tokens.IsNull() && tokens.IsRefreshable()) { // request.AddHeader(XParam.XIsRefreshed.GetStringValue(), true.ToJSON()); // // Refreshe Tokens ... var refreshedTokens = await identityProvider .RefreshTokens(tokens); // // Renew Rest Client with Refreshed Tokens ... var timeout = client.Timeout; client = GetRestClient(refreshedTokens); client.Timeout = Timeout.Infinite; // // Do Request Again using renew Client ... return await RunRestRequestByResponse( client: client, request: request, method: method ); } } // var ex = resp.Content.ToXError(); if (!ex.IsNull()) { throw ex.ToException(); } // // UnAuthorized ... if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { XException.NotAuthorized.Throw(); } // // NotFound ... if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) { XException.NotFound.Throw(); } // XException.ActionFailed.Throw(); } // var result = new XBaseRestResponse { Response = resp, Model = resp.Content.FromJSON() }; // return result; }, cancellationToken: cancellationToken); // return response; } /// /// Get User SelectBy Param /// /// /// /// /// public string GetUserSelectByParam( XActionRequest model, bool forceNotNull = true, ICollection excludes = null ) { // // Validate Args ... ValidationProvider.NotNull(model); // var id = model.UserId; var userName = model.UserName; var email = model.Email; var phoneNumber = model.MobileNumber; // if (excludes != null) { // if (excludes.Contains(XUserSelectBy.ID)) { id = null; } if (excludes.Contains(XUserSelectBy.Email)) { email = null; } if (excludes.Contains(XUserSelectBy.MobileNumber)) { phoneNumber = null; } if (excludes.Contains(XUserSelectBy.Username)) { userName = null; } } var selectByParam = id.IsNullOrEmpty() ? userName.IsNullOrEmpty() ? email.IsNullOrEmpty() ? phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id; // // Check result ... if (forceNotNull && selectByParam.IsNullOrEmpty()) { XException.InvalidData.Throw(); } // return selectByParam; } #endregion } }