Centralize admin auth with endpoint filter

This commit is contained in:
2026-02-05 17:11:17 +01:00
parent c03cee1777
commit 8176940d18
3 changed files with 34 additions and 18 deletions

View File

@@ -4,6 +4,7 @@ using GameList.Contracts;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using GameList.Infrastructure;
namespace GameList.Endpoints;
@@ -11,12 +12,12 @@ public static class AdminEndpoints
{
public static void MapAdminEndpoints(this IEndpointRouteBuilder app)
{
var admin = app.MapGroup("/api/admin");
var admin = app.MapGroup("/api/admin")
.RequireAuthorization()
.AddEndpointFilter<AdminOnlyFilter>();
admin.MapPost("/results", async ([FromBody] Contracts.ResultsOpenRequest request, HttpContext ctx, AppDbContext db) =>
{
if (!await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
var state = await db.AppState.FirstAsync();
state.ResultsOpen = request.ResultsOpen;
state.UpdatedAt = DateTimeOffset.UtcNow;
@@ -38,8 +39,6 @@ public static class AdminEndpoints
admin.MapGet("/vote-status", async (HttpContext ctx, AppDbContext db) =>
{
if (!await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
var voters = await db.Players
.AsNoTracking()
.Include(p => p.Suggestions)
@@ -61,7 +60,6 @@ public static class AdminEndpoints
admin.MapPost("/joker", async ([FromBody] GrantJokerRequest request, HttpContext ctx, AppDbContext db) =>
{
if (!await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
var player = await db.Players.FirstOrDefaultAsync(p => p.Id == request.PlayerId);
if (player is null) return Results.NotFound(new { error = "Player not found." });
@@ -78,8 +76,6 @@ public static class AdminEndpoints
admin.MapDelete("/players/{playerId:guid}", async (Guid playerId, HttpContext ctx, AppDbContext db) =>
{
if (!await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
var player = await db.Players
.Include(p => p.Suggestions)
.FirstOrDefaultAsync(p => p.Id == playerId);
@@ -114,7 +110,7 @@ public static class AdminEndpoints
admin.MapPost("/link-suggestions", async ([FromBody] LinkSuggestionsRequest request, HttpContext ctx, AppDbContext db) =>
{
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
if (player is null || !await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
if (player is null) return Results.Unauthorized();
var phase = await EndpointHelpers.GetPhase(db, player.Id);
if (phase != Phase.Vote)
@@ -186,7 +182,7 @@ public static class AdminEndpoints
admin.MapPost("/unlink-suggestions", async ([FromBody] UnlinkSuggestionsRequest request, HttpContext ctx, AppDbContext db) =>
{
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
if (player is null || !await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
if (player is null) return Results.Unauthorized();
var phase = await EndpointHelpers.GetPhase(db, player.Id);
if (phase != Phase.Vote)
@@ -240,8 +236,6 @@ public static class AdminEndpoints
admin.MapPost("/reset", async (HttpContext ctx, AppDbContext db) =>
{
if (!await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
await db.Votes.ExecuteDeleteAsync();
await db.Suggestions.ExecuteDeleteAsync();
@@ -258,8 +252,6 @@ public static class AdminEndpoints
admin.MapPost("/factory-reset", async (HttpContext ctx, AppDbContext db) =>
{
if (!await EndpointHelpers.IsAdmin(ctx, db)) return Results.Unauthorized();
await using var tx = await db.Database.BeginTransactionAsync();
await db.Votes.ExecuteDeleteAsync();

View File

@@ -11,10 +11,10 @@ internal static class EndpointHelpers
{
public static async Task<Player?> GetAuthenticatedPlayer(HttpContext ctx, AppDbContext db)
{
if (ctx?.User?.Identity?.IsAuthenticated != true)
{
return null;
}
if (ctx?.User?.Identity?.IsAuthenticated != true) return null;
if (ctx.Items.TryGetValue(nameof(Player), out var cached) && cached is Player cachedPlayer)
return cachedPlayer;
var idValue = ctx.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrWhiteSpace(idValue) || !Guid.TryParse(idValue, out var playerId))
@@ -28,7 +28,10 @@ internal static class EndpointHelpers
if (existing is null)
{
await Infrastructure.PlayerIdentityExtensions.SignOutPlayerAsync(ctx);
return null;
}
ctx.Items[nameof(Player)] = existing;
return existing;
}