diff --git a/composer.json b/composer.json index 7530ca805d9233919477a6f36cd1b549bde69d06..af9b71ba81cc8b8516180847f820f2c6584e4915 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "ext-iconv": "*", "doctrine/annotations": "^2.0", "doctrine/doctrine-bundle": "^2.9", + "doctrine/doctrine-fixtures-bundle": "^3.4", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.14", "phpdocumentor/reflection-docblock": "^5.3", diff --git a/composer.lock b/composer.lock index 6fc1361d86e528cf0c718c449d7877ffe6f86778..96732fe64a67c77b3aadc96c1df48bce4e84d429 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": "ea34e3d53cd3f7bdae486a8efc33e77e", + "content-hash": "188a59d3b18ac6d94fa9edc37fc5f533", "packages": [ { "name": "doctrine/annotations", @@ -352,6 +352,88 @@ ], "time": "2022-10-09T11:47:59+00:00" }, + { + "name": "doctrine/data-fixtures", + "version": "1.6.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "c27821d038e64f1bfc852a94064d65d2a75ad01f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/c27821d038e64f1bfc852a94064d65d2a75ad01f", + "reference": "c27821d038e64f1bfc852a94064d65d2a75ad01f", + "shasum": "" + }, + "require": { + "doctrine/persistence": "^1.3.3|^2.0|^3.0", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<2.13", + "doctrine/orm": "<2.12", + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "doctrine/coding-standard": "^10.0", + "doctrine/dbal": "^2.13 || ^3.0", + "doctrine/deprecations": "^1.0", + "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", + "doctrine/orm": "^2.12", + "ext-sqlite3": "*", + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/cache": "^5.0 || ^6.0", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "database" + ], + "support": { + "issues": "https://github.com/doctrine/data-fixtures/issues", + "source": "https://github.com/doctrine/data-fixtures/tree/1.6.3" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", + "type": "tidelift" + } + ], + "time": "2023-01-07T15:10:22+00:00" + }, { "name": "doctrine/dbal", "version": "3.6.1", @@ -623,6 +705,89 @@ ], "time": "2023-03-23T20:02:57+00:00" }, + { + "name": "doctrine/doctrine-fixtures-bundle", + "version": "3.4.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineFixturesBundle.git", + "reference": "601988c5b46dbd20a0f886f967210aba378a6fd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/601988c5b46dbd20a0f886f967210aba378a6fd5", + "reference": "601988c5b46dbd20a0f886f967210aba378a6fd5", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "^1.3", + "doctrine/doctrine-bundle": "^1.11|^2.0", + "doctrine/orm": "^2.6.0", + "doctrine/persistence": "^1.3.7|^2.0|^3.0", + "php": "^7.1 || ^8.0", + "symfony/config": "^3.4|^4.3|^5.0|^6.0", + "symfony/console": "^3.4|^4.3|^5.0|^6.0", + "symfony/dependency-injection": "^3.4.47|^4.3|^5.0|^6.0", + "symfony/doctrine-bridge": "^3.4|^4.1|^5.0|^6.0", + "symfony/http-kernel": "^3.4|^4.3|^5.0|^6.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "^1.4.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "symfony/phpunit-bridge": "^6.0.8", + "vimeo/psalm": "^4.22" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\FixturesBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DoctrineFixturesBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "Fixture", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineFixturesBundle/issues", + "source": "https://github.com/doctrine/DoctrineFixturesBundle/tree/3.4.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-fixtures-bundle", + "type": "tidelift" + } + ], + "time": "2022-04-28T17:58:29+00:00" + }, { "name": "doctrine/doctrine-migrations-bundle", "version": "3.2.2", diff --git a/config/bundles.php b/config/bundles.php index c1e923b86abc7f69e6b84511e01028b44f9b09f2..d0afd22c2099cfaeea7649be948fd1789dbecc0a 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -13,4 +13,5 @@ return [ Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], + Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], ]; diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 789a9ac141ba2c9f6ece6369e836db4fa2fa8884..5f130df226bf8c65c400fe275fcc9da25dd32a85 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -5,14 +5,18 @@ 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: username firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true - provider: users_in_memory + provider: app_user_provider # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall diff --git a/migrations/Version20230401090258.php b/migrations/Version20230401090258.php new file mode 100644 index 0000000000000000000000000000000000000000..02783f646f964fe35f0e17aed5ca3783e72b9754 --- /dev/null +++ b/migrations/Version20230401090258.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +namespace DoctrineMigrations; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\Migrations\AbstractMigration; + +/** + * Auto-generated Migration: Please modify to your needs! + */ +final class Version20230401090258 extends AbstractMigration +{ + public function getDescription(): string + { + return ''; + } + + public function up(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(180) NOT NULL, roles LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), 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'); + } +} diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php new file mode 100644 index 0000000000000000000000000000000000000000..987f6fe955c778c958d11e0929d0f2526f821bb1 --- /dev/null +++ b/src/DataFixtures/AppFixtures.php @@ -0,0 +1,17 @@ +<?php + +namespace App\DataFixtures; + +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Persistence\ObjectManager; + +class AppFixtures extends Fixture +{ + public function load(ObjectManager $manager): void + { + // $product = new Product(); + // $manager->persist($product); + + $manager->flush(); + } +} diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php new file mode 100644 index 0000000000000000000000000000000000000000..c49a16240e040c82a56d69a7fd3110042b9145bc --- /dev/null +++ b/src/DataFixtures/UserFixtures.php @@ -0,0 +1,36 @@ +<?php + +namespace App\DataFixtures; + +use App\Entity\User; +use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Persistence\ObjectManager; + +class UserFixtures extends Fixture { + + public function __construct(UserPasswordEncoderInterface $passwordEncoder) + { + $this->passwordEncoder = $passwordEncoder; + } + + public function load(ObjectManager $manager) { + // Exemples de données à insérer dans la base de données + $user_list = [ + ['username' => 'admin', 'passwd' => '123', 'roles' => ['ROLE_USER', 'ROLE_ADMIN']], + ['username' => 'david', 'passwd' => '456', 'roles' => ['ROLE_USER']], + ]; + // Boucle pour chaque ligne + foreach ($user_list as $user_data) { + // Crée une nouvelle entité + $user = new User(); + // Donne des valeurs à ses attributs + $user->setUsername($user_data['username']); + $user->setPassword($this->passwordEncoder->encodePassword($user, $user_data['passwd'])); + $user->setRoles($user_data['roles']); + // Enregistre dans la BDD (INSERT) + $manager->persist($user); + } + $manager->flush(); + } +} \ No newline at end of file diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000000000000000000000000000000000000..0a0a505e9ba1f7eb9cae9fdb84be3bfdefb6252b --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,113 @@ +<?php + +namespace App\Entity; + +use App\Repository\UserRepository; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Security\Core\User\UserInterface; + +#[ORM\Entity(repositoryClass: UserRepository::class)] +class User implements UserInterface, PasswordAuthenticatedUserInterface +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + private ?int $id = null; + + #[ORM\Column(length: 180, unique: true)] + private ?string $username = null; + + #[ORM\Column] + private array $roles = []; + + /** + * @var string The hashed password + */ + #[ORM\Column] + private ?string $password = null; + + public function getId(): ?int + { + return $this->id; + } + + /** + * @deprecated since Symfony 5.3, use getUserIdentifier instead + */ + public function getUsername(): string + { + return (string) $this->username; + } + + public function setUsername(string $username): self + { + $this->username = $username; + + return $this; + } + + /** + * A visual identifier that represents this user. + * + * @see UserInterface + */ + public function getUserIdentifier(): string + { + return (string) $this->username; + } + + /** + * @see UserInterface + */ + public function getRoles(): array + { + $roles = $this->roles; + // guarantee every user at least has ROLE_USER + $roles[] = 'ROLE_USER'; + + return array_unique($roles); + } + + public function setRoles(array $roles): self + { + $this->roles = $roles; + + return $this; + } + + /** + * @see PasswordAuthenticatedUserInterface + */ + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): self + { + $this->password = $password; + + return $this; + } + + /** + * Returning a salt is only needed, if you are not using a modern + * hashing algorithm (e.g. bcrypt or sodium) in your security.yaml. + * + * @see UserInterface + */ + public function getSalt(): ?string + { + return null; + } + + /** + * @see UserInterface + */ + public function eraseCredentials() + { + // If you store any temporary, sensitive data on the user, clear it here + // $this->plainPassword = null; + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000000000000000000000000000000000000..9b60483f8a98d48e903a38c962766de7bd6d350d --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,83 @@ +<?php + +namespace App\Repository; + +use App\Entity\User; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; + +/** + * @extends ServiceEntityRepository<User> + * + * @method User|null find($id, $lockMode = null, $lockVersion = null) + * @method User|null findOneBy(array $criteria, array $orderBy = null) + * @method User[] findAll() + * @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, User::class); + } + + public function save(User $entity, bool $flush = false): void + { + $this->getEntityManager()->persist($entity); + + if ($flush) { + $this->getEntityManager()->flush(); + } + } + + public function remove(User $entity, bool $flush = false): void + { + $this->getEntityManager()->remove($entity); + + if ($flush) { + $this->getEntityManager()->flush(); + } + } + + /** + * 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.', \get_class($user))); + } + + $user->setPassword($newHashedPassword); + + $this->save($user, true); + } + +// /** +// * @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 4e7049c14738d804c9c6b1603fd7d12be96bf768..aad1b5bacd208e06007c749e97aa06ccda26117f 100644 --- a/symfony.lock +++ b/symfony.lock @@ -25,6 +25,18 @@ "./src/Repository/.gitignore" ] }, + "doctrine/doctrine-fixtures-bundle": { + "version": "3.4", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.0", + "ref": "1f5514cfa15b947298df4d771e694e578d4c204d" + }, + "files": [ + "./src/DataFixtures/AppFixtures.php" + ] + }, "doctrine/doctrine-migrations-bundle": { "version": "3.2", "recipe": {