Centralize admin auth with endpoint filter
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user