mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
67 lines
2.4 KiB
C#
67 lines
2.4 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Api.SeaHavenIndustries.Options;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Api.SeaHavenIndustries.Filters
|
|
{
|
|
/// <summary>Validates X-Ingest-Key for POST /api/workorders/ingest.</summary>
|
|
public class IngestApiKeyFilter : IAsyncActionFilter
|
|
{
|
|
private readonly WorkOrderIngestOptions _options;
|
|
|
|
public IngestApiKeyFilter(IOptions<WorkOrderIngestOptions> options)
|
|
{
|
|
_options = options.Value;
|
|
}
|
|
|
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
|
{
|
|
if (!_options.Enabled)
|
|
{
|
|
context.Result = new ObjectResult(new { message = "Work order ingest is disabled." })
|
|
{
|
|
StatusCode = StatusCodes.Status503ServiceUnavailable
|
|
};
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(_options.ApiKey)
|
|
|| _options.ApiKey.Contains("${", StringComparison.Ordinal))
|
|
{
|
|
context.Result = new ObjectResult(new { message = "Ingest API key is not configured." })
|
|
{
|
|
StatusCode = StatusCodes.Status503ServiceUnavailable
|
|
};
|
|
return;
|
|
}
|
|
|
|
if (!context.HttpContext.Request.Headers.TryGetValue("X-Ingest-Key", out var provided)
|
|
|| !FixedTimeEquals(provided.ToString(), _options.ApiKey))
|
|
{
|
|
context.Result = new UnauthorizedObjectResult(new { message = "Invalid or missing X-Ingest-Key." });
|
|
return;
|
|
}
|
|
|
|
await next();
|
|
}
|
|
|
|
private static bool FixedTimeEquals(string provided, string expected)
|
|
{
|
|
var providedBytes = Encoding.UTF8.GetBytes(provided);
|
|
var expectedBytes = Encoding.UTF8.GetBytes(expected);
|
|
return providedBytes.Length == expectedBytes.Length
|
|
&& CryptographicOperations.FixedTimeEquals(providedBytes, expectedBytes);
|
|
}
|
|
}
|
|
|
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
|
public sealed class IngestApiKeyAttribute : ServiceFilterAttribute
|
|
{
|
|
public IngestApiKeyAttribute() : base(typeof(IngestApiKeyFilter))
|
|
{
|
|
}
|
|
}
|
|
}
|