84 lines
3.6 KiB
C#
84 lines
3.6 KiB
C#
|
|
using System.Net.Http.Headers;
|
||
|
|
using System.Text;
|
||
|
|
using System.Text.Json.Nodes;
|
||
|
|
using Microsoft.AspNetCore.WebUtilities;
|
||
|
|
using w4c_workflows.Models.Credentials;
|
||
|
|
|
||
|
|
namespace w4c_workflows.Services.Credentials;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Applies a resolved credential to an outbound HTTP request according to its
|
||
|
|
/// type's injection strategy. Returns a human-readable error when the credential
|
||
|
|
/// cannot be injected (unknown type, missing field, unsupported strategy).
|
||
|
|
/// </summary>
|
||
|
|
public static class CredentialInjector
|
||
|
|
{
|
||
|
|
public static string? Apply(CredentialData credential, CredentialTypeCatalog catalog, HttpRequestMessage request)
|
||
|
|
{
|
||
|
|
var type = catalog.Get(credential.Type);
|
||
|
|
if (type == null)
|
||
|
|
return $"unknown credential type '{credential.Type}'";
|
||
|
|
|
||
|
|
var injection = type.Injection;
|
||
|
|
return injection.Kind switch
|
||
|
|
{
|
||
|
|
CredentialInjectionKind.Basic => ApplyBasic(credential.Data, injection, request),
|
||
|
|
CredentialInjectionKind.Bearer => ApplyBearer(credential.Data, injection, request),
|
||
|
|
CredentialInjectionKind.Header => ApplyHeader(credential.Data, injection, request),
|
||
|
|
CredentialInjectionKind.Query => ApplyQuery(credential.Data, injection, request),
|
||
|
|
CredentialInjectionKind.None => null,
|
||
|
|
_ => $"credential type '{credential.Type}' has an unsupported injection strategy '{injection.Kind}'",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string? ApplyBasic(JsonObject data, CredentialInjection injection, HttpRequestMessage request)
|
||
|
|
{
|
||
|
|
var username = Read(data, injection.UsernameField);
|
||
|
|
if (string.IsNullOrEmpty(username))
|
||
|
|
return $"credential is missing '{injection.UsernameField}'";
|
||
|
|
|
||
|
|
var password = Read(data, injection.PasswordField) ?? string.Empty;
|
||
|
|
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
|
||
|
|
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string? ApplyBearer(JsonObject data, CredentialInjection injection, HttpRequestMessage request)
|
||
|
|
{
|
||
|
|
var token = Read(data, injection.TokenField);
|
||
|
|
if (string.IsNullOrEmpty(token))
|
||
|
|
return $"credential is missing '{injection.TokenField}'";
|
||
|
|
|
||
|
|
request.Headers.Authorization = new AuthenticationHeaderValue(injection.Prefix, token);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string? ApplyHeader(JsonObject data, CredentialInjection injection, HttpRequestMessage request)
|
||
|
|
{
|
||
|
|
var name = Read(data, injection.NameField);
|
||
|
|
var value = Read(data, injection.ValueField);
|
||
|
|
if (string.IsNullOrEmpty(name) || value == null)
|
||
|
|
return "credential is missing its header name or value";
|
||
|
|
|
||
|
|
request.Headers.TryAddWithoutValidation(name, value);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string? ApplyQuery(JsonObject data, CredentialInjection injection, HttpRequestMessage request)
|
||
|
|
{
|
||
|
|
var name = Read(data, injection.NameField);
|
||
|
|
var value = Read(data, injection.ValueField);
|
||
|
|
if (string.IsNullOrEmpty(name) || value == null)
|
||
|
|
return "credential is missing its query parameter name or value";
|
||
|
|
if (request.RequestUri == null)
|
||
|
|
return "cannot inject a query credential without a request URI";
|
||
|
|
|
||
|
|
var query = QueryHelpers.AddQueryString(request.RequestUri.ToString(), name, value);
|
||
|
|
request.RequestUri = new Uri(query);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string? Read(JsonObject data, string? field)
|
||
|
|
=> field != null && data[field] is JsonValue value && value.TryGetValue<string>(out var text) ? text : null;
|
||
|
|
}
|