From c56b8a7f3210d18067db0f23753cc4351c71117a Mon Sep 17 00:00:00 2001 From: LD-Reborn Date: Sun, 14 Dec 2025 11:38:32 +0100 Subject: [PATCH] Added basic authentication and localization --- .gitignore | 3 +- src/Server/Controllers/AccountController.cs | 65 +++++++++++++++++++ .../Controllers/Frontend/HomeController.cs | 1 + src/Server/Models/Auth.cs | 13 ++++ src/Server/Resources/SharedResources.de.resx | 21 ++++++ src/Server/Resources/SharedResources.en.resx | 21 ++++++ src/Server/Services/LocalizationService.cs | 18 +++++ src/Server/Views/Account/Login.cshtml | 28 ++++++++ src/Server/Views/Shared/_Layout.cshtml | 21 ++++-- 9 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 src/Server/Controllers/AccountController.cs create mode 100644 src/Server/Models/Auth.cs create mode 100644 src/Server/Resources/SharedResources.de.resx create mode 100644 src/Server/Resources/SharedResources.en.resx create mode 100644 src/Server/Services/LocalizationService.cs create mode 100644 src/Server/Views/Account/Login.cshtml diff --git a/.gitignore b/.gitignore index d5d7fbf..45efc47 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ src/Indexer/Scripts/__pycache__ src/Indexer/logs src/Server/logs src/Shared/bin -src/Shared/obj \ No newline at end of file +src/Shared/obj +src/Server/wwwroot/logs/* diff --git a/src/Server/Controllers/AccountController.cs b/src/Server/Controllers/AccountController.cs new file mode 100644 index 0000000..9c0f19d --- /dev/null +++ b/src/Server/Controllers/AccountController.cs @@ -0,0 +1,65 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Server.Models; + +namespace Server.Controllers; + +[Route("[Controller]")] +public class AccountController : Controller +{ + private readonly SimpleAuthOptions _options; + + public AccountController(IOptions options) + { + _options = options.Value; + } + + [HttpGet("Login")] + public IActionResult Login(string? returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + return View(); + } + + [HttpPost("Login")] + public async Task Login( + string username, + string password, + string? returnUrl = null) + { + var user = _options.Users.SingleOrDefault(u => + u.Username == username && u.Password == password); + + if (user == null) + { + ModelState.AddModelError("", "Invalid credentials"); + return View(); + } + + var claims = new List + { + new(ClaimTypes.Name, user.Username) + }; + + claims.AddRange(user.Roles.Select(r => + new Claim(ClaimTypes.Role, r))); + + var identity = new ClaimsIdentity( + claims, "AppCookie"); + + await HttpContext.SignInAsync( + "AppCookie", + new ClaimsPrincipal(identity)); + + return Redirect(returnUrl ?? "/"); + } + + [HttpGet("Logout")] + public async Task Logout() + { + await HttpContext.SignOutAsync("AppCookie"); + return RedirectToAction("Login"); + } +} diff --git a/src/Server/Controllers/Frontend/HomeController.cs b/src/Server/Controllers/Frontend/HomeController.cs index cb6d4b6..f0467ab 100644 --- a/src/Server/Controllers/Frontend/HomeController.cs +++ b/src/Server/Controllers/Frontend/HomeController.cs @@ -16,6 +16,7 @@ public class HomeController : Controller _logger = logger; } + [Authorize] [HttpGet("/")] public IActionResult Index() { diff --git a/src/Server/Models/Auth.cs b/src/Server/Models/Auth.cs new file mode 100644 index 0000000..1bd0fa4 --- /dev/null +++ b/src/Server/Models/Auth.cs @@ -0,0 +1,13 @@ +namespace Server.Models; + +public class SimpleAuthOptions +{ + public List Users { get; set; } = new(); +} + +public class SimpleUser +{ + public string Username { get; set; } = ""; + public string Password { get; set; } = ""; + public string[] Roles { get; set; } = Array.Empty(); +} diff --git a/src/Server/Resources/SharedResources.de.resx b/src/Server/Resources/SharedResources.de.resx new file mode 100644 index 0000000..e18c07d --- /dev/null +++ b/src/Server/Resources/SharedResources.de.resx @@ -0,0 +1,21 @@ + + + + Angemeldet als + + + Abmelden + + + Anmelden + + + Benutzername + + + Passwort + + + Ungültiger Benutzername oder Passwort. + + \ No newline at end of file diff --git a/src/Server/Resources/SharedResources.en.resx b/src/Server/Resources/SharedResources.en.resx new file mode 100644 index 0000000..bd6e064 --- /dev/null +++ b/src/Server/Resources/SharedResources.en.resx @@ -0,0 +1,21 @@ + + + + Logged in as + + + Log out + + + Login + + + Username + + + Password + + + Invalid credentials. + + \ No newline at end of file diff --git a/src/Server/Services/LocalizationService.cs b/src/Server/Services/LocalizationService.cs new file mode 100644 index 0000000..2e51070 --- /dev/null +++ b/src/Server/Services/LocalizationService.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Localization; + +namespace Server.Services; + +public class LocalizationService +{ + private readonly IStringLocalizer _localizer; + + public LocalizationService(IStringLocalizerFactory factory) + { + _localizer = factory.Create("SharedResources", "Server"); + } + + public string Get(string key) => _localizer[key]; + + public string this[string key] => _localizer[key]; + public string this[string key, params object[] args] => _localizer[key, args]; +} \ No newline at end of file diff --git a/src/Server/Views/Account/Login.cshtml b/src/Server/Views/Account/Login.cshtml new file mode 100644 index 0000000..e3b132f --- /dev/null +++ b/src/Server/Views/Account/Login.cshtml @@ -0,0 +1,28 @@ +@using Server.Services +@inject LocalizationService T +@{ + ViewData["Title"] = "Login"; + var returnUrl = ViewData["ReturnUrl"] as string; +} + +
+

Login

+
+
+ + +
+ +
+ + +
+ + + + @if (!ViewData.ModelState.IsValid) + { +

Invalid username or password

+ } +
+
\ No newline at end of file diff --git a/src/Server/Views/Shared/_Layout.cshtml b/src/Server/Views/Shared/_Layout.cshtml index 2c3ee38..042d42e 100644 --- a/src/Server/Views/Shared/_Layout.cshtml +++ b/src/Server/Views/Shared/_Layout.cshtml @@ -19,12 +19,21 @@