using System.Linq; using System.Text.Json; using Gott.Extensions; using Gott.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; namespace Gott.Endpoints; public static class AdminEndpoint { public static void MapAdminEndpoints(this WebApplication app) { app.MapGet("/api/admin/users", HandleListUsers); app.MapPost("/api/admin/users", HandleCreateUser); app.MapPut("/api/admin/users/{userId}", HandleUpdateUser); app.MapPut("/api/admin/users/{userId}/password", HandleResetPassword); app.MapDelete("/api/admin/users/{userId}", HandleDeleteUser); app.MapPost("/api/admin/users/{userId}/impersonate", HandleImpersonate); app.MapPost("/api/admin/users/{userId}/login-link", HandleLoginLink); } private static async System.Threading.Tasks.Task HandleListUsers(HttpContext ctx, DatabaseService db) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } var users = await db.GetUsers(); var result = users.Select(u => new { userId = u.UserId, username = u.Username, displayName = u.DisplayName, isAdmin = u.IsAdmin, defaultPath = u.DefaultPath, canCreateAgents = u.CanCreateAgents, canUseCli = u.CanUseCli, createdAt = u.CreatedAt, updatedAt = u.UpdatedAt }); await ctx.Response.WriteAsJsonAsync(result); } private static async System.Threading.Tasks.Task HandleCreateUser(HttpContext ctx, DatabaseService db, AuthService auth) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } var body = await JsonSerializer.DeserializeAsync(ctx.Request.Body); var username = body.TryGetProperty("username", out var u) ? u.GetString() : null; var password = body.TryGetProperty("password", out var p) ? p.GetString() : null; var displayName = body.TryGetProperty("displayName", out var dn) ? dn.GetString() : null; var isAdmin = body.TryGetProperty("isAdmin", out var ia) && ia.GetBoolean(); var defaultPath = body.TryGetProperty("defaultPath", out var dp) ? dp.GetString() : null; var canCreateAgents = !body.TryGetProperty("canCreateAgents", out var cca) || cca.GetBoolean(); var canUseCli = !body.TryGetProperty("canUseCli", out var cuc) || cuc.GetBoolean(); if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) { ctx.Response.StatusCode = 400; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzername und Passwort erforderlich" }); return; } if (password.Length < 4) { ctx.Response.StatusCode = 400; await ctx.Response.WriteAsJsonAsync(new { error = "Passwort muss mindestens 4 Zeichen lang sein" }); return; } var existing = await db.GetUserByUsername(username); if (existing != null) { ctx.Response.StatusCode = 409; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzername bereits vergeben" }); return; } var hash = auth.HashPassword(password); var user = await db.CreateUser(username, hash, displayName, isAdmin, defaultPath, canCreateAgents, canUseCli); ctx.Response.StatusCode = 201; await ctx.Response.WriteAsJsonAsync(new { userId = user.UserId, username = user.Username, displayName = user.DisplayName, isAdmin = user.IsAdmin, defaultPath = user.DefaultPath, canCreateAgents = user.CanCreateAgents, canUseCli = user.CanUseCli, createdAt = user.CreatedAt }); } private static async System.Threading.Tasks.Task HandleUpdateUser(HttpContext ctx, DatabaseService db, string userId) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } var user = await db.GetUser(userId); if (user == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzer nicht gefunden" }); return; } var body = await JsonSerializer.DeserializeAsync(ctx.Request.Body); var username = body.TryGetProperty("username", out var u) ? u.GetString() : user.Username; var displayName = body.TryGetProperty("displayName", out var dn) ? dn.GetString() : user.DisplayName; var isAdmin = body.TryGetProperty("isAdmin", out var ia) ? ia.GetBoolean() : user.IsAdmin; var defaultPath = body.TryGetProperty("defaultPath", out var dp) ? dp.GetString() : user.DefaultPath; var canCreateAgents = body.TryGetProperty("canCreateAgents", out var cca) ? cca.GetBoolean() : user.CanCreateAgents; var canUseCli = body.TryGetProperty("canUseCli", out var cuc) ? cuc.GetBoolean() : user.CanUseCli; if (string.IsNullOrWhiteSpace(username)) { ctx.Response.StatusCode = 400; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzername darf nicht leer sein" }); return; } // Prüfen ob anderer User den Username bereits hat var existing = await db.GetUserByUsername(username); if (existing != null && existing.UserId != userId) { ctx.Response.StatusCode = 409; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzername bereits vergeben" }); return; } await db.UpdateUserAdmin(userId, username, displayName, isAdmin, defaultPath, canCreateAgents, canUseCli); var updated = await db.GetUser(userId); await ctx.Response.WriteAsJsonAsync(new { userId = updated.UserId, username = updated.Username, displayName = updated.DisplayName, isAdmin = updated.IsAdmin, defaultPath = updated.DefaultPath, canCreateAgents = updated.CanCreateAgents, canUseCli = updated.CanUseCli, createdAt = updated.CreatedAt, updatedAt = updated.UpdatedAt }); } private static async System.Threading.Tasks.Task HandleResetPassword(HttpContext ctx, DatabaseService db, AuthService auth, string userId) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } var user = await db.GetUser(userId); if (user == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzer nicht gefunden" }); return; } var body = await JsonSerializer.DeserializeAsync(ctx.Request.Body); var newPassword = body.TryGetProperty("password", out var p) ? p.GetString() : null; if (string.IsNullOrWhiteSpace(newPassword) || newPassword.Length < 4) { ctx.Response.StatusCode = 400; await ctx.Response.WriteAsJsonAsync(new { error = "Passwort muss mindestens 4 Zeichen lang sein" }); return; } var hash = auth.HashPassword(newPassword); await db.UpdateUserPassword(userId, hash); await ctx.Response.WriteAsJsonAsync(new { ok = true }); } private static async System.Threading.Tasks.Task HandleDeleteUser(HttpContext ctx, DatabaseService db, string userId) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } // Sich selbst löschen verhindern var currentUserId = ctx.GetUserId(); if (currentUserId == userId) { ctx.Response.StatusCode = 400; await ctx.Response.WriteAsJsonAsync(new { error = "Du kannst dich nicht selbst löschen" }); return; } var user = await db.GetUser(userId); if (user == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzer nicht gefunden" }); return; } await db.DeleteUser(userId); await ctx.Response.WriteAsJsonAsync(new { ok = true }); } private static async System.Threading.Tasks.Task HandleImpersonate(HttpContext ctx, DatabaseService db, AuthService auth, string userId) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } var user = await db.GetUser(userId); if (user == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzer nicht gefunden" }); return; } // Token für den Zieluser erstellen und als Cookie setzen var token = auth.CreateToken(user.UserId); ctx.Response.Cookies.Append("gott_token", token, new CookieOptions { HttpOnly = true, SameSite = SameSiteMode.Strict, Path = "/", MaxAge = System.TimeSpan.FromDays(365) }); await ctx.Response.WriteAsJsonAsync(new { userId = user.UserId, username = user.Username, displayName = user.DisplayName, isAdmin = user.IsAdmin }); } private static async System.Threading.Tasks.Task HandleLoginLink(HttpContext ctx, DatabaseService db, AuthService auth, string userId) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } var user = await db.GetUser(userId); if (user == null) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Benutzer nicht gefunden" }); return; } var token = auth.CreateToken(user.UserId); await ctx.Response.WriteAsJsonAsync(new { token }); } }