From 6a087aae486f177ce14ce7fb99aabbe5e4af6474 Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Tue, 20 May 2025 20:38:51 +0100 Subject: [PATCH] auth --- config/packages/security.yaml | 33 ++++++++----------- public/css/base.css | 10 ++++++ src/Controller/GuiController.php | 4 ++- src/Controller/SecurityController.php | 31 ++++++++++++++++++ templates/security/login.html.twig | 47 +++++++++++++++++++++++++++ todo.txt | 3 +- 6 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 src/Controller/SecurityController.php create mode 100644 templates/security/login.html.twig diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 367af25..cd1ece3 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,37 +1,30 @@ security: - # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' - # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: - users_in_memory: { memory: null } + app_user_provider: + memory: + users: + me@joeac.net: + password: '%env(resolve:JOEAC_PASSWORD)%' + roles: ['ROLE_EDITOR'] firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true - provider: users_in_memory - - # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#the-firewall - - # https://symfony.com/doc/current/security/impersonating_user.html - # switch_user: true - - # Easy way to control access for large sections of your site - # Note: Only the *first* access control that matches will be used - access_control: - # - { path: ^/admin, roles: ROLE_ADMIN } - # - { path: ^/profile, roles: ROLE_USER } + provider: app_user_provider + form_login: + login_path: login + check_path: login + enable_csrf: true + logout: + path: logout when@test: security: password_hashers: - # By default, password hashers are resource intensive and take time. This is - # important to generate secure password hashes. In tests however, secure hashes - # are not important, waste resources and increase test times. The following - # reduces the work factor to the lowest possible values. Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: algorithm: auto cost: 4 # Lowest possible value for bcrypt diff --git a/public/css/base.css b/public/css/base.css index fd5bbf2..df41b68 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -27,6 +27,7 @@ --colour-primary-fg: var(--colour-primary-90); --colour-primary-fg-accent: var(--colour-primary-80); --colour-primary-bg: var(--colour-primary-10); + --colour-primary-fg-accent: var(--colour-primary-20); --colour-code-fg: var(--colour-primary-90); --colour-code-bg: var(--colour-primary-15); --colour-hyperlink: var(--colour-hyperlink-80); @@ -55,6 +56,7 @@ --colour-primary-fg: var(--colour-primary-20); --colour-primary-fg-accent: var(--colour-primary-40); --colour-primary-bg: var(--colour-primary-95); + --colour-primary-fg-accent: var(--colour-primary-90); --colour-hyperlink: var(--colour-hyperlink-40); } } @@ -286,3 +288,11 @@ pre code { border-radius: none; padding: none; } + +/** Alerts */ +.alert { + border: 2px solid var(--colour-primary-fg); + border-radius: 2px; + padding-inline: var(--spacing-inline-xs); + background-color: var(--color-primary-fg-accent); +} diff --git a/src/Controller/GuiController.php b/src/Controller/GuiController.php index daa4f1e..a9a8f47 100644 --- a/src/Controller/GuiController.php +++ b/src/Controller/GuiController.php @@ -11,10 +11,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; use function Symfony\Component\HttpFoundation\Response; class GuiController extends AbstractController { - #[Route('/')] + #[Route('/', name: 'index')] #[Template('/index.html.twig')] public function index(): array { return [ @@ -37,6 +38,7 @@ class GuiController extends AbstractController { ]; } + #[IsGranted('ROLE_EDITOR')] #[Route('/notes/write')] #[Template('/write_note.html.twig')] public function writeNote( diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..87ca20e --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,31 @@ +getLastAuthenticationError(); + $lastUsername = $authenticationUtils->getLastUsername(); + + return $this->render('security/login.html.twig', [ + 'title' => 'Log in', + 'description' => 'Log in to Joe Carstairs\' personal website', + 'lastUsername' => $lastUsername, + 'error' => $error, + ]); + } + + #[Route(path: '/logout', name: 'logout')] + public function logout(): void + { + throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); + } +} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig new file mode 100644 index 0000000..f2cd861 --- /dev/null +++ b/templates/security/login.html.twig @@ -0,0 +1,47 @@ +{% extends 'base.html.twig' %} + +{% block content %} +
+

Log in

+ + {% if app.user %} +

+ You are logged in as {{ app.user.userIdentifier }}. + If you're not happy about that, log out. +

+ {% else %} + {% if error %} +
{{ error.messageKey|trans(error.messageData, 'security') }}
+ {% endif %} + +
+ + + + + + + + + + +
+ {% endif %} +
+{% endblock %} diff --git a/todo.txt b/todo.txt index f1d3a24..5254546 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,3 @@ -Authenticate everything -De-authenticate public routes Write an SQL script to insert all links from existing website as notes Sort links on index page by date descending Tidy up notes index page to match existing links index page @@ -37,3 +35,4 @@ Remove the notes index page Creating replies sends a Webmention Creating re-posts sends a Webmention Creating reacjis (reactions) sends a Webmention +Replace all href attribute values with calls to path() or importmap()