Added Authentication with user-auth and apikey-auth
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using watcher_monitoring.Models;
|
||||
|
||||
using watcher_monitoring.Data;
|
||||
using watcher_monitoring.Attributes;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -12,6 +13,7 @@ namespace watcher_monitoring.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
[ApiKeyAuth]
|
||||
public class APIController : Controller
|
||||
{
|
||||
private readonly WatcherDbContext _context;
|
||||
|
||||
153
watcher-monitoring/Controllers/ApiKeyController.cs
Normal file
153
watcher-monitoring/Controllers/ApiKeyController.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Cryptography;
|
||||
using watcher_monitoring.Data;
|
||||
using watcher_monitoring.Models;
|
||||
|
||||
namespace watcher_monitoring.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class ApiKeyController : Controller
|
||||
{
|
||||
private readonly WatcherDbContext _context;
|
||||
private readonly ILogger<ApiKeyController> _logger;
|
||||
|
||||
public ApiKeyController(WatcherDbContext context, ILogger<ApiKeyController> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// Generiert einen neuen API-Key
|
||||
private static string GenerateApiKey()
|
||||
{
|
||||
var randomBytes = new byte[32];
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomBytes);
|
||||
return Convert.ToBase64String(randomBytes).Replace("+", "").Replace("/", "").Replace("=", "");
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> CreateApiKey([FromBody] CreateApiKeyRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Name))
|
||||
{
|
||||
return BadRequest(new { error = "Name ist erforderlich" });
|
||||
}
|
||||
|
||||
var apiKey = new ApiKey
|
||||
{
|
||||
Key = GenerateApiKey(),
|
||||
Name = request.Name,
|
||||
Description = request.Description,
|
||||
ExpiresAt = request.ExpiresAt,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
_context.ApiKeys.Add(apiKey);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Neuer API-Key erstellt: {Name}", apiKey.Name);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
id = apiKey.Id,
|
||||
key = apiKey.Key, // Wird nur einmal zurückgegeben!
|
||||
name = apiKey.Name,
|
||||
description = apiKey.Description,
|
||||
createdAt = apiKey.CreatedAt,
|
||||
expiresAt = apiKey.ExpiresAt,
|
||||
isActive = apiKey.IsActive
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> ListApiKeys()
|
||||
{
|
||||
var apiKeys = await _context.ApiKeys
|
||||
.OrderByDescending(k => k.CreatedAt)
|
||||
.Select(k => new
|
||||
{
|
||||
id = k.Id,
|
||||
name = k.Name,
|
||||
description = k.Description,
|
||||
createdAt = k.CreatedAt,
|
||||
expiresAt = k.ExpiresAt,
|
||||
lastUsedAt = k.LastUsedAt,
|
||||
isActive = k.IsActive,
|
||||
isExpired = k.IsExpired,
|
||||
keyPreview = k.Key.Substring(0, Math.Min(8, k.Key.Length)) + "..." // Nur die ersten 8 Zeichen
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(apiKeys);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetApiKey(int id)
|
||||
{
|
||||
var apiKey = await _context.ApiKeys.FindAsync(id);
|
||||
|
||||
if (apiKey == null)
|
||||
{
|
||||
return NotFound(new { error = "API-Key nicht gefunden" });
|
||||
}
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
id = apiKey.Id,
|
||||
name = apiKey.Name,
|
||||
description = apiKey.Description,
|
||||
createdAt = apiKey.CreatedAt,
|
||||
expiresAt = apiKey.ExpiresAt,
|
||||
lastUsedAt = apiKey.LastUsedAt,
|
||||
isActive = apiKey.IsActive,
|
||||
isExpired = apiKey.IsExpired,
|
||||
keyPreview = apiKey.Key.Substring(0, Math.Min(8, apiKey.Key.Length)) + "..."
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPut("{id}/toggle")]
|
||||
public async Task<IActionResult> ToggleApiKey(int id)
|
||||
{
|
||||
var apiKey = await _context.ApiKeys.FindAsync(id);
|
||||
|
||||
if (apiKey == null)
|
||||
{
|
||||
return NotFound(new { error = "API-Key nicht gefunden" });
|
||||
}
|
||||
|
||||
apiKey.IsActive = !apiKey.IsActive;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("API-Key {Name} wurde {Status}", apiKey.Name, apiKey.IsActive ? "aktiviert" : "deaktiviert");
|
||||
|
||||
return Ok(new { isActive = apiKey.IsActive });
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteApiKey(int id)
|
||||
{
|
||||
var apiKey = await _context.ApiKeys.FindAsync(id);
|
||||
|
||||
if (apiKey == null)
|
||||
{
|
||||
return NotFound(new { error = "API-Key nicht gefunden" });
|
||||
}
|
||||
|
||||
_context.ApiKeys.Remove(apiKey);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("API-Key gelöscht: {Name}", apiKey.Name);
|
||||
|
||||
return Ok(new { message = "API-Key erfolgreich gelöscht" });
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateApiKeyRequest
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public DateTime? ExpiresAt { get; set; }
|
||||
}
|
||||
133
watcher-monitoring/Controllers/AuthController.cs
Normal file
133
watcher-monitoring/Controllers/AuthController.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Claims;
|
||||
using watcher_monitoring.Data;
|
||||
using watcher_monitoring.Models;
|
||||
|
||||
namespace watcher_monitoring.Controllers;
|
||||
|
||||
public class AuthController : Controller
|
||||
{
|
||||
private readonly WatcherDbContext _context;
|
||||
private readonly ILogger<AuthController> _logger;
|
||||
|
||||
public AuthController(WatcherDbContext context, ILogger<AuthController> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet]
|
||||
public IActionResult Login(string? returnUrl = null)
|
||||
{
|
||||
// Wenn der Benutzer bereits angemeldet ist, zur Startseite weiterleiten
|
||||
if (User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string? returnUrl = null)
|
||||
{
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Benutzer suchen
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Username == model.Username && u.IsActive);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Login-Versuch mit ungültigem Benutzernamen: {Username}", model.Username);
|
||||
TempData["Error"] = "Ungültiger Benutzername oder Passwort";
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// Passwort überprüfen (BCrypt)
|
||||
if (!BCrypt.Net.BCrypt.Verify(model.Password, user.Password))
|
||||
{
|
||||
_logger.LogWarning("Login-Versuch mit falschem Passwort für Benutzer: {Username}", model.Username);
|
||||
TempData["Error"] = "Ungültiger Benutzername oder Passwort";
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// LastLogin aktualisieren
|
||||
user.LastLogin = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Claims erstellen
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Email, user.Email),
|
||||
new Claim("LastLogin", user.LastLogin.ToString("o"))
|
||||
};
|
||||
|
||||
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
|
||||
|
||||
var authProperties = new AuthenticationProperties
|
||||
{
|
||||
IsPersistent = model.RememberMe,
|
||||
ExpiresUtc = model.RememberMe ? DateTimeOffset.UtcNow.AddDays(30) : DateTimeOffset.UtcNow.AddHours(8)
|
||||
};
|
||||
|
||||
await HttpContext.SignInAsync(
|
||||
CookieAuthenticationDefaults.AuthenticationScheme,
|
||||
claimsPrincipal,
|
||||
authProperties);
|
||||
|
||||
_logger.LogInformation("Benutzer {Username} erfolgreich angemeldet", user.Username);
|
||||
|
||||
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Fehler beim Login-Vorgang");
|
||||
TempData["Error"] = "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.";
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
var username = User.Identity?.Name ?? "Unbekannt";
|
||||
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
_logger.LogInformation("Benutzer {Username} erfolgreich abgemeldet", username);
|
||||
|
||||
return RedirectToAction("Login", "Auth");
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public IActionResult AccessDenied()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using watcher_monitoring.Models;
|
||||
|
||||
@@ -8,6 +9,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace watcher_monitoring.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly WatcherDbContext _context;
|
||||
|
||||
311
watcher-monitoring/Controllers/UserController.cs
Normal file
311
watcher-monitoring/Controllers/UserController.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
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<UserController> _logger;
|
||||
|
||||
public UserController(WatcherDbContext context, ILogger<UserController> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// GET: /User
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<bool> 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("=", "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user