using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; 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.Configuration; using xIdentityService.Constants; using xIdentityService.Interfaces; using xIdentityService.Models; namespace xIdentityService.Providers { public partial class XIdentityProvider : IXIdentityProvider { private readonly IXTokenProvider tokenProvider; private readonly IHostingEnvironment environment; private JsonSerializerSettings jsonSerializerSettings; private readonly XValidationProvider validationProvider; private readonly XIdentityServiceConfiguration identityConfiguration; public ILogger Logger { get; } public IXSecurityProvider SecurityProvider { get; } public string BaseUrl { get; } public string RevisionSecretKey { get; } public bool ReadResponseAsString { get; set; } public XIdentityProvider ( IXTokenProvider tokenProvider, IHostingEnvironment environment, ILogger logger, IXSecurityProvider securityProvider, XIdentityServiceConfiguration identityConfiguration, XValidationProvider validationProvider ) { // BaseUrl = identityConfiguration.Authority; RevisionSecretKey = identityConfiguration.XRevisionSecretKey; // this.environment = environment; Logger = logger; SecurityProvider = securityProvider; // this.identityConfiguration = identityConfiguration; this.validationProvider = validationProvider; this.jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver () }; // this.tokenProvider = tokenProvider; } // #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 = identityConfiguration.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 ( XApiAccountEndpoint 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, identityConfiguration.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 ) { // 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 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; }); // return response; } public async Task> RunRestRequestByResponse ( RestClient client, RestRequest request, XHttpMethod method, bool supportRefreshingTokens = true ) { // 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 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; }); // 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 } }