Files
xIdentityService/Providers/XIdentityProvider.cs
T
2025-07-17 06:42:00 +03:30

553 lines
20 KiB
C#

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<XIdentityProvider> 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<XIdentityProvider> 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 ...
/// <summary>
/// Get an Instance of Http Client
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Get an Instance of RestClient
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Get an Instance of RestRequest
/// </summary>
/// <param name="endpoint"></param>
/// <param name="params"></param>
/// <returns></returns>
public RestRequest GetRestRequet (
XApiAccountEndpoint endpoint,
IDictionary<string, string> @params = null,
bool addXPoweredValue = true,
IDictionary<string, string> headers = 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 ());
}
}
//
// 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;
}
/// <summary>
/// Handle Call Specific Request and retrieve Response
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="method"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public async Task<T> RunRestRequest<T> (
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<T> (
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<T> ();
return result;
});
//
return response;
}
public async Task<XBaseRestResponse<T>> RunRestRequestByResponse<T> (
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<T> (
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<T>
{
Response = resp,
Model = resp.Content.FromJSON<T> ()
};
//
return result;
});
//
return response;
}
/// <summary>
/// Get User SelectBy Param
/// </summary>
/// <param name="model"></param>
/// <param name="forceNotNull"></param>
/// <param name="excludes"></param>
/// <returns></returns>
public string GetUserSelectByParam (
XActionRequest model,
bool forceNotNull = true,
ICollection<XUserSelectBy> 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
}
}