gestion de la connexion de l'utilisateur
This commit is contained in:
2
.env
2
.env
@@ -1,5 +1,5 @@
|
||||
APP_ENV=dev
|
||||
APP_SECRET=
|
||||
APP_SECRET=je_te_remplis_parce_que_tu_me_mets_des_messages_d_erreur
|
||||
APP_SHARE_DIR=var/share
|
||||
APP_VERSION=0.0.1
|
||||
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -33,3 +33,8 @@ Thumbs.db
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
###< symfony/webpack-encore-bundle ###
|
||||
|
||||
###> symfony/asset-mapper ###
|
||||
/public/assets/
|
||||
/assets/vendor/
|
||||
###< symfony/asset-mapper ###
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"symfony/web-link": "8.0.*",
|
||||
"symfony/webpack-encore-bundle": "^2.4",
|
||||
"symfony/yaml": "8.0.*",
|
||||
"symfonycasts/tailwind-bundle": "^0.12.0",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
@@ -80,7 +81,8 @@
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd",
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd",
|
||||
"importmap:install": "symfony-cmd"
|
||||
},
|
||||
"post-install-cmd": [
|
||||
"@auto-scripts"
|
||||
|
||||
228
composer.lock
generated
228
composer.lock
generated
@@ -4,8 +4,85 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "4597facec97a6ff342cba0371179ad02",
|
||||
"content-hash": "b2f5377481dc83317c4b7c49f2dd183e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "3.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/semver.git",
|
||||
"reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
|
||||
"reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.11",
|
||||
"symfony/phpunit-bridge": "^3 || ^7"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\Semver\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nils Adermann",
|
||||
"email": "naderman@naderman.de",
|
||||
"homepage": "http://www.naderman.de"
|
||||
},
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be"
|
||||
},
|
||||
{
|
||||
"name": "Rob Bast",
|
||||
"email": "rob.bast@gmail.com",
|
||||
"homepage": "http://robbast.nl"
|
||||
}
|
||||
],
|
||||
"description": "Semver library that offers utilities, version constraint parsing and validation.",
|
||||
"keywords": [
|
||||
"semantic",
|
||||
"semver",
|
||||
"validation",
|
||||
"versioning"
|
||||
],
|
||||
"support": {
|
||||
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||
"issues": "https://github.com/composer/semver/issues",
|
||||
"source": "https://github.com/composer/semver/tree/3.4.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/composer",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-20T19:15:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/collections",
|
||||
"version": "2.6.0",
|
||||
@@ -1950,6 +2027,87 @@
|
||||
],
|
||||
"time": "2026-01-13T13:06:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/asset-mapper",
|
||||
"version": "v8.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/asset-mapper.git",
|
||||
"reference": "80635c3722b9bb5481e0282497ae23796dcd3712"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/asset-mapper/zipball/80635c3722b9bb5481e0282497ae23796dcd3712",
|
||||
"reference": "80635c3722b9bb5481e0282497ae23796dcd3712",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/semver": "^3.0",
|
||||
"php": ">=8.4",
|
||||
"symfony/filesystem": "^7.4|^8.0",
|
||||
"symfony/http-client": "^7.4|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/asset": "^7.4|^8.0",
|
||||
"symfony/browser-kit": "^7.4|^8.0",
|
||||
"symfony/console": "^7.4|^8.0",
|
||||
"symfony/event-dispatcher-contracts": "^3.0",
|
||||
"symfony/finder": "^7.4|^8.0",
|
||||
"symfony/framework-bundle": "^7.4|^8.0",
|
||||
"symfony/http-foundation": "^7.4|^8.0",
|
||||
"symfony/http-kernel": "^7.4|^8.0",
|
||||
"symfony/process": "^7.4|^8.0",
|
||||
"symfony/runtime": "^7.4|^8.0",
|
||||
"symfony/web-link": "^7.4|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\AssetMapper\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Maps directories of assets & makes them available in a public directory with versioned filenames.",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/asset-mapper/tree/v8.0.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-02-17T13:07:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"version": "v8.0.5",
|
||||
@@ -3293,16 +3451,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/form",
|
||||
"version": "v8.0.4",
|
||||
"version": "v8.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/form.git",
|
||||
"reference": "c34ec2c2648e2dfedab3ce7e3c6c86f8d89c3092"
|
||||
"reference": "954e17b053dad9fb227ebd90260752e3a46bb06a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/form/zipball/c34ec2c2648e2dfedab3ce7e3c6c86f8d89c3092",
|
||||
"reference": "c34ec2c2648e2dfedab3ce7e3c6c86f8d89c3092",
|
||||
"url": "https://api.github.com/repos/symfony/form/zipball/954e17b053dad9fb227ebd90260752e3a46bb06a",
|
||||
"reference": "954e17b053dad9fb227ebd90260752e3a46bb06a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3364,7 +3522,7 @@
|
||||
"description": "Allows to easily create, process and reuse HTML forms",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/form/tree/v8.0.4"
|
||||
"source": "https://github.com/symfony/form/tree/v8.0.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3384,7 +3542,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-23T11:07:10+00:00"
|
||||
"time": "2026-03-06T13:17:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/framework-bundle",
|
||||
@@ -7473,6 +7631,62 @@
|
||||
],
|
||||
"time": "2025-12-04T18:17:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfonycasts/tailwind-bundle",
|
||||
"version": "v0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SymfonyCasts/tailwind-bundle.git",
|
||||
"reference": "17c85e25d3ceb54b8599e8ca4c5b67c485f2a48a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SymfonyCasts/tailwind-bundle/zipball/17c85e25d3ceb54b8599e8ca4c5b67c485f2a48a",
|
||||
"reference": "17c85e25d3ceb54b8599e8ca4c5b67c485f2a48a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/asset-mapper": "^6.3|^7.0|^8.0",
|
||||
"symfony/cache": "^6.3|^7.0|^8.0",
|
||||
"symfony/console": "^5.4|^6.3|^7.0|^8.0",
|
||||
"symfony/deprecation-contracts": "^2.2|^3.0",
|
||||
"symfony/http-client": "^5.4|^6.3|^7.0|^8.0",
|
||||
"symfony/process": "^5.4|^6.3|^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"symfony/filesystem": "^6.3|^7.0|^8.0",
|
||||
"symfony/framework-bundle": "^6.3|^7.0|^8.0",
|
||||
"symfony/phpunit-bridge": "^6.3.9|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfonycasts\\TailwindBundle\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ryan Weaver",
|
||||
"homepage": "https://symfonycasts.com"
|
||||
}
|
||||
],
|
||||
"description": "Delightful Tailwind Support for Symfony + AssetMapper",
|
||||
"keywords": [
|
||||
"asset-mapper",
|
||||
"tailwind"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/SymfonyCasts/tailwind-bundle/issues",
|
||||
"source": "https://github.com/SymfonyCasts/tailwind-bundle/tree/v0.12.0"
|
||||
},
|
||||
"time": "2025-11-24T10:14:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/extra-bundle",
|
||||
"version": "v3.23.0",
|
||||
|
||||
@@ -15,4 +15,5 @@ return [
|
||||
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
||||
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfonycasts\TailwindBundle\SymfonycastsTailwindBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
11
config/packages/asset_mapper.yaml
Normal file
11
config/packages/asset_mapper.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
framework:
|
||||
asset_mapper:
|
||||
# The paths to make available to the asset mapper.
|
||||
paths:
|
||||
- assets/
|
||||
missing_import_mode: strict
|
||||
|
||||
when@prod:
|
||||
framework:
|
||||
asset_mapper:
|
||||
missing_import_mode: warn
|
||||
@@ -32,7 +32,17 @@ security:
|
||||
check_path: app_login
|
||||
enable_csrf: true
|
||||
logout:
|
||||
# où rediriger après la déconnexion
|
||||
path: app_logout
|
||||
# custom_authenticator: App\Security\AppCustomAuthenticator
|
||||
|
||||
remember_me:
|
||||
secret: '%kernel.secret%'
|
||||
lifetime: 604800
|
||||
path: /
|
||||
# by default, the feature is enabled by checking a checkbox in the
|
||||
# login form, uncomment the following line to always enable it.
|
||||
#always_remember_me: true
|
||||
# where to redirect after logout
|
||||
# target: app_any_route
|
||||
|
||||
@@ -45,8 +55,8 @@ security:
|
||||
# Note: Only the *first* matching rule is applied
|
||||
# autorisations
|
||||
access_control:
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
- { path: ^/admin, roles: ROLE_ADMIN }
|
||||
- { path: ^/user, roles: ROLE_USER }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
|
||||
6
config/packages/symfonycasts_tailwind.yaml
Normal file
6
config/packages/symfonycasts_tailwind.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
symfonycasts_tailwind:
|
||||
# Specify the EXACT version of Tailwind CSS you want to use
|
||||
binary_version: 'v4.1.11'
|
||||
|
||||
# Alternatively, you can specify the path to the binary that you manage yourself
|
||||
#binary: 'node_modules/.bin/tailwindcss'
|
||||
@@ -280,7 +280,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* }>,
|
||||
* },
|
||||
* asset_mapper?: bool|array{ // Asset Mapper configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* paths?: array<string, scalar|Param|null>,
|
||||
* excluded_patterns?: list<scalar|Param|null>,
|
||||
* exclude_dotfiles?: bool|Param, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true
|
||||
@@ -1465,6 +1465,15 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* },
|
||||
* default_transport?: scalar|Param|null, // Default: "default"
|
||||
* }
|
||||
* @psalm-type SymfonycastsTailwindConfig = array{
|
||||
* input_css?: list<scalar|Param|null>,
|
||||
* config_file?: scalar|Param|null, // Path to the tailwind.config.js file // Default: "%kernel.project_dir%/tailwind.config.js"
|
||||
* binary?: scalar|Param|null, // The tailwind binary to use instead of downloading a new one // Default: null
|
||||
* binary_version?: scalar|Param|null, // Tailwind CLI version to download - null means the latest version // Default: null
|
||||
* binary_platform?: "auto"|"linux-arm64"|"linux-arm64-musl"|"linux-x64"|"linux-x64-musl"|"macos-arm64"|"macos-x64"|"windows-x64"|Param, // Tailwind CLI platform to download - "auto" will try to detect the platform automatically // Default: "auto"
|
||||
* postcss_config_file?: scalar|Param|null, // Path to PostCSS config file which is passed to the Tailwind CLI // Default: null
|
||||
* strict_mode?: bool|Param|null, // When enabled, an exception will be thrown if there are no built assets (default: false in `test` env, true otherwise) // Default: null
|
||||
* }
|
||||
* @psalm-type ConfigType = array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
@@ -1479,6 +1488,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* webpack_encore?: WebpackEncoreConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* turbo?: TurboConfig,
|
||||
* symfonycasts_tailwind?: SymfonycastsTailwindConfig,
|
||||
* "when@dev"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
@@ -1496,6 +1506,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* webpack_encore?: WebpackEncoreConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* turbo?: TurboConfig,
|
||||
* symfonycasts_tailwind?: SymfonycastsTailwindConfig,
|
||||
* },
|
||||
* "when@prod"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
@@ -1511,6 +1522,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* webpack_encore?: WebpackEncoreConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* turbo?: TurboConfig,
|
||||
* symfonycasts_tailwind?: SymfonycastsTailwindConfig,
|
||||
* },
|
||||
* "when@test"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
@@ -1527,6 +1539,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* webpack_encore?: WebpackEncoreConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* turbo?: TurboConfig,
|
||||
* symfonycasts_tailwind?: SymfonycastsTailwindConfig,
|
||||
* },
|
||||
* ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
|
||||
* imports?: ImportsConfig,
|
||||
|
||||
28
importmap.php
Normal file
28
importmap.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Returns the importmap for this application.
|
||||
*
|
||||
* - "path" is a path inside the asset mapper system. Use the
|
||||
* "debug:asset-map" command to see the full list of paths.
|
||||
*
|
||||
* - "entrypoint" (JavaScript only) set to true for any module that will
|
||||
* be used as an "entrypoint" (and passed to the importmap() Twig function).
|
||||
*
|
||||
* The "importmap:require" command can be used to add new entries to this file.
|
||||
*/
|
||||
return [
|
||||
'app' => [
|
||||
'path' => './assets/app.js',
|
||||
'entrypoint' => true,
|
||||
],
|
||||
'@hotwired/stimulus' => [
|
||||
'version' => '3.2.2',
|
||||
],
|
||||
'@symfony/stimulus-bundle' => [
|
||||
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
||||
],
|
||||
'@hotwired/turbo' => [
|
||||
'version' => '7.3.0',
|
||||
],
|
||||
];
|
||||
@@ -2,38 +2,63 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use LogicException;
|
||||
use App\Repository\UserRepository; // 👈 AJOUTE CETTE LIGNE ICI !
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; // 👈 ET CELLE-CI AUSSI !
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
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: 'app_login', methods: ['GET','POST'])]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
#[Route(path: '/test-password', name: 'test_password')]
|
||||
public function testPassword(UserRepository $userRepo, UserPasswordHasherInterface $hasher): Response
|
||||
{
|
||||
// 1. On va chercher ton admin directement en base
|
||||
$admin = $userRepo->findOneBy(['email' => 'admin@kaz.fr']);
|
||||
|
||||
if (!$admin) {
|
||||
dd("L'utilisateur n'existe pas dans la base lue par ce contrôleur !");
|
||||
}
|
||||
|
||||
// 2. On vérifie si "password" est bien le bon mot de passe
|
||||
$isValid = $hasher->isPasswordValid($admin, 'password');
|
||||
|
||||
// 3. On affiche le résultat brut
|
||||
dd([
|
||||
'email' => $admin->getEmail(),
|
||||
'mot_de_passe_hache' => $admin->getPassword(),
|
||||
'est_ce_que_password_est_valide' => $isValid
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/login', name: 'app_login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils, Request $request): Response // 👈 2. ON AJOUTE $request ICI
|
||||
{
|
||||
// 🚨 3. NOTRE CODE DE DÉTECTIVE POUR VOIR CE QUI EST ENVOYÉ :
|
||||
if ($request->isMethod('POST')) {
|
||||
dd($request->request->all());
|
||||
}
|
||||
// 🚨 FIN DU CODE DE DÉTECTIVE
|
||||
|
||||
// si on a un utilisateur déjà connecté, alors on le redirige sur la page d'accueil
|
||||
if ($this->getUser()) {
|
||||
return $this->redirectToRoute('app_home');
|
||||
}
|
||||
|
||||
// get the login error if there is one
|
||||
// Récupération de l'erreur de connexion (s'il y en a une)
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
|
||||
// last username entered by the user
|
||||
// Récupération du dernier nom d'utilisateur saisi par l'adhérent
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('security/login.html.twig', [
|
||||
'last_username' => $lastUsername,
|
||||
'error' => $error,
|
||||
'error' => $error
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(path: '/logout', name: 'app_logout', methods: ['POST'])]
|
||||
#[Route(path: '/logout', name: 'app_logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
throw new LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Form\ChangePasswordType;
|
||||
use App\Service\KazApiService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||
@@ -14,9 +19,6 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
class UserController extends AbstractController
|
||||
{
|
||||
|
||||
// TODO : UserPasswordHasherInterface
|
||||
// voir : https://symfony.com/doc/current/security/passwords.html#hashing-the-password
|
||||
/**
|
||||
* Permet de vérifier si un utilisateur existe dans le ldap.
|
||||
*
|
||||
@@ -30,6 +32,7 @@ class UserController extends AbstractController
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
|
||||
#[Route('/user/{email}', name: 'app_user', methods: ['GET'])]
|
||||
public function index(string $email, KazApiService $apiClient): Response
|
||||
{
|
||||
@@ -39,4 +42,45 @@ class UserController extends AbstractController
|
||||
'exist' => $exist,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/user/mot-de-passe', name: 'app_user_edit_password', methods: ['GET', 'POST'])]
|
||||
public function editPassword(Request $request, UserPasswordHasherInterface $hasher, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
# Récupération de l'adhérent actuellement connecté
|
||||
$user = $this->getUser();
|
||||
|
||||
# Création du formulaire
|
||||
$form = $this->createForm(ChangePasswordType::class);
|
||||
|
||||
# Liaison du formulaire à la requête HTTP
|
||||
$form->handleRequest($request);
|
||||
|
||||
# Vérification du formulaire, s'il est bien soumis et valide
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
# Récupération des données du formulaire
|
||||
$user = $this->getUser();
|
||||
$plainOldPassword = $form->get('oldPassword')->getData();
|
||||
$newPassword = $form->get('newPassword')->getData();
|
||||
|
||||
# Vérification de l'ancien mot de passe
|
||||
if (!$hasher->isPasswordValid($user, $plainOldPassword)) {
|
||||
$form->get('oldPassword')->addError(new FormError('L\'ancien mot de passe est incorrect.'));
|
||||
} else {
|
||||
# Si tout est OK : Hachage du mot de passe
|
||||
$hashedPassword = $hasher->hashPassword($user, $newPassword);
|
||||
$user->setPassword($hashedPassword);
|
||||
|
||||
# Sauvegarde en BDD
|
||||
$entityManager->flush();
|
||||
|
||||
# Message de succès pour l'adhérent
|
||||
$this->addFlash('success', 'Votre mot de passe a bien été mis à jour !');
|
||||
|
||||
return $this->redirectToRoute('app_user_edit_password');
|
||||
}
|
||||
}
|
||||
return $this->render('user/edit_password.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -29,20 +29,15 @@ class AppFixtures extends Fixture
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
# Instanciation d'un nouvel utilisateur (Adhérent)
|
||||
$user = new User();
|
||||
|
||||
# Attribution d'un email aléatoire et unique
|
||||
$user->setEmail($faker->unique()->safeEmail());
|
||||
|
||||
# Définition des droits d'accès de l'utilisateur
|
||||
$user->setRoles(['ROLE_USER']);
|
||||
|
||||
# Hachage sécurisé du mot de passe "password"
|
||||
$user->setPassword($this->hasher->hashPassword($user, 'password'));
|
||||
|
||||
# Définition d'un NOM et Prénom
|
||||
$user->setFirstname($faker->firstName());
|
||||
$user->setLastname($faker->lastName());
|
||||
|
||||
# autres fixtures à modifier plus tard
|
||||
$user->setNextcloudQuota($faker->numberBetween(1, 20) . 'G');
|
||||
$user->setQuota($faker->numberBetween(1, 10) . 'G');
|
||||
@@ -52,11 +47,32 @@ class AppFixtures extends Fixture
|
||||
$user->setHasMobilizon($faker->boolean(50));
|
||||
$user->setHasNextcloudAccess($faker->boolean(90));
|
||||
$user->setIdentifiantKaz($faker->uuid());
|
||||
}
|
||||
|
||||
// Création d'un compte de test fixe
|
||||
$admin = new User();
|
||||
$admin->setEmail('admin@kaz.bzh');
|
||||
$admin->setRoles(['ROLE_USER', 'ROLE_ADMIN']);
|
||||
$admin->setPassword($this->hasher->hashPassword($admin, 'password'));
|
||||
$admin->setFirstName('Admin');
|
||||
$admin->setLastName('KAZ');
|
||||
|
||||
// Remplissage des champs obligatoires restants pour éviter les erreurs SQL
|
||||
$admin->setEmailDeSecours('secours@kaz.bzh');
|
||||
$admin->setIdentifiantKaz('ADMIN-KAZ-001');
|
||||
$admin->setQuota('5G');
|
||||
$admin->setEmailQuota('1G');
|
||||
$admin->setNextcloudQuota('10G');
|
||||
$admin->setHasNextcloudAccess(true);
|
||||
$admin->setHasMobilizon(true);
|
||||
$admin->setHasAgoraAccess(true);
|
||||
|
||||
$manager->persist($admin);
|
||||
|
||||
# Préparation de l'enregistrement de l'objet en base de données
|
||||
$manager->persist($user);
|
||||
|
||||
# Exécution réelle des requêtes SQL (envoi vers la base), une fois la bouche finie
|
||||
$manager->flush();
|
||||
}
|
||||
# Exécution réelle des requêtes SQL (envoi vers la base), une fois la bouche finie
|
||||
$manager->flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
@@ -11,6 +12,7 @@ use Symfony\Component\Uid\Uuid;
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
|
||||
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
@@ -35,7 +37,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
private ?string $password = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $emailQuota = null;
|
||||
private ?string $emailQuota = '1G';
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $emailDeSecours = null;
|
||||
@@ -50,7 +52,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
private ?bool $hasNextcloudAccess = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $nextcloudQuota = '1G';
|
||||
private ?string $nextcloudQuota = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $hasMobilizon = null;
|
||||
@@ -104,9 +106,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
// guarantee every user at least has ROLE_USER
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
$roles[] = 'ROLE_USER'; // garantit qu'il a au moins ce rôle
|
||||
return array_unique($roles);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
* Ajout de cette fonction, car obligatoire pour faire fonctionner UserInterface correctement
|
||||
*/
|
||||
public function eraseCredentials(): void
|
||||
{
|
||||
// Si vous stockez des données temporaires sensibles sur l'utilisateur, nettoyez-les ici
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PasswordAuthenticatedUserInterface
|
||||
*/
|
||||
|
||||
27
symfony.lock
27
symfony.lock
@@ -62,6 +62,21 @@
|
||||
"bin/phpunit"
|
||||
]
|
||||
},
|
||||
"symfony/asset-mapper": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.4",
|
||||
"ref": "5ad1308aa756d58f999ffbe1540d1189f5d7d14a"
|
||||
},
|
||||
"files": [
|
||||
"assets/app.js",
|
||||
"assets/styles/app.css",
|
||||
"config/packages/asset_mapper.yaml",
|
||||
"importmap.php"
|
||||
]
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
@@ -341,6 +356,18 @@
|
||||
"webpack.config.js"
|
||||
]
|
||||
},
|
||||
"symfonycasts/tailwind-bundle": {
|
||||
"version": "0.12",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "0.8",
|
||||
"ref": "d0bd0276f74de90adfaa4c6cd74cc0caacd77e0a"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/symfonycasts_tailwind.yaml"
|
||||
]
|
||||
},
|
||||
"twig/extra-bundle": {
|
||||
"version": "v3.23.0"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./templates/**/*.html.twig",
|
||||
"./assets/**/*.js",
|
||||
"./templates/**/*.html.twig",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {
|
||||
colors: {
|
||||
'brand-teal': '#4DD5C8', // Boutons
|
||||
'brand-gold': '#E6A638', // Accent
|
||||
'brand-dark': '#000000', // Police
|
||||
'bg-primary': '#F9FCF7', // Fond principal
|
||||
'bg-secondary': '#23978B', // Fond secondaire
|
||||
},
|
||||
fontFamily: {
|
||||
'sans': ['Sora', 'system-ui', 'sans-serif'],
|
||||
'title': ['Caveat', 'cursive'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
@@ -2,16 +2,30 @@
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||
<title>{% block title %}Association KAZ{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
|
||||
{% block stylesheets %}
|
||||
<link rel="stylesheet" href="/assets/styles/app.css">
|
||||
<link rel="stylesheet" href="{{ asset('styles/app.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="container mx-auto mt-4 px-4">
|
||||
{# Section des notifications #}
|
||||
{% for label, messages in app.flashes %}
|
||||
{% for message in messages %}
|
||||
<div class="p-4 mb-4 rounded-lg shadow-md border-l-4 {{ label == 'success' ? 'bg-brand-teal text-brand-dark border-bg-secondary' : 'bg-brand-gold text-brand-dark border-red-700' }}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% block body %}{% endblock %}
|
||||
</main>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Accueil | Tableau de bord KAZ{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="mt-8 p-6 bg-white rounded-lg shadow-md border border-gray-200">
|
||||
<h1 class="text-3xl font-bold mb-4">Bienvenue sur ton tableau de bord KAZ </h1>
|
||||
|
||||
{# Vérification si un utilisateur est connecté #}
|
||||
{% if app.user %}
|
||||
<div class="p-4 bg-green-100 text-green-800 rounded mb-4">
|
||||
<p class="font-semibold">Succès ! Tu es connecté avec l'adresse :</p>
|
||||
<p class="text-xl">{{ app.user.userIdentifier }}</p>
|
||||
</div>
|
||||
|
||||
<p class="mt-4">
|
||||
<a href="{{ path('app_logout') }}" class="inline-block px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700">
|
||||
Se déconnecter
|
||||
</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="mb-4">Tu n'es pas encore connecté à ton espace.</p>
|
||||
<a href="{{ path('app_login') }}" class="inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
Aller à la page de connexion
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,39 +1,34 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Log in!{% endblock %}
|
||||
{% block title %}Se connecter | {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form method="post">
|
||||
{% if error %}
|
||||
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 border border-red-200">
|
||||
Email ou mot de passe invalide.
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.user %}
|
||||
<div class="mb-3">
|
||||
You are logged in as {{ app.user.userIdentifier }}, <a href="{{ logout_path() }}">Se déconnecter</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if app.user %}
|
||||
<div class="mb-3">
|
||||
You are logged in as {{ app.user.userIdentifier }}, <a href="{{ logout_path() }}">Logout</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h1 class="h3 mb-3 font-weight-normal">Se connecter à mon compte KAZ</h1>
|
||||
<label for="username">Email</label>
|
||||
<input type="email" value="{{ last_username }}" name="_username" id="username" class="form-control" autocomplete="email" required autofocus>
|
||||
<label for="password">Mot de passe</label>
|
||||
<input type="password" name="_password" id="password" class="form-control" autocomplete="current-password" required>
|
||||
<input type="hidden" name="_csrf_token" data-controller="csrf-protection" value="{{ csrf_token('authenticate') }}">
|
||||
<h1 class="h3 mb-3 font-weight-normal">Se connecter à mon tableau de bord KAZ</h1>
|
||||
<label for="inputEmail">Email :</label>
|
||||
<input type="email" value="{{ last_username }}" name="_username" id="inputEmail" class="form-control" autocomplete="email" required autofocus>
|
||||
<label for="inputPassword">Mot de passe : </label>
|
||||
<input type="password" name="_password" id="inputPassword" class="form-control" autocomplete="current-password" required>
|
||||
<input type="hidden" name="_csrf_token" data-controller="csrf-protection" value="{{ csrf_token('authenticate') }}">
|
||||
|
||||
<div class="checkbox mb-3">
|
||||
<label>
|
||||
<input type="checkbox" name="_remember_me"> Se souvenir de moi
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{# TODO : vérifier qu'il y a bien une option "Se souvenir de moi" dans mon firewall pour activer la fonctionnalité. #}
|
||||
{# pour en savoir plus : https://symfony.com/doc/current/security/remember_me.html #}
|
||||
|
||||
<div class="checkbox mb-3">
|
||||
<input type="checkbox" name="_remember_me" id="_remember_me">
|
||||
<label for="_remember_me">Se souvenir de moi</label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-lg btn-primary" type="submit">
|
||||
Se connecter
|
||||
</button>
|
||||
</form>
|
||||
<button class="btn btn-lg btn-primary" type="submit">
|
||||
Se connecter
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user