Add authorization options to swagger-ui

This commit is contained in:
Laura Hausmann 2024-01-28 03:33:20 +01:00
parent 3dc64fad38
commit c961111d55
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 68 additions and 13 deletions

View file

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using Iceshrimp.Backend.Controllers.Schemas; using Iceshrimp.Backend.Controllers.Schemas;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
@ -10,7 +11,9 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.RateLimiting; using Microsoft.AspNetCore.RateLimiting;
using Microsoft.OpenApi.Models;
using StackExchange.Redis; using StackExchange.Redis;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Iceshrimp.Backend.Core.Extensions; namespace Iceshrimp.Backend.Core.Extensions;
@ -82,6 +85,55 @@ public static class ServiceExtensions {
}); });
} }
public static void AddSwaggerGenWithOptions(this IServiceCollection services) {
services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Iceshrimp.NET", Version = "1.0" });
options.AddSecurityDefinition("user", new OpenApiSecurityScheme {
Name = "Authorization token",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
options.AddSecurityDefinition("admin", new OpenApiSecurityScheme {
Name = "Authorization token",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
}
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local",
Justification = "SwaggerGenOptions.OperationFilter<T> instantiates this class at runtime")]
private class AuthorizeCheckOperationFilter : IOperationFilter {
public void Apply(OpenApiOperation operation, OperationFilterContext context) {
if (context.MethodInfo.DeclaringType is null)
return;
//TODO: separate admin & user authorize attributes
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.OfType<AuthenticateAttribute>().Any() ||
context.MethodInfo.GetCustomAttributes(true)
.OfType<AuthenticateAttribute>().Any();
if (!hasAuthorize) return;
var schema = new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "user"
}
};
operation.Security = new List<OpenApiSecurityRequirement> {
new() {
[schema] = Array.Empty<string>()
}
};
}
}
public static void AddSlidingWindowRateLimiter(this IServiceCollection services) { public static void AddSlidingWindowRateLimiter(this IServiceCollection services) {
//TODO: separate limiter for authenticated users, partitioned by user id //TODO: separate limiter for authenticated users, partitioned by user id
//TODO: ipv6 /64 subnet buckets //TODO: ipv6 /64 subnet buckets

View file

@ -16,6 +16,20 @@ public static class WebApplicationExtensions {
.UseMiddleware<AuthorizedFetchMiddleware>(); .UseMiddleware<AuthorizedFetchMiddleware>();
} }
public static IApplicationBuilder UseSwaggerWithOptions(this WebApplication app) {
app.UseSwagger();
app.UseSwaggerUI(options => {
options.DocumentTitle = "Iceshrimp API documentation";
options.SwaggerEndpoint("v1/swagger.json", "Iceshrimp.NET");
options.InjectStylesheet("/swagger/styles.css");
options.EnablePersistAuthorization();
options.EnableTryItOutByDefault();
options.DisplayRequestDuration();
options.DefaultModelsExpandDepth(-1); // Hide "Schemas" section
});
return app;
}
public static Config.InstanceSection Initialize(this WebApplication app, string[] args) { public static Config.InstanceSection Initialize(this WebApplication app, string[] args) {
var instanceConfig = app.Configuration.GetSection("Instance").Get<Config.InstanceSection>() ?? var instanceConfig = app.Configuration.GetSection("Instance").Get<Config.InstanceSection>() ??
throw new Exception("Failed to read Instance config section"); throw new Exception("Failed to read Instance config section");

View file

@ -23,9 +23,7 @@ builder.Services.AddApiVersioning(options => {
options.UnsupportedApiVersionStatusCode = 501; options.UnsupportedApiVersionStatusCode = 501;
}); });
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => { builder.Services.AddSwaggerGenWithOptions();
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Iceshrimp.NET", Version = "1.0" });
});
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddViteServices(options => { builder.Services.AddViteServices(options => {
options.PackageDirectory = "../Iceshrimp.Frontend"; options.PackageDirectory = "../Iceshrimp.Frontend";
@ -47,16 +45,7 @@ var config = app.Initialize(args);
// This determines the order of middleware execution in the request pipeline // This determines the order of middleware execution in the request pipeline
app.UseRouting(); app.UseRouting();
app.UseSwagger(); app.UseSwaggerWithOptions();
app.UseSwaggerUI(options => {
options.DocumentTitle = "Iceshrimp API documentation";
options.SwaggerEndpoint("v1/swagger.json", "Iceshrimp.NET");
options.InjectStylesheet("/swagger/styles.css");
options.EnablePersistAuthorization();
options.EnableTryItOutByDefault();
options.DisplayRequestDuration();
options.DefaultModelsExpandDepth(-1); // Hide "Schemas" section
});
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRateLimiter(); app.UseRateLimiter();
app.UseAuthorization(); app.UseAuthorization();