feat: добавил интеграционные тесты
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace UniVerse.Api.Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Swagger operation filter that:
|
||||
/// 1. Adds Bearer security requirement only to endpoints that actually require authentication.
|
||||
/// 2. Appends a "Required roles: ..." remark to the operation description when role restrictions exist.
|
||||
///
|
||||
/// This replaces the global AddSecurityRequirement approach so anonymous endpoints
|
||||
/// (auth/login, auth/refresh, auth/callback) don't show the lock icon in Swagger UI.
|
||||
/// </summary>
|
||||
public class AuthorizeOperationFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
// Collect [Authorize] and [AllowAnonymous] from both the controller and the action.
|
||||
var actionAttributes = context.MethodInfo.GetCustomAttributes(inherit: true);
|
||||
var controllerAttributes = context.MethodInfo.DeclaringType?
|
||||
.GetCustomAttributes(inherit: true) ?? [];
|
||||
|
||||
var allAttributes = actionAttributes.Concat(controllerAttributes).ToList();
|
||||
|
||||
var hasAllowAnonymous = allAttributes.OfType<AllowAnonymousAttribute>().Any();
|
||||
if (hasAllowAnonymous)
|
||||
return; // completely public — no lock icon
|
||||
|
||||
var authorizeAttributes = allAttributes.OfType<AuthorizeAttribute>().ToList();
|
||||
if (authorizeAttributes.Count == 0)
|
||||
return; // no [Authorize] at all — also public
|
||||
|
||||
// Collect all distinct roles across all [Authorize(Roles = "...")] attributes.
|
||||
var roles = authorizeAttributes
|
||||
.Where(a => !string.IsNullOrWhiteSpace(a.Roles))
|
||||
.SelectMany(a => a.Roles!.Split(',', StringSplitOptions.TrimEntries))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(r => r)
|
||||
.ToList();
|
||||
|
||||
// Append role information to the operation description.
|
||||
var roleInfo = roles.Count > 0
|
||||
? $"**Required roles:** {string.Join(", ", roles)}"
|
||||
: "**Required:** any authenticated user";
|
||||
|
||||
operation.Description = string.IsNullOrWhiteSpace(operation.Description)
|
||||
? roleInfo
|
||||
: $"{operation.Description}\n\n{roleInfo}";
|
||||
|
||||
operation.Responses ??= new OpenApiResponses();
|
||||
|
||||
// Add 401 / 403 responses if not already declared.
|
||||
if (!operation.Responses.ContainsKey("401"))
|
||||
{
|
||||
operation.Responses.Add("401", new OpenApiResponse
|
||||
{
|
||||
Description = "Unauthorized — JWT token missing or invalid"
|
||||
});
|
||||
}
|
||||
|
||||
if (roles.Count > 0 && !operation.Responses.ContainsKey("403"))
|
||||
{
|
||||
operation.Responses.Add("403", new OpenApiResponse
|
||||
{
|
||||
Description = $"Forbidden — requires role: {string.Join(" or ", roles)}"
|
||||
});
|
||||
}
|
||||
|
||||
// Add Bearer security requirement to this specific operation.
|
||||
// OpenAPI v2 (Microsoft.OpenApi 2.x) uses OpenApiSecuritySchemeReference
|
||||
// instead of OpenApiSecurityScheme with a Reference property.
|
||||
var bearerSchemeRef = new OpenApiSecuritySchemeReference("Bearer", context.Document);
|
||||
|
||||
operation.Security ??= [];
|
||||
operation.Security.Add(new OpenApiSecurityRequirement
|
||||
{
|
||||
[bearerSchemeRef] = []
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user