From 1377fde1e1833e25e88cbc14af26353e552c8541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melaine=20G=C3=A9rard?= Date: Sat, 28 Dec 2024 23:55:26 +0100 Subject: [PATCH] =?UTF-8?q?:sparkles:=20Impl=C3=A9mentation=20auth=20basiq?= =?UTF-8?q?ue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 4 +- compose.override.yaml | 18 - compose.yaml | 25 -- composer.json | 3 + composer.lock | 387 +++++++++++++++++++++- config/bundles.php | 2 + config/packages/framework.yaml | 4 +- config/packages/security.yaml | 16 +- config/packages/translation.yaml | 3 +- config/packages/twig.yaml | 2 + docker-compose.yml | 15 + importmap.php | 8 +- migrations/Version20241228222158.php | 33 ++ renovate.json | 78 +++++ src/Controller/DashboardController.php | 9 +- src/Controller/HomeController.php | 18 + src/Controller/RegistrationController.php | 46 +++ src/Controller/SecurityController.php | 35 ++ src/Entity/User.php | 148 +++++++++ src/Form/RegistrationFormType.php | 54 +++ src/Repository/UserRepository.php | 60 ++++ symfony.lock | 27 ++ tailwind.config.js | 1 + templates/base.html.twig | 2 +- templates/home/index.html.twig | 20 ++ templates/partials/breadbrumb.html.twig | 2 +- templates/partials/navbar.html.twig | 26 +- templates/registration/register.html.twig | 24 ++ templates/security/login.html.twig | 36 ++ 29 files changed, 1037 insertions(+), 69 deletions(-) delete mode 100644 compose.override.yaml delete mode 100644 compose.yaml create mode 100644 docker-compose.yml create mode 100644 migrations/Version20241228222158.php create mode 100644 renovate.json create mode 100644 src/Controller/HomeController.php create mode 100644 src/Controller/RegistrationController.php create mode 100644 src/Controller/SecurityController.php create mode 100644 src/Entity/User.php create mode 100644 src/Form/RegistrationFormType.php create mode 100644 src/Repository/UserRepository.php create mode 100644 templates/home/index.html.twig create mode 100644 templates/registration/register.html.twig create mode 100644 templates/security/login.html.twig diff --git a/.env b/.env index 32ab3c6..ac80863 100644 --- a/.env +++ b/.env @@ -24,9 +24,9 @@ APP_SECRET= # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" -# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" +DATABASE_URL="mysql://kumora:kumora@127.0.0.1:3309/kumora?serverVersion=8.0.32&charset=utf8mb4" # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" -DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" +# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" ###< doctrine/doctrine-bundle ### ###> symfony/messenger ### diff --git a/compose.override.yaml b/compose.override.yaml deleted file mode 100644 index 8dc54de..0000000 --- a/compose.override.yaml +++ /dev/null @@ -1,18 +0,0 @@ - -services: -###> doctrine/doctrine-bundle ### - database: - ports: - - "5432" -###< doctrine/doctrine-bundle ### - -###> symfony/mailer ### - mailer: - image: axllent/mailpit - ports: - - "1025" - - "8025" - environment: - MP_SMTP_AUTH_ACCEPT_ANY: 1 - MP_SMTP_AUTH_ALLOW_INSECURE: 1 -###< symfony/mailer ### diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index 89c74d1..0000000 --- a/compose.yaml +++ /dev/null @@ -1,25 +0,0 @@ - -services: -###> doctrine/doctrine-bundle ### - database: - image: postgres:${POSTGRES_VERSION:-16}-alpine - environment: - POSTGRES_DB: ${POSTGRES_DB:-app} - # You should definitely change the password in production - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} - POSTGRES_USER: ${POSTGRES_USER:-app} - healthcheck: - test: ["CMD", "pg_isready", "-d", "${POSTGRES_DB:-app}", "-U", "${POSTGRES_USER:-app}"] - timeout: 5s - retries: 5 - start_period: 60s - volumes: - - database_data:/var/lib/postgresql/data:rw - # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! - # - ./docker/db/data:/var/lib/postgresql/data:rw -###< doctrine/doctrine-bundle ### - -volumes: -###> doctrine/doctrine-bundle ### - database_data: -###< doctrine/doctrine-bundle ### diff --git a/composer.json b/composer.json index b92ad34..5b91cfd 100644 --- a/composer.json +++ b/composer.json @@ -35,11 +35,13 @@ "symfony/property-info": "7.2.*", "symfony/runtime": "7.2.*", "symfony/security-bundle": "7.2.*", + "symfony/security-csrf": "7.2.*", "symfony/serializer": "7.2.*", "symfony/stimulus-bundle": "^2.22", "symfony/string": "7.2.*", "symfony/translation": "7.2.*", "symfony/twig-bundle": "7.2.*", + "symfony/uid": "7.2.*", "symfony/ux-icons": "^2.22", "symfony/ux-turbo": "^2.22", "symfony/ux-twig-component": "^2.22", @@ -47,6 +49,7 @@ "symfony/web-link": "7.2.*", "symfony/yaml": "7.2.*", "symfonycasts/tailwind-bundle": "^0.6.1", + "tales-from-a-dev/flowbite-bundle": "^0.7.1", "twig/extra-bundle": "^2.12|^3.0", "twig/twig": "^2.12|^3.0" }, diff --git a/composer.lock b/composer.lock index 8cc464b..078bea5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b7708b5019145d17ad3512f8c441f6bc", + "content-hash": "e0cd8ceeb323f7d4ce2c5aceb5d7eb3d", "packages": [ { "name": "composer/semver", @@ -1377,6 +1377,73 @@ ], "time": "2024-12-27T00:36:43+00:00" }, + { + "name": "gehrisandro/tailwind-merge-php", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/gehrisandro/tailwind-merge-php.git", + "reference": "dc11b9d4a625dd5be885900e5ef14c3efa260277" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/gehrisandro/tailwind-merge-php/zipball/dc11b9d4a625dd5be885900e5ef14c3efa260277", + "reference": "dc11b9d4a625dd5be885900e5ef14c3efa260277", + "shasum": "" + }, + "require": { + "php": "^8.1.0", + "psr/simple-cache": "^3.0" + }, + "require-dev": { + "laravel/pint": "^1.13.8", + "nunomaduro/collision": "^7.10", + "pestphp/pest": "^v2.24.0", + "pestphp/pest-plugin-arch": "^2.6", + "pestphp/pest-plugin-mock": "^2.0.0", + "pestphp/pest-plugin-type-coverage": "^2.8", + "phpstan/phpstan": "^1.10.55", + "rector/rector": "^1.0.5", + "symfony/var-dumper": "^6.4.2" + }, + "type": "library", + "autoload": { + "files": [ + "src/TailwindMerge.php" + ], + "psr-4": { + "TailwindMerge\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "TailwindMerge for PHP merges multiple Tailwind CSS classes by automatically resolving conflicts between them", + "keywords": [ + "classes", + "merge", + "php", + "tailwindcss" + ], + "support": { + "issues": "https://github.com/gehrisandro/tailwind-merge-php/issues", + "source": "https://github.com/gehrisandro/tailwind-merge-php/tree/v1.1.2" + }, + "funding": [ + { + "url": "https://github.com/gehrisandro", + "type": "github" + } + ], + "time": "2024-05-21T17:32:42+00:00" + }, { "name": "league/flysystem", "version": "3.29.1", @@ -2285,6 +2352,57 @@ }, "time": "2024-09-11T13:17:53+00:00" }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, { "name": "symfony/asset", "version": "v7.2.0", @@ -5551,6 +5669,85 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/process", "version": "v7.2.0", @@ -7123,6 +7320,80 @@ ], "time": "2024-12-11T07:49:41+00:00" }, + { + "name": "symfony/uid", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, { "name": "symfony/ux-icons", "version": "v2.22.1", @@ -7859,6 +8130,120 @@ }, "time": "2024-11-06T18:41:22+00:00" }, + { + "name": "tales-from-a-dev/flowbite-bundle", + "version": "v0.7.1", + "source": { + "type": "git", + "url": "https://github.com/tales-from-a-dev/flowbite-bundle.git", + "reference": "cfc2fbebba6989f0d8b5cc00fdc95b7f3edabe2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tales-from-a-dev/flowbite-bundle/zipball/cfc2fbebba6989f0d8b5cc00fdc95b7f3edabe2f", + "reference": "cfc2fbebba6989f0d8b5cc00fdc95b7f3edabe2f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/twig-bridge": "^6.4 || ^7.0", + "tales-from-a-dev/twig-tailwind-extra": "^0.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.15", + "phpunit/phpunit": "^10.0", + "symfony/form": "^6.4 || ^7.0", + "symfony/intl": "^6.4 || ^7.0", + "symfony/security-csrf": "^6.4 || ^7.0", + "symfony/translation": "^6.4 || ^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "TalesFromADev\\FlowbiteBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Monteil", + "email": "monteil.romain@gmail.com" + } + ], + "description": "A Symfony form theme for Flowbite", + "homepage": "https://github.com/tales-from-a-dev/flowbite-bundle", + "keywords": [ + "bundle", + "flowbite", + "form", + "symfony", + "theme" + ], + "support": { + "issues": "https://github.com/tales-from-a-dev/flowbite-bundle/issues", + "source": "https://github.com/tales-from-a-dev/flowbite-bundle/tree/v0.7.1" + }, + "time": "2024-12-11T13:52:04+00:00" + }, + { + "name": "tales-from-a-dev/twig-tailwind-extra", + "version": "v0.3.0", + "source": { + "type": "git", + "url": "https://github.com/tales-from-a-dev/twig-tailwind-extra.git", + "reference": "a3cb86414dd5810740cf91966bc1cf10047ce8ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tales-from-a-dev/twig-tailwind-extra/zipball/a3cb86414dd5810740cf91966bc1cf10047ce8ef", + "reference": "a3cb86414dd5810740cf91966bc1cf10047ce8ef", + "shasum": "" + }, + "require": { + "gehrisandro/tailwind-merge-php": "^1.0", + "php": ">=8.2", + "symfony/cache": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.38", + "symfony/phpunit-bridge": "^6.4 || ^7.0" + }, + "type": "twig", + "autoload": { + "psr-4": { + "TalesFromADev\\Twig\\Extra\\Tailwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Monteil", + "email": "monteil.romain@gmail.com" + } + ], + "description": "A Twig extension for Tailwind", + "homepage": "https://github.com/tales-from-a-dev/twig-tailwind-extra", + "keywords": [ + "extension", + "symfony", + "tailwind", + "twig" + ], + "support": { + "issues": "https://github.com/tales-from-a-dev/twig-tailwind-extra/issues", + "source": "https://github.com/tales-from-a-dev/twig-tailwind-extra/tree/v0.3.0" + }, + "time": "2024-08-07T23:27:08+00:00" + }, { "name": "twig/extra-bundle", "version": "v3.17.0", diff --git a/config/bundles.php b/config/bundles.php index c5154d6..3177489 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -17,4 +17,6 @@ return [ Oneup\FlysystemBundle\OneupFlysystemBundle::class => ['all' => true], Symfony\UX\Icons\UXIconsBundle::class => ['all' => true], Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true], + TalesFromADev\Twig\Extra\Tailwind\Bridge\Symfony\Bundle\TalesFromADevTwigExtraTailwindBundle::class => ['all' => true], + TalesFromADev\FlowbiteBundle\TalesFromADevFlowbiteBundle::class => ['all' => true], ]; diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 7e1ee1f..7ba8b4e 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -1,7 +1,9 @@ # see https://symfony.com/doc/current/reference/configuration/framework.html framework: secret: '%env(APP_SECRET)%' - + form: { csrf_protection: { token_id: 'submit' } } + csrf_protection: + stateless_token_ids: ['submit', 'authenticate', 'logout'] # Note that the session will be started ONLY if you read or write from it. session: true diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 367af25..405c27d 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -4,14 +4,26 @@ security: 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 } + # used to reload user from session & other features (e.g. switch_user) + app_user_provider: + entity: + class: App\Entity\User + property: email firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true - provider: users_in_memory + provider: app_user_provider + form_login: + login_path: app_login + check_path: app_login + enable_csrf: true + logout: + path: app_logout + # where to redirect after logout + # target: app_any_route # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml index b3f8f9c..590bec1 100644 --- a/config/packages/translation.yaml +++ b/config/packages/translation.yaml @@ -1,7 +1,8 @@ framework: - default_locale: en + default_locale: fr translator: default_path: '%kernel.project_dir%/translations' fallbacks: + - fr - en providers: diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 3f795d9..d7a8397 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -1,5 +1,7 @@ twig: file_name_pattern: '*.twig' + form_themes: + - '@TalesFromADevFlowbite/form/default.html.twig' when@test: twig: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e00d3ec --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + mysql: + image: mysql:8.4 + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: kumora + MYSQL_USER: kumora + MYSQL_PASSWORD: kumora + ports: + - "3309:3306" + volumes: + - mysql_datas_kumora:/var/lib/mysql + +volumes: + mysql_datas_kumora: \ No newline at end of file diff --git a/importmap.php b/importmap.php index b73b323..179072e 100644 --- a/importmap.php +++ b/importmap.php @@ -16,13 +16,13 @@ return [ '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/stimulus' => [ + 'version' => '3.2.2', + ], '@hotwired/turbo' => [ - 'version' => '7.3.0', + 'version' => '8.0.12', ], ]; diff --git a/migrations/Version20241228222158.php b/migrations/Version20241228222158.php new file mode 100644 index 0000000..633afd4 --- /dev/null +++ b/migrations/Version20241228222158.php @@ -0,0 +1,33 @@ +addSql('CREATE TABLE `user` (id BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid)\', roles JSON NOT NULL, password VARCHAR(255) NOT NULL, username VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, approuved TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_IDENTIFIER_USERNAME (username), UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', available_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', delivered_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE `user`'); + $this->addSql('DROP TABLE messenger_messages'); + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..d6e0178 --- /dev/null +++ b/renovate.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":automergeDisabled", + ":dependencyDashboard", + ":disableRateLimiting", + "docker:pinDigests", + ":prConcurrentLimitNone" + ], + "reviewers": [ + "@skitounet" + ], + "customManagers": [ + { + "customType": "regex", + "fileMatch": [ + "\\.yaml$" + ], + "matchStrings": [ + "# renovate: datasource=(?[^:]+?) depName=(?.+?)( versioning=(?.+?))?( extractVersion=(?.+?))?( registryUrl=(?.+?))?\\s.+?[:=]\\s*[\"']?(?.+?)[\"']?\\s" + ], + "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver-coerced{{/if}}", + "extractVersionTemplate": "{{#if extractVersion}}{{{extractVersion}}}{{else}}^v?(?.+)${{/if}}" + } + ], + "ignorePaths": [ + "public/**" + ], + "packageRules": [ + { + "matchManagers": ["composer"], + "matchDepTypes": ["require-dev"], + "addLabels": ["php-dev", "automerge"], + "groupName": "php-dev", + "automerge": true + }, + { + "matchManagers": ["composer"], + "matchDepTypes": ["require"], + "matchUpdateTypes": ["minor", "patch", "digest", "pin", "pinDigest"], + "addLabels": ["php-mineur", "automerge"], + "groupName": "php-mineur", + "automerge": true + }, + { + "matchManagers": ["composer"], + "matchDepTypes": ["require"], + "matchUpdateTypes": ["major"], + "addLabels": ["php-majeur"], + "groupName": "php-majeur", + "automerge": false + }, + { + "matchManagers": ["npm"], + "matchDepTypes": ["devDependencies"], + "addLabels": ["node-dev", "automerge"], + "groupName": "node-dev", + "automerge": true + }, + { + "matchManagers": ["npm"], + "matchDepTypes": ["dependencies"], + "matchUpdateTypes": ["minor", "patch", "digest", "pin", "pinDigest"], + "addLabels": ["node-mineur", "automerge"], + "groupName": "node-mineur", + "automerge": true + }, + { + "matchManagers": ["npm"], + "matchDepTypes": ["dependencies"], + "matchUpdateTypes": ["major"], + "addLabels": ["node-majeur"], + "groupName": "node-majeur", + "automerge": false + } + ] +} diff --git a/src/Controller/DashboardController.php b/src/Controller/DashboardController.php index ff6a1ef..d18322f 100644 --- a/src/Controller/DashboardController.php +++ b/src/Controller/DashboardController.php @@ -12,13 +12,16 @@ use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\HttpFoundation\HeaderUtils; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[Route('/files', 'app_files_')] +#[IsGranted('ROLE_USER')] class DashboardController extends AbstractController { /** * @throws FilesystemException */ - #[Route('/', name: 'app_dashboard')] + #[Route('/', name: 'index')] public function index(Filesystem $defaultAdapter, UrlGeneratorInterface $urlGenerator, #[MapQueryParameter('path')] string $path = ''): Response { // On retire les slashs en début et fin de chaîne @@ -44,8 +47,8 @@ class DashboardController extends AbstractController 'last_modified' => $file['lastModified'], 'size' => $file['fileSize'] ?? null, 'url' => $file['type'] === 'file' - ? $this->generateUrl('app_file_proxy', ['filename' => $file['path']], UrlGeneratorInterface::ABSOLUTE_URL) - : $this->generateUrl('app_dashboard', ['path' => $path . '/' . $file['path']]), + ? $this->generateUrl('app_files_app_file_proxy', ['filename' => $file['path']], UrlGeneratorInterface::ABSOLUTE_URL) + : $this->generateUrl('app_files_index', ['path' => $path . '/' . $file['path']]), ]; } } diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php new file mode 100644 index 0000000..a6eeda2 --- /dev/null +++ b/src/Controller/HomeController.php @@ -0,0 +1,18 @@ +render('home/index.html.twig', [ + 'controller_name' => 'HomeController', + ]); + } +} diff --git a/src/Controller/RegistrationController.php b/src/Controller/RegistrationController.php new file mode 100644 index 0000000..e3e9cd6 --- /dev/null +++ b/src/Controller/RegistrationController.php @@ -0,0 +1,46 @@ +isGranted('IS_AUTHENTICATED_FULLY')) { + return $this->redirectToRoute('app_home'); + } + $user = new User(); + $form = $this->createForm(RegistrationFormType::class, $user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + /** @var string $plainPassword */ + $plainPassword = $form->get('plainPassword')->getData(); + + // encode the plain password + $user->setPassword($userPasswordHasher->hashPassword($user, $plainPassword)); + + $entityManager->persist($user); + $entityManager->flush(); + + $this->addFlash('success', 'Votre compte a été créé avec succès. Vous recevrez un email de confirmation une fois que votre compte aura été approuvé.'); + + return $this->redirectToRoute('app_login'); + } + + return $this->render('registration/register.html.twig', [ + 'registrationForm' => $form, + ]); + } +} diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..c4624b4 --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,35 @@ +isGranted('IS_AUTHENTICATED_FULLY')) { + return $this->redirectToRoute('app_home'); + } + // get the login error if there is one + $error = $authenticationUtils->getLastAuthenticationError(); + + // last username entered by the user + $lastUsername = $authenticationUtils->getLastUsername(); + + return $this->render('security/login.html.twig', [ + 'last_username' => $lastUsername, + 'error' => $error, + ]); + } + + #[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.'); + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000..44cbb9b --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,148 @@ + The user roles + */ + #[ORM\Column] + private array $roles = []; + + /** + * @var string The hashed password + */ + #[ORM\Column] + private ?string $password = null; + + #[ORM\Column(length: 255)] + private ?string $username = null; + + #[ORM\Column(length: 255)] + private ?string $email = null; + + #[ORM\Column] + private ?bool $approuved = false; + + public function __construct() + { + $this->id = Uuid::v4(); + } + + public function getId(): ?Uuid + { + return $this->id; + } + + /** + * A visual identifier that represents this user. + * + * @see UserInterface + */ + public function getUserIdentifier(): string + { + return (string) $this->email; + } + + /** + * @see UserInterface + * + * @return list + */ + public function getRoles(): array + { + $roles = $this->roles; + // guarantee every user at least has ROLE_USER + $roles[] = 'ROLE_USER'; + + return array_unique($roles); + } + + /** + * @param list $roles + */ + public function setRoles(array $roles): static + { + $this->roles = $roles; + + return $this; + } + + /** + * @see PasswordAuthenticatedUserInterface + */ + public function getPassword(): ?string + { + return $this->password; + } + + public function setPassword(string $password): static + { + $this->password = $password; + + return $this; + } + + /** + * @see UserInterface + */ + public function eraseCredentials(): void + { + // If you store any temporary, sensitive data on the user, clear it here + // $this->plainPassword = null; + } + + public function getUsername(): ?string + { + return $this->username; + } + + public function setUsername(string $username): static + { + $this->username = $username; + + return $this; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): static + { + $this->email = $email; + + return $this; + } + + public function isApprouved(): ?bool + { + return $this->approuved; + } + + public function setApprouved(bool $approuved): static + { + $this->approuved = $approuved; + + return $this; + } +} diff --git a/src/Form/RegistrationFormType.php b/src/Form/RegistrationFormType.php new file mode 100644 index 0000000..be5e7b0 --- /dev/null +++ b/src/Form/RegistrationFormType.php @@ -0,0 +1,54 @@ +add('username') + ->add('email') + ->add('plainPassword', PasswordType::class, [ + 'mapped' => false, + 'attr' => ['autocomplete' => 'new-password'], + 'constraints' => [ + new NotBlank([ + 'message' => 'Please enter a password', + ]), + new Length([ + 'min' => 6, + 'minMessage' => 'Your password should be at least {{ limit }} characters', + // max length allowed by Symfony for security reasons + 'max' => 4096, + ]), + ], + ]) + ->add('agreeTerms', CheckboxType::class, [ + 'mapped' => false, + 'constraints' => [ + new IsTrue([ + 'message' => 'Vous devez accepter les conditions d\'utilisation.', + ]), + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..4f2804e --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,60 @@ + + */ +class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, User::class); + } + + /** + * Used to upgrade (rehash) the user's password automatically over time. + */ + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void + { + if (!$user instanceof User) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', $user::class)); + } + + $user->setPassword($newHashedPassword); + $this->getEntityManager()->persist($user); + $this->getEntityManager()->flush(); + } + + // /** + // * @return User[] Returns an array of User objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('u') + // ->andWhere('u.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('u.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?User + // { + // return $this->createQueryBuilder('u') + // ->andWhere('u.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/symfony.lock b/symfony.lock index 495ad26..584596b 100644 --- a/symfony.lock +++ b/symfony.lock @@ -274,6 +274,15 @@ "templates/base.html.twig" ] }, + "symfony/uid": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "7.0", + "ref": "0df5844274d871b37fc3816c57a768ffc60a43a5" + } + }, "symfony/ux-icons": { "version": "2.22", "recipe": { @@ -335,6 +344,24 @@ "symfonycasts/tailwind-bundle": { "version": "v0.6.1" }, + "tales-from-a-dev/flowbite-bundle": { + "version": "0.7", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "0.4", + "ref": "8c5eef17730535682128557a1016872fd3e81c33" + } + }, + "tales-from-a-dev/twig-tailwind-extra": { + "version": "0.3", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "0.2", + "ref": "7243ab070ed66198eb82c026684e9b9773e7b64a" + } + }, "twig/extra-bundle": { "version": "v3.17.0" } diff --git a/tailwind.config.js b/tailwind.config.js index bb226fb..4d92bc8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,7 @@ /** @type {import('tailwindcss').Config} */ module.exports = { content: [ + "./vendor/tales-from-a-dev/flowbite-bundle/templates/**/*.html.twig", "./assets/**/*.js", "./templates/**/*.html.twig", ], diff --git a/templates/base.html.twig b/templates/base.html.twig index 063c794..bd350d6 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -11,7 +11,7 @@ {% block importmap %}{{ importmap('app') }}{% endblock %} {% endblock %} - + {% include "partials/navbar.html.twig" %} {% block body %}{% endblock %} diff --git a/templates/home/index.html.twig b/templates/home/index.html.twig new file mode 100644 index 0000000..73e04b2 --- /dev/null +++ b/templates/home/index.html.twig @@ -0,0 +1,20 @@ +{% extends 'base.html.twig' %} + +{% block title %}Hello HomeController!{% endblock %} + +{% block body %} + + +
+

Hello {{ controller_name }}! ✅

+ + This friendly message is coming from: +
    +
  • Your controller at /home/melaine/Coding/Camélia-Studio/Kumora/src/Controller/HomeController.php
  • +
  • Your template at /home/melaine/Coding/Camélia-Studio/Kumora/templates/home/index.html.twig
  • +
+
+{% endblock %} diff --git a/templates/partials/breadbrumb.html.twig b/templates/partials/breadbrumb.html.twig index ea7bb0a..39fb689 100644 --- a/templates/partials/breadbrumb.html.twig +++ b/templates/partials/breadbrumb.html.twig @@ -1,7 +1,7 @@