102 lines
3.1 KiB
C#
102 lines
3.1 KiB
C#
using System.Security.Claims;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Hl7.Fhir.Serialization;
|
|
using Microsoft.Extensions.Options;
|
|
using Task = System.Threading.Tasks.Task;
|
|
|
|
public class FhirApiKeyOrJwtMiddleware
|
|
{
|
|
public const string SchemeName = "FhirApiKey";
|
|
|
|
private readonly RequestDelegate _next;
|
|
private readonly FhirOptions _options;
|
|
private static readonly FhirJsonSerializer Serializer = new();
|
|
|
|
public FhirApiKeyOrJwtMiddleware(RequestDelegate next, IOptions<FhirOptions> options)
|
|
{
|
|
_next = next;
|
|
_options = options.Value;
|
|
}
|
|
|
|
public async Task InvokeAsync(HttpContext context)
|
|
{
|
|
if (!context.Request.Path.StartsWithSegments("/fhir"))
|
|
{
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
if (context.Request.Path.StartsWithSegments("/fhir/R4/metadata"))
|
|
{
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
if (context.User.Identity?.IsAuthenticated == true)
|
|
{
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
var configuredKeys = GetConfiguredKeys();
|
|
if (configuredKeys.Length == 0)
|
|
{
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
if (context.Request.Headers.TryGetValue("X-Api-Key", out var suppliedKey)
|
|
&& MatchesAnyKey(suppliedKey!, configuredKeys))
|
|
{
|
|
var claims = new[]
|
|
{
|
|
new Claim(ClaimTypes.NameIdentifier, "44444444-4444-4444-4444-444444444444"),
|
|
new Claim(ClaimTypes.Name, "integration.mirth"),
|
|
new Claim("display_name", "Mirth Connect"),
|
|
new Claim("clinical_role", ClinicalRole.Integration.ToDbString()),
|
|
};
|
|
context.User = new ClaimsPrincipal(new ClaimsIdentity(claims, SchemeName));
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
if (context.Request.Headers.ContainsKey("X-Api-Key"))
|
|
{
|
|
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
|
context.Response.ContentType = "application/fhir+json";
|
|
var outcome = FhirOperationOutcomeBuilder.Create(401, "login", "Invalid or missing API key.");
|
|
await context.Response.WriteAsync(Serializer.SerializeToString(outcome));
|
|
return;
|
|
}
|
|
|
|
await _next(context);
|
|
}
|
|
|
|
private string[] GetConfiguredKeys()
|
|
{
|
|
var keys = new List<string>();
|
|
if (!string.IsNullOrWhiteSpace(_options.ApiKey))
|
|
keys.Add(_options.ApiKey);
|
|
foreach (var k in _options.ApiKeys)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(k))
|
|
keys.Add(k);
|
|
}
|
|
return keys.ToArray();
|
|
}
|
|
|
|
private static bool MatchesAnyKey(string supplied, string[] configuredKeys)
|
|
{
|
|
var suppliedBytes = Encoding.UTF8.GetBytes(supplied);
|
|
var matched = false;
|
|
foreach (var configured in configuredKeys)
|
|
{
|
|
var configuredBytes = Encoding.UTF8.GetBytes(configured);
|
|
if (CryptographicOperations.FixedTimeEquals(suppliedBytes, configuredBytes))
|
|
matched = true;
|
|
}
|
|
return matched;
|
|
}
|
|
}
|