using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Security.Cryptography; using watcher_monitoring.Data; using watcher_monitoring.Models; using BCrypt.Net; namespace watcher_monitoring.Controllers; [Authorize] [Route("[controller]")] public class UserController : Controller { private readonly WatcherDbContext _context; private readonly ILogger _logger; public UserController(WatcherDbContext context, ILogger logger) { _context = context; _logger = logger; } // GET: /User [HttpGet] public async Task Index() { var users = await _context.Users .Include(u => u.ApiKeys) .OrderByDescending(u => u.CreatedAt) .ToListAsync(); return View(users); } // GET: /User/Details/{id} [HttpGet("Details/{id}")] public async Task Details(int id) { var user = await _context.Users .Include(u => u.ApiKeys) .FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return NotFound(); } return View(user); } // GET: /User/Create [HttpGet("Create")] public IActionResult Create() { return View(); } // POST: /User/Create [HttpPost("Create")] [ValidateAntiForgeryToken] public async Task Create([Bind("Username,Email,Password")] User user) { if (ModelState.IsValid) { // Prüfen, ob Username oder Email bereits existiert var existingUser = await _context.Users .FirstOrDefaultAsync(u => u.Username == user.Username || u.Email == user.Email); if (existingUser != null) { if (existingUser.Username == user.Username) { ModelState.AddModelError("Username", "Benutzername ist bereits vergeben"); } if (existingUser.Email == user.Email) { ModelState.AddModelError("Email", "E-Mail-Adresse ist bereits registriert"); } return View(user); } // Passwort hashen mit BCrypt user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password); user.CreatedAt = DateTime.UtcNow; user.LastLogin = DateTime.UtcNow; user.IsActive = true; _context.Users.Add(user); await _context.SaveChangesAsync(); _logger.LogInformation("Neuer User erstellt: {Username}", user.Username); return RedirectToAction(nameof(Index)); } return View(user); } // GET: /User/Edit/{id} [HttpGet("Edit/{id}")] public async Task Edit(int id) { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } return View(user); } // POST: /User/Edit/{id} [HttpPost("Edit/{id}")] [ValidateAntiForgeryToken] public async Task Edit(int id, [Bind("Id,Username,Email,IsActive")] User updatedUser) { if (id != updatedUser.Id) { return NotFound(); } if (ModelState.IsValid) { try { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } // Prüfen, ob neuer Username oder Email bereits von anderem User verwendet wird var duplicate = await _context.Users .FirstOrDefaultAsync(u => u.Id != id && (u.Username == updatedUser.Username || u.Email == updatedUser.Email)); if (duplicate != null) { if (duplicate.Username == updatedUser.Username) { ModelState.AddModelError("Username", "Benutzername ist bereits vergeben"); } if (duplicate.Email == updatedUser.Email) { ModelState.AddModelError("Email", "E-Mail-Adresse ist bereits registriert"); } return View(updatedUser); } user.Username = updatedUser.Username; user.Email = updatedUser.Email; user.IsActive = updatedUser.IsActive; _context.Update(user); await _context.SaveChangesAsync(); _logger.LogInformation("User aktualisiert: {Username}", user.Username); return RedirectToAction(nameof(Index)); } catch (DbUpdateConcurrencyException) { if (!await UserExists(updatedUser.Id)) { return NotFound(); } throw; } } return View(updatedUser); } // POST: /User/Delete/{id} [HttpPost("Delete/{id}")] [ValidateAntiForgeryToken] public async Task Delete(int id) { var user = await _context.Users .Include(u => u.ApiKeys) .FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return NotFound(); } // Alle API-Keys des Users löschen _context.ApiKeys.RemoveRange(user.ApiKeys); _context.Users.Remove(user); await _context.SaveChangesAsync(); _logger.LogInformation("User gelöscht: {Username}", user.Username); return RedirectToAction(nameof(Index)); } // POST: /User/ToggleActive/{id} [HttpPost("ToggleActive/{id}")] [ValidateAntiForgeryToken] public async Task ToggleActive(int id) { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } user.IsActive = !user.IsActive; await _context.SaveChangesAsync(); _logger.LogInformation("User {Username} wurde {Status}", user.Username, user.IsActive ? "aktiviert" : "deaktiviert"); return RedirectToAction(nameof(Details), new { id }); } // POST: /User/GenerateApiKey/{id} [HttpPost("GenerateApiKey/{id}")] [ValidateAntiForgeryToken] public async Task GenerateApiKey(int id, string keyName, string? description, DateTime? expiresAt) { var user = await _context.Users.FindAsync(id); if (user == null) { return NotFound(); } if (string.IsNullOrWhiteSpace(keyName)) { TempData["Error"] = "API-Key-Name ist erforderlich"; return RedirectToAction(nameof(Details), new { id }); } var apiKey = new ApiKey { Key = GenerateApiKeyString(), Name = keyName, Description = description, ExpiresAt = expiresAt, IsActive = true, UserId = user.Id }; _context.ApiKeys.Add(apiKey); await _context.SaveChangesAsync(); _logger.LogInformation("Neuer API-Key für User {Username} erstellt: {KeyName}", user.Username, keyName); TempData["Success"] = $"API-Key erstellt: {apiKey.Key}"; TempData["NewApiKey"] = apiKey.Key; return RedirectToAction(nameof(Details), new { id }); } // POST: /User/DeleteApiKey/{userId}/{keyId} [HttpPost("DeleteApiKey/{userId}/{keyId}")] [ValidateAntiForgeryToken] public async Task DeleteApiKey(int userId, int keyId) { var apiKey = await _context.ApiKeys .FirstOrDefaultAsync(k => k.Id == keyId && k.UserId == userId); if (apiKey == null) { return NotFound(); } _context.ApiKeys.Remove(apiKey); await _context.SaveChangesAsync(); _logger.LogInformation("API-Key gelöscht: {KeyName}", apiKey.Name); TempData["Success"] = "API-Key erfolgreich gelöscht"; return RedirectToAction(nameof(Details), new { id = userId }); } // POST: /User/ToggleApiKey/{userId}/{keyId} [HttpPost("ToggleApiKey/{userId}/{keyId}")] [ValidateAntiForgeryToken] public async Task ToggleApiKey(int userId, int keyId) { var apiKey = await _context.ApiKeys .FirstOrDefaultAsync(k => k.Id == keyId && k.UserId == userId); if (apiKey == null) { return NotFound(); } apiKey.IsActive = !apiKey.IsActive; await _context.SaveChangesAsync(); _logger.LogInformation("API-Key {KeyName} wurde {Status}", apiKey.Name, apiKey.IsActive ? "aktiviert" : "deaktiviert"); return RedirectToAction(nameof(Details), new { id = userId }); } private async Task UserExists(int id) { return await _context.Users.AnyAsync(u => u.Id == id); } private static string GenerateApiKeyString() { var randomBytes = new byte[32]; using var rng = RandomNumberGenerator.Create(); rng.GetBytes(randomBytes); return Convert.ToBase64String(randomBytes).Replace("+", "").Replace("/", "").Replace("=", ""); } }