This commit is contained in:
2025-05-20 20:38:51 +01:00
parent 7a051cf7ad
commit 6a087aae48
6 changed files with 105 additions and 23 deletions

View File

@@ -1,37 +1,30 @@
security: security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers: password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers: providers:
users_in_memory: { memory: null } app_user_provider:
memory:
users:
me@joeac.net:
password: '%env(resolve:JOEAC_PASSWORD)%'
roles: ['ROLE_EDITOR']
firewalls: firewalls:
dev: dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/ pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false security: false
main: main:
lazy: true lazy: true
provider: users_in_memory provider: app_user_provider
form_login:
# activate different ways to authenticate login_path: login
# https://symfony.com/doc/current/security.html#the-firewall check_path: login
enable_csrf: true
# https://symfony.com/doc/current/security/impersonating_user.html logout:
# switch_user: true path: logout
# 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 }
when@test: when@test:
security: security:
password_hashers: 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: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto algorithm: auto
cost: 4 # Lowest possible value for bcrypt cost: 4 # Lowest possible value for bcrypt

View File

@@ -27,6 +27,7 @@
--colour-primary-fg: var(--colour-primary-90); --colour-primary-fg: var(--colour-primary-90);
--colour-primary-fg-accent: var(--colour-primary-80); --colour-primary-fg-accent: var(--colour-primary-80);
--colour-primary-bg: var(--colour-primary-10); --colour-primary-bg: var(--colour-primary-10);
--colour-primary-fg-accent: var(--colour-primary-20);
--colour-code-fg: var(--colour-primary-90); --colour-code-fg: var(--colour-primary-90);
--colour-code-bg: var(--colour-primary-15); --colour-code-bg: var(--colour-primary-15);
--colour-hyperlink: var(--colour-hyperlink-80); --colour-hyperlink: var(--colour-hyperlink-80);
@@ -55,6 +56,7 @@
--colour-primary-fg: var(--colour-primary-20); --colour-primary-fg: var(--colour-primary-20);
--colour-primary-fg-accent: var(--colour-primary-40); --colour-primary-fg-accent: var(--colour-primary-40);
--colour-primary-bg: var(--colour-primary-95); --colour-primary-bg: var(--colour-primary-95);
--colour-primary-fg-accent: var(--colour-primary-90);
--colour-hyperlink: var(--colour-hyperlink-40); --colour-hyperlink: var(--colour-hyperlink-40);
} }
} }
@@ -286,3 +288,11 @@ pre code {
border-radius: none; border-radius: none;
padding: 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);
}

View File

@@ -11,10 +11,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use function Symfony\Component\HttpFoundation\Response; use function Symfony\Component\HttpFoundation\Response;
class GuiController extends AbstractController { class GuiController extends AbstractController {
#[Route('/')] #[Route('/', name: 'index')]
#[Template('/index.html.twig')] #[Template('/index.html.twig')]
public function index(): array { public function index(): array {
return [ return [
@@ -37,6 +38,7 @@ class GuiController extends AbstractController {
]; ];
} }
#[IsGranted('ROLE_EDITOR')]
#[Route('/notes/write')] #[Route('/notes/write')]
#[Template('/write_note.html.twig')] #[Template('/write_note.html.twig')]
public function writeNote( public function writeNote(

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
#[Route(path: '/login', name: 'login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
$error = $authenticationUtils->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.');
}
}

View File

@@ -0,0 +1,47 @@
{% extends 'base.html.twig' %}
{% block content %}
<section>
<h1>Log in</h1>
{% if app.user %}
<p>
You are logged in as {{ app.user.userIdentifier }}.
If you're not happy about that, <a href="{{ path('logout') }}">log out</a>.
</p>
{% else %}
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<form action="{{ path('login') }}" method="POST">
<label for="username">Email</label>
<input
type="email"
value="{{ lastUsername }}"
name="_username"
id="username"
class="form-control"
autocomplete="email"
required
autofocus
>
<label for="password">Password</label>
<input
type="password"
name="_password"
id="password"
class="form-control"
autocomplete="current-password"
required
>
<input type="hidden" name="_target_path" value="{{ path('index') }}">
<input type="hidden" name="_csrf_token" data-controller="csrf-protection" value="{{ csrf_token('authenticate') }}">
<button type="submit">Log in</button>
</form>
{% endif %}
</section>
{% endblock %}

View File

@@ -1,5 +1,3 @@
Authenticate everything
De-authenticate public routes
Write an SQL script to insert all links from existing website as notes Write an SQL script to insert all links from existing website as notes
Sort links on index page by date descending Sort links on index page by date descending
Tidy up notes index page to match existing links index page 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 replies sends a Webmention
Creating re-posts sends a Webmention Creating re-posts sends a Webmention
Creating reacjis (reactions) sends a Webmention Creating reacjis (reactions) sends a Webmention
Replace all href attribute values with calls to path() or importmap()