From a30313b45e8f54e39be5380ccb3f73b1781272f0 Mon Sep 17 00:00:00 2001 From: Sebastien Curt <curt.sebastien@gmail.com> Date: Tue, 23 Nov 2021 13:32:32 +0100 Subject: [PATCH] Add View and controllers for user registration --- composer.json | 4 +- composer.lock | 1474 ++++------------- config/bundles.php | 2 + config/packages/security.yaml | 5 + config/routes.yaml | 2 + config/services.yaml | 5 + src/Controller/RegistrationController.php | 101 ++ src/Controller/SecurityController.php | 36 + src/Form/RegistrationFormType.php | 88 + src/Security/AppCustomAuthenticator.php | 62 + src/Security/EmailVerifier.php | 57 + symfony.lock | 21 +- templates/base.html.twig | 4 +- .../registration/confirmation_email.html.twig | 11 + templates/registration/register.html.twig | 36 + templates/security/login.html.twig | 42 + 16 files changed, 754 insertions(+), 1196 deletions(-) create mode 100644 src/Controller/RegistrationController.php create mode 100644 src/Controller/SecurityController.php create mode 100644 src/Form/RegistrationFormType.php create mode 100644 src/Security/AppCustomAuthenticator.php create mode 100644 src/Security/EmailVerifier.php create mode 100644 templates/registration/confirmation_email.html.twig create mode 100644 templates/registration/register.html.twig create mode 100644 templates/security/login.html.twig diff --git a/composer.json b/composer.json index 2c2dbb9..f56b42d 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=7.2.5", + "php": ">=7.4", "ext-ctype": "*", "ext-iconv": "*", "composer/package-versions-deprecated": "1.11.99.4", @@ -12,6 +12,7 @@ "doctrine/doctrine-bundle": "^2.4", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.10", + "gregwar/captcha-bundle": "^2.1", "phpdocumentor/reflection-docblock": "^5.3", "sensio/framework-extra-bundle": "^6.1", "symfony/asset": "5.3.*", @@ -33,6 +34,7 @@ "symfony/proxy-manager-bridge": "5.3.*", "symfony/runtime": "5.3.*", "symfony/security-bundle": "5.3.*", + "symfony/security-csrf": "5.3.*", "symfony/serializer": "5.3.*", "symfony/string": "5.3.*", "symfony/translation": "5.3.*", diff --git a/composer.lock b/composer.lock index d5e956c..d18beb2 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": "32414ef45f243ae1f152f3cbbb2ac376", + "content-hash": "e79713ea98e230cc6acefeabf296273d", "packages": [ { "name": "composer/package-versions-deprecated", @@ -1587,6 +1587,128 @@ ], "time": "2021-05-22T16:11:15+00:00" }, + { + "name": "gregwar/captcha", + "version": "v1.1.9", + "source": { + "type": "git", + "url": "https://github.com/Gregwar/Captcha.git", + "reference": "4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gregwar/Captcha/zipball/4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5", + "reference": "4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-mbstring": "*", + "php": ">=5.3.0", + "symfony/finder": "*" + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + }, + "type": "captcha", + "autoload": { + "psr-4": { + "Gregwar\\": "src/Gregwar" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "description": "Captcha generator", + "homepage": "https://github.com/Gregwar/Captcha", + "keywords": [ + "bot", + "captcha", + "spam" + ], + "support": { + "issues": "https://github.com/Gregwar/Captcha/issues", + "source": "https://github.com/Gregwar/Captcha/tree/master" + }, + "time": "2020-03-24T14:39:05+00:00" + }, + { + "name": "gregwar/captcha-bundle", + "version": "v2.1.5", + "source": { + "type": "git", + "url": "https://github.com/Gregwar/CaptchaBundle.git", + "reference": "2f96c759abae9c914856b9b9fc909268ba98a625" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gregwar/CaptchaBundle/zipball/2f96c759abae9c914856b9b9fc909268ba98a625", + "reference": "2f96c759abae9c914856b9b9fc909268ba98a625", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "gregwar/captcha": "^1.1.9", + "php": ">=7.1.3", + "symfony/form": "~4.0|~5.0", + "symfony/framework-bundle": "~4.0|~5.0", + "symfony/translation": "~4.0|^5.0", + "twig/twig": "^2.10|^3.0" + }, + "require-dev": { + "symplify/easy-coding-standard": "^6.1" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Gregwar\\CaptchaBundle\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "description": "Captcha bundle", + "homepage": "https://github.com/Gregwar/CaptchaBundle", + "keywords": [ + "Symfony2", + "bot", + "captcha", + "code", + "security", + "spam", + "symfony", + "visual" + ], + "support": { + "issues": "https://github.com/Gregwar/CaptchaBundle/issues", + "source": "https://github.com/Gregwar/CaptchaBundle/tree/v2.1.5" + }, + "time": "2021-04-20T07:20:37+00:00" + }, { "name": "laminas/laminas-code", "version": "4.4.3", @@ -7433,6 +7555,55 @@ ], "time": "2021-07-29T06:20:01+00:00" }, + { + "name": "symfonycasts/verify-email-bundle", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/SymfonyCasts/verify-email-bundle.git", + "reference": "55487a5e55ab67ad77a84eefd1c92b0fab47e6ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SymfonyCasts/verify-email-bundle/zipball/55487a5e55ab67ad77a84eefd1c92b0fab47e6ce", + "reference": "55487a5e55ab67ad77a84eefd1c92b0fab47e6ce", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/config": "^4.4 | ^5.0 | ^6.0", + "symfony/dependency-injection": "^4.4 | ^5.0 | ^6.0", + "symfony/deprecation-contracts": "^2.2", + "symfony/http-kernel": "^4.4 | ^5.0 | ^6.0", + "symfony/routing": "^4.4 | ^5.0 | ^6.0" + }, + "conflict": { + "symfony/framework-bundle": "<4.4" + }, + "require-dev": { + "doctrine/orm": "^2.7", + "doctrine/persistence": "^2.0", + "symfony/framework-bundle": "^4.4 | ^5.0 | ^6.0", + "symfony/phpunit-bridge": "^5.0 | ^6.0", + "vimeo/psalm": "^4.3" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "SymfonyCasts\\Bundle\\VerifyEmail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Simple, stylish Email Verification for Symfony", + "support": { + "issues": "https://github.com/SymfonyCasts/verify-email-bundle/issues", + "source": "https://github.com/SymfonyCasts/verify-email-bundle/tree/v1.6.0" + }, + "time": "2021-11-19T00:41:41+00:00" + }, { "name": "twig/extra-bundle", "version": "v3.3.3", @@ -7648,183 +7819,146 @@ ], "packages-dev": [ { - "name": "behat/gherkin", - "version": "v4.9.0", + "name": "myclabs/deep-copy", + "version": "1.10.2", "source": { "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { - "php": "~7.2|~8.0" + "php": "^7.1 || ^8.0" }, - "require-dev": { - "cucumber/cucumber": "dev-gherkin-22.0.0", - "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5" + "replace": { + "myclabs/deep-copy": "self.version" }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP", - "homepage": "http://behat.org/", + "description": "Create deep copies (clones) of your objects", "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" + "clone", + "copy", + "duplicate", + "object", + "object graph" ], "support": { - "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" }, - "time": "2021-10-12T13:05:09+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" }, { - "name": "codeception/codeception", - "version": "4.1.22", + "name": "nikic/php-parser", + "version": "v4.13.1", "source": { "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "9777ec3690ceedc4bce2ed13af7af4ca4ee3088f" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/9777ec3690ceedc4bce2ed13af7af4ca4ee3088f", - "reference": "9777ec3690ceedc4bce2ed13af7af4ca4ee3088f", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", "shasum": "" }, "require": { - "behat/gherkin": "^4.4.0", - "codeception/lib-asserts": "^1.0", - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", - "codeception/stub": "^2.0 | ^3.0", - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "guzzlehttp/psr7": "^1.4 | ^2.0", - "php": ">=5.6.0 <9.0", - "symfony/console": ">=2.7 <6.0", - "symfony/css-selector": ">=2.7 <6.0", - "symfony/event-dispatcher": ">=2.7 <6.0", - "symfony/finder": ">=2.7 <6.0", - "symfony/yaml": ">=2.7 <6.0" + "ext-tokenizer": "*", + "php": ">=7.0" }, "require-dev": { - "codeception/module-asserts": "1.*@dev", - "codeception/module-cli": "1.*@dev", - "codeception/module-db": "1.*@dev", - "codeception/module-filesystem": "1.*@dev", - "codeception/module-phpbrowser": "1.*@dev", - "codeception/specify": "~0.3", - "codeception/util-universalframework": "*@dev", - "monolog/monolog": "~1.8", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <6.0", - "vlucas/phpdotenv": "^2.0 | ^3.0 | ^4.0 | ^5.0" - }, - "suggest": { - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "hoa/console": "For interactive console functionality", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ - "codecept" + "bin/php-parse" ], "type": "library", "extra": { - "branch-alias": [] + "branch-alias": { + "dev-master": "4.9-dev" + } }, "autoload": { "psr-4": { - "Codeception\\": "src/Codeception", - "Codeception\\Extension\\": "ext" + "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" + "name": "Nikita Popov" } ], - "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", + "description": "A PHP parser written in PHP", "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" + "parser", + "php" ], "support": { - "issues": "https://github.com/Codeception/Codeception/issues", - "source": "https://github.com/Codeception/Codeception/tree/4.1.22" + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1" }, - "funding": [ - { - "url": "https://opencollective.com/codeception", - "type": "open_collective" - } - ], - "time": "2021-08-06T17:15:34+00:00" + "time": "2021-11-03T20:52:16+00:00" }, { - "name": "codeception/lib-asserts", - "version": "1.13.2", + "name": "phar-io/manifest", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/Codeception/lib-asserts.git", - "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6" + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/184231d5eab66bc69afd6b9429344d80c67a33b6", - "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3 | ^9.0", "ext-dom": "*", - "php": ">=5.6.0 <9.0" + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -7832,61 +7966,48 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" }, { - "name": "Gintautas Miselis" + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" }, { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Assertion methods used by Codeception core and Asserts module", - "homepage": "https://codeception.com/", - "keywords": [ - "codeception" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/Codeception/lib-asserts/issues", - "source": "https://github.com/Codeception/lib-asserts/tree/1.13.2" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" }, - "time": "2020-10-21T16:26:20+00:00" + "time": "2021-07-20T11:28:43+00:00" }, { - "name": "codeception/lib-innerbrowser", - "version": "1.5.1", + "name": "phar-io/version", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/Codeception/lib-innerbrowser.git", - "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2" + "url": "https://github.com/phar-io/version.git", + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-innerbrowser/zipball/31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", - "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { - "codeception/codeception": "4.*@dev", - "ext-dom": "*", - "ext-json": "*", - "ext-mbstring": "*", - "php": ">=5.6.0 <9.0", - "symfony/browser-kit": ">=2.7 <6.0", - "symfony/dom-crawler": ">=2.7 <6.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "require-dev": { - "codeception/util-universalframework": "dev-master" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -7896,857 +8017,52 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" }, { - "name": "Gintautas Miselis" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Parent library for all Codeception framework modules and PhpBrowser", - "homepage": "https://codeception.com/", - "keywords": [ - "codeception" - ], + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/Codeception/lib-innerbrowser/issues", - "source": "https://github.com/Codeception/lib-innerbrowser/tree/1.5.1" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" }, - "time": "2021-08-30T15:21:42+00:00" + "time": "2021-02-23T14:00:09+00:00" }, { - "name": "codeception/module-asserts", - "version": "1.3.1", + "name": "phpspec/prophecy", + "version": "1.14.0", "source": { "type": "git", - "url": "https://github.com/Codeception/module-asserts.git", - "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de" + "url": "https://github.com/phpspec/prophecy.git", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-asserts/zipball/59374f2fef0cabb9e8ddb53277e85cdca74328de", - "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { - "codeception/codeception": "*@dev", - "codeception/lib-asserts": "^1.13.1", - "php": ">=5.6.0 <9.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - }, - { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" - } - ], - "description": "Codeception module containing various assertions", - "homepage": "https://codeception.com/", - "keywords": [ - "assertions", - "asserts", - "codeception" - ], - "support": { - "issues": "https://github.com/Codeception/module-asserts/issues", - "source": "https://github.com/Codeception/module-asserts/tree/1.3.1" - }, - "time": "2020-10-21T16:48:15+00:00" - }, - { - "name": "codeception/module-phpbrowser", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-phpbrowser.git", - "reference": "770a6be4160a5c0c08d100dd51bff35f6056bbf1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-phpbrowser/zipball/770a6be4160a5c0c08d100dd51bff35f6056bbf1", - "reference": "770a6be4160a5c0c08d100dd51bff35f6056bbf1", - "shasum": "" - }, - "require": { - "codeception/codeception": "^4.0", - "codeception/lib-innerbrowser": "^1.3", - "guzzlehttp/guzzle": "^6.3|^7.0", - "php": ">=5.6.0 <9.0" - }, - "conflict": { - "codeception/codeception": "<4.0" - }, - "require-dev": { - "codeception/module-rest": "^1.0" - }, - "suggest": { - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - } - ], - "description": "Codeception module for testing web application over HTTP", - "homepage": "http://codeception.com/", - "keywords": [ - "codeception", - "functional-testing", - "http" - ], - "support": { - "issues": "https://github.com/Codeception/module-phpbrowser/issues", - "source": "https://github.com/Codeception/module-phpbrowser/tree/1.0.2" - }, - "time": "2020-10-24T15:29:28+00:00" - }, - { - "name": "codeception/module-symfony", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/Codeception/module-symfony.git", - "reference": "3e43a76c06bf9d82a3bbfc84e930ff14b53db1f6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-symfony/zipball/3e43a76c06bf9d82a3bbfc84e930ff14b53db1f6", - "reference": "3e43a76c06bf9d82a3bbfc84e930ff14b53db1f6", - "shasum": "" - }, - "require": { - "codeception/codeception": "^4.0", - "codeception/lib-innerbrowser": "^1.4", - "ext-json": "*", - "php": "^7.3 | ^8.0" - }, - "require-dev": { - "codeception/module-asserts": "^1.3", - "codeception/module-doctrine2": "^1.1", - "vlucas/phpdotenv": "^4.2 | ^5.3" - }, - "suggest": { - "codeception/module-asserts": "Include traditional PHPUnit assertions in your tests", - "symfony/web-profiler-bundle": "Tool that gives information about the execution of requests" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" - } - ], - "description": "Codeception module for Symfony framework", - "homepage": "https://codeception.com/", - "keywords": [ - "codeception", - "symfony" - ], - "support": { - "issues": "https://github.com/Codeception/module-symfony/issues", - "source": "https://github.com/Codeception/module-symfony/tree/2.0.5" - }, - "time": "2021-07-07T00:17:29+00:00" - }, - { - "name": "codeception/phpunit-wrapper", - "version": "9.0.6", - "source": { - "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "b0c06abb3181eedca690170f7ed0fd26a70bfacc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/b0c06abb3181eedca690170f7ed0fd26a70bfacc", - "reference": "b0c06abb3181eedca690170f7ed0fd26a70bfacc", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "phpunit/phpunit": "^9.0" - }, - "require-dev": { - "codeception/specify": "*", - "consolidation/robo": "^3.0.0-alpha3", - "vlucas/phpdotenv": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\PHPUnit\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - }, - { - "name": "Naktibalda" - } - ], - "description": "PHPUnit classes used by Codeception", - "support": { - "issues": "https://github.com/Codeception/phpunit-wrapper/issues", - "source": "https://github.com/Codeception/phpunit-wrapper/tree/9.0.6" - }, - "time": "2020-12-28T13:59:47+00:00" - }, - { - "name": "codeception/stub", - "version": "3.7.0", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "468dd5fe659f131fc997f5196aad87512f9b1304" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/468dd5fe659f131fc997f5196aad87512f9b1304", - "reference": "468dd5fe659f131fc997f5196aad87512f9b1304", - "shasum": "" - }, - "require": { - "phpunit/phpunit": "^8.4 | ^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "support": { - "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/3.7.0" - }, - "time": "2020-07-03T15:54:43+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.4.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.8.3 || ^2.1", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2021-10-18T09:52:00+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2021-10-22T20:56:57+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72", - "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.1.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2021-10-06T17:43:30+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.10.2", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2020-11-13T09:40:50+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.13.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", - "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1" - }, - "time": "2021-11-03T20:52:16+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "bae7c545bef187884426f042434e561ab1ddb182" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", - "reference": "bae7c545bef187884426f042434e561ab1ddb182", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.1.0" - }, - "time": "2021-02-23T14:00:09+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.14.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { "phpspec/phpspec": "^6.0 || ^7.0", @@ -9463,210 +8779,6 @@ ], "time": "2021-09-25T07:38:51+00:00" }, - { - "name": "psr/http-client", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client/tree/master" - }, - "time": "2020-06-29T06:28:15+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" - }, - "time": "2019-04-30T12:38:16+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, { "name": "sebastian/cli-parser", "version": "1.0.1", @@ -11230,7 +10342,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.2.5", + "php": ">=7.4", "ext-ctype": "*", "ext-iconv": "*" }, diff --git a/config/bundles.php b/config/bundles.php index cd98906..ac9ef18 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -12,4 +12,6 @@ return [ Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], + SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle::class => ['all' => true], + Gregwar\CaptchaBundle\GregwarCaptchaBundle::class => ['all' => true], ]; diff --git a/config/packages/security.yaml b/config/packages/security.yaml index e5ed9a7..c1ebf67 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -20,6 +20,11 @@ security: main: lazy: true provider: app_user_provider + custom_authenticator: App\Security\AppCustomAuthenticator + 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/routes.yaml b/config/routes.yaml index c3283aa..35beba8 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,3 +1,5 @@ #index: # path: / # controller: App\Controller\DefaultController::index +gregwar_captcha_routing: + resource: "@GregwarCaptchaBundle/Resources/config/routing/routing.yml" diff --git a/config/services.yaml b/config/services.yaml index ef07b76..da913b9 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,6 +4,7 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: + app.captcha_bypass : '%env(string:BY_PASS_CODE)%' services: # default configuration for services in *this* file @@ -21,5 +22,9 @@ services: - '../src/Kernel.php' - '../src/Tests/' + App\Form\RegistrationFormType: + arguments: + $byPass: '%app.captcha_bypass%' + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/Controller/RegistrationController.php b/src/Controller/RegistrationController.php new file mode 100644 index 0000000..76e4571 --- /dev/null +++ b/src/Controller/RegistrationController.php @@ -0,0 +1,101 @@ +<?php + +namespace App\Controller; + +use App\Entity\User; +use App\Form\RegistrationFormType; +use App\Repository\UserRepository; +use App\Security\EmailVerifier; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Twig\Mime\TemplatedEmail; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Mime\Address; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +use Symfony\Component\Routing\Annotation\Route; +use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; + +class RegistrationController extends AbstractController +{ + private EmailVerifier $emailVerifier; + + public function __construct(EmailVerifier $emailVerifier) + { + $this->emailVerifier = $emailVerifier; + } + + /** + * @Route("/register", name="app_register") + */ + public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, EntityManagerInterface $entityManager): Response + { + $user = new User(); + $form = $this->createForm(RegistrationFormType::class, $user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + + $user->setSalt(random_bytes(100)); + + // encode the plain password + $user->setPassword( + $userPasswordHasher->hashPassword( + $user, + $form->get('plainPassword')->getData() + ) + ); + + $entityManager->persist($user); + $entityManager->flush(); + + // generate a signed url and email it to the user + $this->emailVerifier->sendEmailConfirmation('app_verify_email', $user, + (new TemplatedEmail()) + ->from(new Address('contact@localhost.com', 'MemoRekall')) + ->to($user->getEmail()) + ->subject('Please Confirm your Email') + ->htmlTemplate('registration/confirmation_email.html.twig') + ); + // do anything else you need here, like send an email + + return $this->redirectToRoute('_profiler_home'); + } + + return $this->render('registration/register.html.twig', [ + 'registrationForm' => $form->createView(), + ]); + } + + /** + * @Route("/verify/email", name="app_verify_email") + */ + public function verifyUserEmail(Request $request, UserRepository $userRepository): Response + { + $id = $request->get('id'); + + if (null === $id) { + return $this->redirectToRoute('app_register'); + } + + $user = $userRepository->find($id); + + if (null === $user) { + return $this->redirectToRoute('app_register'); + } + + // validate email confirmation link, sets User::isVerified=true and persists + try { + $this->emailVerifier->handleEmailConfirmation($request, $user); + } catch (VerifyEmailExceptionInterface $exception) { + $this->addFlash('verify_email_error', $exception->getReason()); + + return $this->redirectToRoute('app_register'); + } + + // @TODO Change the redirect on success and handle or remove the flash message in your templates + $this->addFlash('success', 'Your email address has been verified.'); + + return $this->redirectToRoute('app_register'); + } +} diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..65c096f --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,36 @@ +<?php + +namespace App\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; + +class SecurityController extends AbstractController +{ + /** + * @Route("/login", name="app_login") + */ + public function login(AuthenticationUtils $authenticationUtils): Response + { + // if ($this->getUser()) { + // return $this->redirectToRoute('target_path'); + // } + + // 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("/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/Form/RegistrationFormType.php b/src/Form/RegistrationFormType.php new file mode 100644 index 0000000..daf1dfd --- /dev/null +++ b/src/Form/RegistrationFormType.php @@ -0,0 +1,88 @@ +<?php + +namespace App\Form; + +use App\Entity\User; +use Gregwar\CaptchaBundle\Type\CaptchaType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\EmailType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\IsTrue; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\DependencyInjection\Loader\Configurator; + +class RegistrationFormType extends AbstractType +{ + private string $byPass; + public function __construct(string $byPass){ + $this->byPass = $byPass; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $row_attr = ['class' => 'form-group d-flex flex-column m-auto mb-4 col-6',]; + $builder + ->add('email',EmailType::class, [ + 'row_attr' => $row_attr + ]) + ->add('agreeTerms', CheckboxType::class, [ + 'mapped' => false, + 'constraints' => [ + new IsTrue([ + 'message' => 'You should agree to our terms.', + ]), + ], + 'row_attr' => ['class' => 'form-group d-flex flex-row m-auto mb-4 col-6',], + 'label' => 'Accept terms and conditions' + ]) + ->add('plainPassword', PasswordType::class, [ + // instead of being set onto the object directly, + // this is read and encoded in the controller + '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, + ]), + ], + 'row_attr' => $row_attr + ]) + ->add('firstName', TextType::class, + ['constraints' => [new NotBlank(['message' => 'Please enter your first name'])], + 'row_attr' => $row_attr + ]) + ->add('lastName', TextType::class, + ['constraints' => [new NotBlank(['message' => 'Please enter your last name'])], + 'row_attr' => $row_attr + ]) + ->add('captcha', CaptchaType::class,[ + 'label' => 'Captcha', + 'required' => false, + 'reload' => true, + 'as_url' => true, + 'row_attr' => $row_attr, + 'bypass_code' => $this->byPass + ]) + ->add('submit', SubmitType::class, ['label' => 'Register', 'row_attr' => $row_attr]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/src/Security/AppCustomAuthenticator.php b/src/Security/AppCustomAuthenticator.php new file mode 100644 index 0000000..818ef11 --- /dev/null +++ b/src/Security/AppCustomAuthenticator.php @@ -0,0 +1,62 @@ +<?php + +namespace App\Security; + +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; +use Symfony\Component\Security\Http\Authenticator\Passport\Passport; +use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; +use Symfony\Component\Security\Http\Util\TargetPathTrait; + +class AppCustomAuthenticator extends AbstractLoginFormAuthenticator +{ + use TargetPathTrait; + + public const LOGIN_ROUTE = 'app_login'; + + private UrlGeneratorInterface $urlGenerator; + + public function __construct(UrlGeneratorInterface $urlGenerator) + { + $this->urlGenerator = $urlGenerator; + } + + public function authenticate(Request $request): PassportInterface + { + $email = $request->request->get('email', ''); + + $request->getSession()->set(Security::LAST_USERNAME, $email); + + return new Passport( + new UserBadge($email), + new PasswordCredentials($request->request->get('password', '')), + [ + new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')), + ] + ); + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response + { + if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) { + return new RedirectResponse($targetPath); + } + + // For example: + //return new RedirectResponse($this->urlGenerator->generate('some_route')); + throw new \Exception('TODO: provide a valid redirect inside '.__FILE__); + } + + protected function getLoginUrl(Request $request): string + { + return $this->urlGenerator->generate(self::LOGIN_ROUTE); + } +} diff --git a/src/Security/EmailVerifier.php b/src/Security/EmailVerifier.php new file mode 100644 index 0000000..d5a7508 --- /dev/null +++ b/src/Security/EmailVerifier.php @@ -0,0 +1,57 @@ +<?php + +namespace App\Security; + +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Twig\Mime\TemplatedEmail; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; +use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface; + +class EmailVerifier +{ + private $verifyEmailHelper; + private $mailer; + private $entityManager; + + public function __construct(VerifyEmailHelperInterface $helper, MailerInterface $mailer, EntityManagerInterface $manager) + { + $this->verifyEmailHelper = $helper; + $this->mailer = $mailer; + $this->entityManager = $manager; + } + + public function sendEmailConfirmation(string $verifyEmailRouteName, UserInterface $user, TemplatedEmail $email): void + { + $signatureComponents = $this->verifyEmailHelper->generateSignature( + $verifyEmailRouteName, + $user->getId(), + $user->getEmail(), + ['id' => $user->getId()] + ); + + $context = $email->getContext(); + $context['signedUrl'] = $signatureComponents->getSignedUrl(); + $context['expiresAtMessageKey'] = $signatureComponents->getExpirationMessageKey(); + $context['expiresAtMessageData'] = $signatureComponents->getExpirationMessageData(); + + $email->context($context); + + $this->mailer->send($email); + } + + /** + * @throws VerifyEmailExceptionInterface + */ + public function handleEmailConfirmation(Request $request, UserInterface $user): void + { + $this->verifyEmailHelper->validateEmailConfirmation($request->getUri(), $user->getId(), $user->getEmail()); + + $user->setIsVerified(true); + + $this->entityManager->persist($user); + $this->entityManager->flush(); + } +} diff --git a/symfony.lock b/symfony.lock index 09dcb4f..a62470f 100644 --- a/symfony.lock +++ b/symfony.lock @@ -148,6 +148,12 @@ "guzzlehttp/psr7": { "version": "2.1.0" }, + "gregwar/captcha": { + "version": "v1.1.9" + }, + "gregwar/captcha-bundle": { + "version": "v2.1.5" + }, "laminas/laminas-code": { "version": "4.4.3" }, @@ -228,24 +234,12 @@ "psr/event-dispatcher": { "version": "1.0.0" }, - "psr/http-client": { - "version": "1.0.1" - }, - "psr/http-factory": { - "version": "1.0.1" - }, - "psr/http-message": { - "version": "1.0.1" - }, "psr/link": { "version": "1.0.0" }, "psr/log": { "version": "1.1.4" }, - "ralouphie/getallheaders": { - "version": "3.0.3" - }, "sebastian/cli-parser": { "version": "1.0.1" }, @@ -664,6 +658,9 @@ "symfony/yaml": { "version": "v5.3.6" }, + "symfonycasts/verify-email-bundle": { + "version": "v1.6.0" + }, "theseer/tokenizer": { "version": "1.2.1" }, diff --git a/templates/base.html.twig b/templates/base.html.twig index 16d7273..9d5aa3b 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -6,11 +6,11 @@ {# Run `composer require symfony/webpack-encore-bundle` and uncomment the following Encore helpers to start using Symfony UX #} {% block stylesheets %} - {#{{ encore_entry_link_tags('app') }}#} + {{ encore_entry_link_tags('app') }} {% endblock %} {% block javascripts %} - {#{{ encore_entry_script_tags('app') }}#} + {{ encore_entry_script_tags('app') }} {% endblock %} </head> <body> diff --git a/templates/registration/confirmation_email.html.twig b/templates/registration/confirmation_email.html.twig new file mode 100644 index 0000000..bc307f2 --- /dev/null +++ b/templates/registration/confirmation_email.html.twig @@ -0,0 +1,11 @@ +<h1>Hi! Please confirm your email!</h1> + +<p> + Please confirm your email address by clicking the following link: <br><br> + <a href="{{ signedUrl }}">Confirm my Email</a>. + This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}. +</p> + +<p> + Cheers! +</p> diff --git a/templates/registration/register.html.twig b/templates/registration/register.html.twig new file mode 100644 index 0000000..7ea0360 --- /dev/null +++ b/templates/registration/register.html.twig @@ -0,0 +1,36 @@ +{% extends 'base.html.twig' %} + +{% block title %}Register{% endblock %} +{% block body %} + <div class="container d-flex flex-column justify-content-center"> + {% for flashError in app.flashes('verify_email_error') %} + <div class="alert alert-danger" role="alert">{{ flashError }}</div> + {% endfor %} + {% for flashError in app.flashes('success') %} + <div class="alert alert-danger" role="alert">{{ flashError }}</div> + {% endfor %} + + <div> + + </div> + + + {{ form_start(registrationForm, {'attr': {'class': 'd-flex flex-column justify-content-center'}}) }} + {{ form_errors(registrationForm) }} + {{ form_row(registrationForm.firstName) }} + {{ form_row(registrationForm.lastName) }} + {{ form_row(registrationForm.email) }} + {{ form_row(registrationForm.plainPassword, { + label: 'Password' + }) }} + + {{ form_row(registrationForm.captcha) }} + <div class="form-group d-flex flex-row m-auto mb-4 col-6"> + {{ form_label(registrationForm.agreeTerms) }} {{ form_widget(registrationForm.agreeTerms, {'attr': {'class': 'ms-3'}}) }} + </div> + + {{ form_row(registrationForm.submit) }} + {# <button type="submit" class="btn">Register</button>#} + {{ form_end(registrationForm) }} + </div> +{% endblock %} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig new file mode 100644 index 0000000..7490fa3 --- /dev/null +++ b/templates/security/login.html.twig @@ -0,0 +1,42 @@ +{% extends 'base.html.twig' %} + +{% block title %}Log in!{% endblock %} + +{% block body %} +<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.username }}, <a href="{{ path('app_logout') }}">Logout</a> + </div> + {% endif %} + + <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1> + <label for="inputEmail">Email</label> + <input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" autocomplete="email" required autofocus> + <label for="inputPassword">Password</label> + <input type="password" name="password" id="inputPassword" class="form-control" autocomplete="current-password" required> + + <input type="hidden" name="_csrf_token" + value="{{ csrf_token('authenticate') }}" + > + + {# + Uncomment this section and add a remember_me option below your firewall to activate remember me functionality. + See https://symfony.com/doc/current/security/remember_me.html + + <div class="checkbox mb-3"> + <label> + <input type="checkbox" name="_remember_me"> Remember me + </label> + </div> + #} + + <button class="btn btn-lg btn-primary" type="submit"> + Sign in + </button> +</form> +{% endblock %} -- GitLab