653 lines
22 KiB
C#
653 lines
22 KiB
C#
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<string, string> @params = null,
|
|
IDictionary<string, string> headers = null,
|
|
IDictionary<string, string> queryStrings = null
|
|
);
|
|
|
|
Task<T> RunRestRequest<T>(
|
|
RestClient client,
|
|
RestRequest request,
|
|
XHttpMethod method,
|
|
bool supportRefreshingTokens = true,
|
|
CancellationToken cancellationToken = default
|
|
);
|
|
|
|
Task<XBaseRestResponse<T>> RunRestRequestByResponse<T>(
|
|
RestClient client,
|
|
RestRequest request,
|
|
XHttpMethod method,
|
|
bool supportRefreshingTokens = true,
|
|
CancellationToken cancellationToken = default
|
|
);
|
|
|
|
string GetUserSelectByParam(
|
|
XActionRequest model,
|
|
bool forceNotNull = true,
|
|
ICollection<XUserSelectBy> 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 ...
|
|
/// <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 = 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>
|
|
public RestRequest GetRestRequet(
|
|
Enum endpoint,
|
|
bool addXPoweredValue = true,
|
|
IDictionary<string, string> @params = null,
|
|
IDictionary<string, string> headers = null,
|
|
IDictionary<string, string> 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;
|
|
}
|
|
|
|
/// <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,
|
|
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<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;
|
|
},
|
|
cancellationToken: cancellationToken);
|
|
|
|
//
|
|
return response;
|
|
}
|
|
public async Task<XBaseRestResponse<T>> RunRestRequestByResponse<T>(
|
|
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<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;
|
|
},
|
|
cancellationToken: cancellationToken);
|
|
|
|
//
|
|
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
|
|
}
|
|
} |