Refactor and expand REST action authentication support

Refactored authentication logic for REST actions to use a structured switch block, adding explicit support for ApiKey, BearerToken, JwtBearer, OAuth2, BasicAuth, and NTLM authentication types. Introduced dedicated HttpClient/Handler for NTLM with proper disposal. Improved extensibility, clarity, and resource management. Added IOptions and Options usage for configuration.
This commit is contained in:
2026-03-16 13:43:27 +01:00
parent b38d53248c
commit 87194df697

View File

@@ -1,9 +1,11 @@
using MediatR; using MediatR;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using ReC.Application.Common; using ReC.Application.Common;
using ReC.Application.Common.Constants; using ReC.Application.Common.Constants;
using ReC.Application.Common.Dto; using ReC.Application.Common.Dto;
using ReC.Application.Common.Exceptions; using ReC.Application.Common.Exceptions;
using ReC.Application.Common.Options;
using ReC.Application.Common.Procedures.InsertProcedure; using ReC.Application.Common.Procedures.InsertProcedure;
using ReC.Application.Results.Commands; using ReC.Application.Results.Commands;
using ReC.Domain.Constants; using ReC.Domain.Constants;
@@ -32,8 +34,6 @@ public class InvokeRecActionViewCommandHandler(
{ {
var action = request.Action; var action = request.Action;
using var http = clientFactory.CreateClient(Http.ClientName);
if (action.RestType is not RestType restType) if (action.RestType is not RestType restType)
throw new DataIntegrityException( throw new DataIntegrityException(
$"Rec action could not be invoked because the RestType value is null. " + $"Rec action could not be invoked because the RestType value is null. " +
@@ -50,54 +50,59 @@ public class InvokeRecActionViewCommandHandler(
foreach (var header in action.Headers) foreach (var header in action.Headers)
httpReq.Headers.Add(header.Key, header.Value); httpReq.Headers.Add(header.Key, header.Value);
switch (action.EndpointAuthType) HttpClient? ntlmClient = null;
HttpClientHandler? ntlmHandler = null;
try
{ {
case EndpointAuthType.NoAuth: switch (action.EndpointAuthType)
break; {
case EndpointAuthType.NoAuth:
break;
case EndpointAuthType.ApiKey: case EndpointAuthType.ApiKey:
if (action.EndpointAuthApiKey is string apiKey && action.EndpointAuthApiValue is string apiValue) if (action.EndpointAuthApiKey is string apiKey && action.EndpointAuthApiValue is string apiValue)
{
switch (action.EndpointAuthApiKeyAddTo)
{ {
case ApiKeyLocation.Header: switch (action.EndpointAuthApiKeyAddTo)
httpReq.Headers.Add(apiKey, apiValue); {
break; case ApiKeyLocation.Header:
case ApiKeyLocation.Query: httpReq.Headers.Add(apiKey, apiValue);
var uriBuilder = new UriBuilder(httpReq.RequestUri!); break;
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query); case ApiKeyLocation.Query:
query[apiKey] = apiValue; var uriBuilder = new UriBuilder(httpReq.RequestUri!);
uriBuilder.Query = query.ToString(); var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
httpReq.RequestUri = uriBuilder.Uri; query[apiKey] = apiValue;
break; uriBuilder.Query = query.ToString();
default: httpReq.RequestUri = uriBuilder.Uri;
throw new DataIntegrityException( break;
$"The API key location '{action.EndpointAuthApiKeyAddTo}' is not supported. " + default:
$"ProfileId: {action.ProfileId}, " + throw new DataIntegrityException(
$"Id: {action.Id}" $"The API key location '{action.EndpointAuthApiKeyAddTo}' is not supported. " +
); $"ProfileId: {action.ProfileId}, " +
$"Id: {action.Id}"
);
}
} }
} break;
break;
case EndpointAuthType.BearerToken: case EndpointAuthType.BearerToken:
case EndpointAuthType.JwtBearer: case EndpointAuthType.JwtBearer:
case EndpointAuthType.OAuth2: case EndpointAuthType.OAuth2:
if (action.EndpointAuthToken is string authToken) if (action.EndpointAuthToken is string authToken)
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken); httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
break; break;
case EndpointAuthType.BasicAuth: case EndpointAuthType.BasicAuth:
if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword) if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword)
{ {
var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}")); var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}"));
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth); httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth);
} }
break; break;
case EndpointAuthType.NtlmAuth: case EndpointAuthType.NtlmAuth:
if (!string.IsNullOrWhiteSpace(action.EndpointAuthUsername)) if (!string.IsNullOrWhiteSpace(action.EndpointAuthUsername))
{ {
if (_options.UseHttp1ForNtlm) if (_options.UseHttp1ForNtlm)
{ {
httpReq.Version = HttpVersion.Version11; httpReq.Version = HttpVersion.Version11;
@@ -108,44 +113,55 @@ public class InvokeRecActionViewCommandHandler(
?.Replace("%NTLM_PW%", config?.GetValue<string>("%NTLM_PW%")) ?.Replace("%NTLM_PW%", config?.GetValue<string>("%NTLM_PW%"))
#endif #endif
; ;
var credentials = new NetworkCredential( var credentials = new NetworkCredential(
action.EndpointAuthUsername, action.EndpointAuthUsername,
endpointAuthPassword, endpointAuthPassword,
action.EndpointAuthDomain); action.EndpointAuthDomain);
var credentialCache = new CredentialCache { { httpReq.RequestUri!, "NTLM", credentials } }; var credentialCache = new CredentialCache { { httpReq.RequestUri!, "NTLM", credentials } };
httpReq.Options.Set(new HttpRequestOptionsKey<CredentialCache>("Credentials"), credentialCache); ntlmHandler = new HttpClientHandler
} {
break; Credentials = credentialCache,
UseDefaultCredentials = false
};
ntlmClient = new HttpClient(ntlmHandler);
}
break;
case EndpointAuthType.DigestAuth: case EndpointAuthType.DigestAuth:
case EndpointAuthType.OAuth1: case EndpointAuthType.OAuth1:
case EndpointAuthType.AwsSignature: case EndpointAuthType.AwsSignature:
// These authentication methods require more complex implementations, // These authentication methods require more complex implementations,
// often involving multi-step handshakes or specialized libraries. // often involving multi-step handshakes or specialized libraries.
// They are left as placeholders for future implementation. // They are left as placeholders for future implementation.
default: default:
throw new NotImplementedException( throw new NotImplementedException(
$"The authentication type '{action.EndpointAuthType}' is not supported yet. " + $"The authentication type '{action.EndpointAuthType}' is not supported yet. " +
$"ProfileId: {action.ProfileId}, " + $"ProfileId: {action.ProfileId}, " +
$"Id: {action.Id}" $"Id: {action.Id}"
); );
} }
using var response = await http.SendAsync(httpReq, cancel); using var http = ntlmClient ?? clientFactory.CreateClient(Http.ClientName);
var resBody = await response.Content.ReadAsStringAsync(cancel); using var response = await http.SendAsync(httpReq, cancel);
var resHeaders = response.Headers.ToDictionary(); var resBody = await response.Content.ReadAsStringAsync(cancel);
var resHeaders = response.Headers.ToDictionary();
var statusCode = (short)response.StatusCode; var statusCode = (short)response.StatusCode;
await sender.ExecuteInsertProcedure(new InsertResultProcedure() await sender.ExecuteInsertProcedure(new InsertResultProcedure()
{ {
StatusId = statusCode, StatusId = statusCode,
ActionId = action.Id, ActionId = action.Id,
Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }), Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }),
Body = resBody Body = resBody
}, _options.AddedWho, cancel); }, _options.AddedWho, cancel);
return response.IsSuccessStatusCode; return response.IsSuccessStatusCode;
}
finally
{
ntlmHandler?.Dispose();
}
} }
private static HttpRequestMessage CreateHttpRequestMessage(RestType restType, string? endpointUri) private static HttpRequestMessage CreateHttpRequestMessage(RestType restType, string? endpointUri)