Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[{compose.yaml,compose.*.yaml}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
7 changes: 7 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,11 @@ TRUSTED_HOSTS=
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=546bb96105d5cf940dabee2145436526
APP_SHARE_DIR=var/share
###< symfony/framework-bundle ###

###> symfony/routing ###
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
DEFAULT_URI=http://localhost
###< symfony/routing ###
4 changes: 4 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

###> symfony/framework-bundle ###
APP_SECRET=d49c44ded0cfe17c6222e4340544ff91
###< symfony/framework-bundle ###
11 changes: 3 additions & 8 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,9 @@
EOF;

$finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/config')
->in(__DIR__ . '/public')
->in(__DIR__ . '/src')
->append([
__FILE__,
__DIR__ . '/.twig-cs-fixer.php',
__DIR__ . '/rector.php',
])
->in(__DIR__)
->exclude('var')
->notPath('config/reference.php')
;

$config = new PhpCsFixer\Config();
Expand Down
87 changes: 87 additions & 0 deletions assets/controllers/csrf_protection_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
const tokenCheck = /^[-_/+a-zA-Z0-9]{24,}$/;

// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager
// Use `form.requestSubmit()` to ensure that the submit event is triggered. Using `form.submit()` will not trigger the event
// and thus this event-listener will not be executed.
document.addEventListener(
'submit',
function (event) {
generateCsrfToken(event.target);
},
true
);

// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
document.addEventListener('turbo:submit-start', function (event) {
const h = generateCsrfHeaders(event.detail.formSubmission.formElement);
Object.keys(h).map(function (k) {
event.detail.formSubmission.fetchRequest.headers[k] = h[k];
});
});

// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
document.addEventListener('turbo:submit-end', function (event) {
removeCsrfToken(event.detail.formSubmission.formElement);
});

export function generateCsrfToken(formElement) {
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');

if (!csrfField) {
return;
}

let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
let csrfToken = csrfField.value;

if (!csrfCookie && nameCheck.test(csrfToken)) {
csrfField.setAttribute('data-csrf-protection-cookie-value', (csrfCookie = csrfToken));
csrfField.defaultValue = csrfToken = btoa(
String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18)))
);
}
csrfField.dispatchEvent(new Event('change', { bubbles: true }));

if (csrfCookie && tokenCheck.test(csrfToken)) {
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
}
}

export function generateCsrfHeaders(formElement) {
const headers = {};
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');

if (!csrfField) {
return headers;
}

const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');

if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
headers[csrfCookie] = csrfField.value;
}

return headers;
}

export function removeCsrfToken(formElement) {
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');

if (!csrfField) {
return;
}

const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');

if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';

document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
}
}

/* stimulusFetch: 'lazy' */
export default 'csrf-protection-controller';
16 changes: 16 additions & 0 deletions assets/controllers/hello_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Controller } from '@hotwired/stimulus';

/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}
2 changes: 1 addition & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import '../scss/app.scss';

import '../bootstrap';
import '../stimulus_bootstrap';
7 changes: 7 additions & 0 deletions assets/js/component/Item.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function Items({ id, title, body }) {
return (
Expand All @@ -14,3 +15,9 @@ export default function Items({ id, title, body }) {
</div>
);
};

Items.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
};
1 change: 0 additions & 1 deletion assets/bootstrap.js → assets/stimulus_bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ export const app = startStimulusApp(
);
// register any custom, 3rd party controllers here
// app.register('some_controller_name', SomeImportedController);

window.app = app;
57 changes: 28 additions & 29 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,41 @@
"php": "^8.4",
"ext-ctype": "*",
"ext-iconv": "*",
"bacon/bacon-qr-code": "^3.0.1",
"league/commonmark": "^2.7.1",
"bacon/bacon-qr-code": "^3.0.3",
"league/commonmark": "^2.8.0",
"league/glide-symfony": "^2.0.1",
"pragmarx/google2fa": "^8.0.3",
"runtime/frankenphp-symfony": "^0.2.0",
"symfony/asset": "7.3.*",
"symfony/console": "7.3.*",
"symfony/dotenv": "7.3.*",
"symfony/expression-language": "7.3.*",
"symfony/flex": "^2.9",
"symfony/form": "7.3.*",
"symfony/framework-bundle": "7.3.*",
"symfony/monolog-bundle": "^3.10",
"symfony/runtime": "7.3.*",
"symfony/security-bundle": "7.3.*",
"pragmarx/google2fa": "^9",
"symfony/asset": "7.4.*",
"symfony/console": "7.4.*",
"symfony/dotenv": "7.4.*",
"symfony/expression-language": "7.4.*",
"symfony/flex": "^2.10",
"symfony/form": "7.4.*",
"symfony/framework-bundle": "7.4.*",
"symfony/monolog-bundle": "^3.11.1",
"symfony/runtime": "7.4.*",
"symfony/security-bundle": "7.4.*",
"symfony/stimulus-bundle": "^2.31",
"symfony/twig-bundle": "7.3.*",
"symfony/twig-bundle": "7.4.*",
"symfony/ux-turbo": "^2.31",
"symfony/webpack-encore-bundle": "^2.3",
"symfony/yaml": "7.3.*",
"twig/extra-bundle": "^3.22",
"symfony/webpack-encore-bundle": "^2.4",
"symfony/yaml": "7.4.*",
"twig/extra-bundle": "^3.22.2",
"twig/markdown-extra": "^3.22",
"twig/twig": "^3.22"
"twig/twig": "^3.22.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.89.1",
"friendsofphp/php-cs-fixer": "^3.92.3",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.31",
"phpstan/phpstan-symfony": "^2.0.8",
"rector/rector": "^2.2.7",
"phpstan/phpstan": "^2.1.33",
"phpstan/phpstan-symfony": "^2.0.9",
"rector/rector": "^2.3.0",
"symfony/apache-pack": "^1.0.1",
"symfony/debug-bundle": "7.3.*",
"symfony/maker-bundle": "^1.64",
"symfony/stopwatch": "7.3.*",
"symfony/web-profiler-bundle": "7.3.*",
"vincentlanglet/twig-cs-fixer": "^3.10"
"symfony/debug-bundle": "7.4.*",
"symfony/maker-bundle": "^1.65.1",
"symfony/stopwatch": "7.4.*",
"symfony/web-profiler-bundle": "7.4.*",
"vincentlanglet/twig-cs-fixer": "^3.11"
},
"config": {
"preferred-install": {
Expand Down Expand Up @@ -105,7 +104,7 @@
"extra": {
"symfony": {
"allow-contrib": true,
"require": "7.3.*"
"require": "7.4.*"
}
}
}
Loading