first commit
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Fitness_core_BE.Models.IOs;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace Fitness_core_BE.Helpers
|
||||
{
|
||||
// Gates the subscription-billing worker endpoint behind a single shared secret in the
|
||||
// X-Worker-Key header. Distinct from JwtMiddleware/CustomMiddleware (human/agent session
|
||||
// auth) and from PaymentService:InternalApiKey (Core's own outbound key to Payment-Service) —
|
||||
// this authenticates the trusted scheduler calling back into Core. Writes the 401/503
|
||||
// response directly (like RequiresPermissionAttribute) rather than throwing, because Core
|
||||
// has no global exception handler to turn a thrown exception into the right status code on
|
||||
// this unauthenticated route.
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class RequireWorkerKeyAttribute : Attribute, IAsyncActionFilter
|
||||
{
|
||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
var configuration = context.HttpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
|
||||
var configuredKey = configuration?["Worker:InternalApiKey"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(configuredKey))
|
||||
{
|
||||
await WriteErrorAsync(context, 503, "Worker:InternalApiKey is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
var providedKey = context.HttpContext.Request.Headers["X-Worker-Key"].FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(providedKey) || !FixedTimeEquals(providedKey, configuredKey))
|
||||
{
|
||||
await WriteErrorAsync(context, 401, "Invalid worker key.");
|
||||
return;
|
||||
}
|
||||
|
||||
await next();
|
||||
}
|
||||
|
||||
private static async Task WriteErrorAsync(ActionExecutingContext context, int statusCode, string message)
|
||||
{
|
||||
context.HttpContext.Response.StatusCode = statusCode;
|
||||
await context.HttpContext.Response.WriteAsJsonAsync(new ApiResponse<object>
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Message = message,
|
||||
Data = null
|
||||
});
|
||||
}
|
||||
|
||||
private static bool FixedTimeEquals(string a, string b)
|
||||
{
|
||||
return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(a), Encoding.UTF8.GetBytes(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user