[backend] Add request duration header for non-timing-sensitive (cryptography-related) endpoints
This commit is contained in:
parent
2984abbe2b
commit
94820c2b71
7 changed files with 44 additions and 7 deletions
|
@ -42,6 +42,7 @@ public class AuthController(DatabaseContext db, UserService userSvc) : Controlle
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
[HideRequestDuration]
|
||||
[EnableRateLimiting("strict")]
|
||||
[Consumes(MediaTypeNames.Application.Json)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AuthResponse))]
|
||||
|
|
|
@ -45,6 +45,7 @@ public class QueryCollectionModelBinderProvider(IModelBinderProvider provider) :
|
|||
public IModelBinder? GetBinder(ModelBinderProviderContext context) {
|
||||
if (context.BindingInfo.BindingSource == null) return null;
|
||||
if (!context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Query)) return null;
|
||||
if (!context.Metadata.IsCollectionType) return null;
|
||||
|
||||
var binder = provider.GetBinder(context);
|
||||
return new QueryCollectionModelBinder(binder);
|
||||
|
|
|
@ -46,6 +46,7 @@ public static class ServiceExtensions {
|
|||
services.AddSingleton<RequestBufferingMiddleware>();
|
||||
services.AddSingleton<AuthorizationMiddleware>();
|
||||
services.AddSingleton<RequestVerificationMiddleware>();
|
||||
services.AddSingleton<RequestDurationMiddleware>();
|
||||
|
||||
// Hosted services = long running background tasks
|
||||
// Note: These need to be added as a singleton as well to ensure data consistency
|
||||
|
|
|
@ -9,7 +9,8 @@ namespace Iceshrimp.Backend.Core.Extensions;
|
|||
public static class WebApplicationExtensions {
|
||||
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder app) {
|
||||
// Caution: make sure these are in the correct order
|
||||
return app.UseMiddleware<ErrorHandlerMiddleware>()
|
||||
return app.UseMiddleware<RequestDurationMiddleware>()
|
||||
.UseMiddleware<ErrorHandlerMiddleware>()
|
||||
.UseMiddleware<RequestVerificationMiddleware>()
|
||||
.UseMiddleware<RequestBufferingMiddleware>()
|
||||
.UseMiddleware<AuthenticationMiddleware>()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
||||
|
@ -66,7 +67,8 @@ public class ErrorHandlerMiddleware(IOptions<Config.SecuritySection> options, IL
|
|||
}
|
||||
}
|
||||
else {
|
||||
ctx.Response.StatusCode = 500;
|
||||
ctx.Response.StatusCode = 500;
|
||||
ctx.Response.Headers.RequestId = ctx.TraceIdentifier;
|
||||
await ctx.Response.WriteAsJsonAsync(new ErrorResponse {
|
||||
StatusCode = 500,
|
||||
Error = "Internal Server Error",
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
||||
using Iceshrimp.Backend.Controllers.Schemas;
|
||||
using Iceshrimp.Backend.Core.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Iceshrimp.Backend.Core.Middleware;
|
||||
|
||||
public class RequestDurationMiddleware : IMiddleware {
|
||||
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) {
|
||||
if (ctx.GetEndpoint()?.Metadata.GetMetadata<HideRequestDuration>() == null) {
|
||||
var pre = DateTime.Now;
|
||||
ctx.Response.OnStarting(() => {
|
||||
var duration = (int)(DateTime.Now - pre).TotalMilliseconds;
|
||||
ctx.Response.Headers.Append("X-Request-Duration",
|
||||
duration.ToString(CultureInfo.InvariantCulture) + " ms");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
await next(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public class HideRequestDuration : Attribute;
|
|
@ -9,12 +9,13 @@ using Microsoft.EntityFrameworkCore;
|
|||
|
||||
namespace Iceshrimp.Backend.Pages.OAuth;
|
||||
|
||||
[HideRequestDuration]
|
||||
public class AuthorizeModel(DatabaseContext db) : PageModel {
|
||||
[FromQuery(Name = "response_type")] public required string ResponseType { get; set; }
|
||||
[FromQuery(Name = "client_id")] public required string ClientId { get; set; }
|
||||
[FromQuery(Name = "redirect_uri")] public required string RedirectUri { get; set; }
|
||||
[FromQuery(Name = "force_login")] public bool ForceLogin { get; set; } = false;
|
||||
[FromQuery(Name = "lang")] public string? Language { get; set; }
|
||||
[FromQuery(Name = "response_type")] public string ResponseType { get; set; } = null!;
|
||||
[FromQuery(Name = "client_id")] public string ClientId { get; set; } = null!;
|
||||
[FromQuery(Name = "redirect_uri")] public string RedirectUri { get; set; } = null!;
|
||||
[FromQuery(Name = "force_login")] public bool ForceLogin { get; set; } = false;
|
||||
[FromQuery(Name = "lang")] public string? Language { get; set; }
|
||||
|
||||
[FromQuery(Name = "scope")]
|
||||
public string Scope {
|
||||
|
@ -27,6 +28,8 @@ public class AuthorizeModel(DatabaseContext db) : PageModel {
|
|||
public OauthToken? Token = null;
|
||||
|
||||
public async Task OnGet() {
|
||||
if (ResponseType == null || ClientId == null || RedirectUri == null)
|
||||
throw GracefulException.BadRequest("Required parameters are missing or invalid");
|
||||
App = await db.OauthApps.FirstOrDefaultAsync(p => p.ClientId == ClientId.Replace(' ', '+'))
|
||||
?? throw GracefulException.BadRequest("Invalid client_id");
|
||||
if (MastodonOauthHelpers.ExpandScopes(Scopes).Except(MastodonOauthHelpers.ExpandScopes(App.Scopes)).Any())
|
||||
|
|
Loading…
Add table
Reference in a new issue