diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..90b902ad04 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.dist.php export-ignore +/Changelog.md export-ignore +/Makefile export-ignore +/README.md export-ignore +/docs/ export-ignore +/tests/ export-ignore +/Upgrade.md export-ignore +/phpunit.xml.dist export-ignore diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..12bcd609ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,32 @@ + + + + +**Symfony FOSUserBundle versions**: + + + +**Description of the problem including expected versus actual behavior**: + +**Steps to reproduce**: + 1. + 2. + 3. + +**Provide logs (if relevant)**: + + + +**Describe the feature**: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000000..9634258354 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,58 @@ +name: CI + +on: + pull_request: + push: + branches: + - master + - 2.x + - 3.x + +jobs: + run: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: + - '8.1' + - '8.2' + - '8.3' + symfony-versions: [false] + include: + - description: 'Symfony 6.*' + php: '8.2' + symfony-versions: '^6.4' + name: PHP ${{ matrix.php }} ${{ matrix.description }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/cache@v4 + with: + path: ~/.composer/cache/files + key: composer-${{ matrix.php }}-${{ matrix.symfony-versions }}-${{ hashFiles('composer.json') }} + restore-keys: | + composer-${{ matrix.php }}- + composer- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + + - name: Install symfony/flex + run: composer config --global --no-plugins allow-plugins.symfony/flex true && composer global require symfony/flex + if: matrix.symfony-versions + + - name: Install dependencies + env: + SYMFONY_REQUIRE: ${{ matrix.symfony-versions }} + run: composer update + + - name: Install PHPUnit + run: ./vendor/bin/simple-phpunit install + + - name: Run PHPUnit tests + run: | + ./vendor/bin/simple-phpunit diff --git a/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml new file mode 100644 index 0000000000..2fe3380761 --- /dev/null +++ b/.github/workflows/static-analysis.yaml @@ -0,0 +1,35 @@ +name: Code style and composer validate + +on: + pull_request: + push: + branches: [ master, 2.x, 3.x ] + +jobs: + composer-validate: + name: Validate composer.json + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Validate + run: composer validate --no-check-lock --strict + + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + + - name: Install dependencies + run: composer install + + - name: Run script + run: vendor/bin/php-cs-fixer fix --verbose --diff --dry-run diff --git a/.gitignore b/.gitignore index a54545ed77..9986d0223f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.php-cs-fixer.php +.php-cs-fixer.cache +composer.lock phpunit.xml -nbproject -catalog.xml +vendor/ +.phpunit.result.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000000..88e63d8e57 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,37 @@ + + +For the full copyright and license information, please view the LICENSE +file that was distributed with this source code. +EOF; + +return (new PhpCsFixer\Config()) + ->setRules([ + '@Symfony' => true, + 'array_syntax' => ['syntax' => 'short'], + 'combine_consecutive_unsets' => true, + 'header_comment' => ['header' => $header], + 'linebreak_after_opening_tag' => true, + 'no_php4_constructor' => true, + 'no_useless_else' => true, + 'ordered_class_elements' => true, + 'ordered_imports' => true, + 'php_unit_construct' => true, + 'php_unit_strict' => true, + 'phpdoc_no_empty_return' => false, + 'no_superfluous_phpdoc_tags' => [ + 'remove_inheritdoc' => true, + 'allow_mixed' => true, + ], + ]) + ->setUsingCache(true) + ->setRiskyAllowed(true) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__) + ) +; diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000000..8aaa92dc1b --- /dev/null +++ b/Changelog.md @@ -0,0 +1,357 @@ +Changelog +========= + +### 4.0.0 (2024-07-04) + +* [BC break] Removed the CouchDB ODM integration +* [BC break] Added return types in most methods +* [BC break] Marked classes as final when they were `@final` +* Removed support for symfony <6.4 +* Removed support for PHP <8.1 +* Remove the mailer implementation based on Swiftmailer +* Added support for Symfony 7 + +### 3.4.0 (2024-06-25) + +* Deprecated the TwigSwiftMailer implementation + +### 3.3.0 (2024-06-24) + +* Added a mailer implementation based on symfony/mailer and Twig +* Added tentative return types in most methods +* Deprecated the CouchDB ODM integration as the ODM is unmaintained + +### 3.2.1 (2023-07-06) + +* Fixed remaining deprecations with Symfony 6.3 + +### 3.2.0 (2023-07-06) + +* Fixed deprecations with Symfony 6.3 +* Fixed deprecations with Doctrine ORM (requires using DoctrineBundle 2.10.1 or newer for the fix to be effective) +* Fixed the way to access the session when enabling confirmation emails +* Fixed the way to access the firewall name when enabling the registration feature + +### 3.1.0 (2022-10-26) + +* Added support for Symfony 6 + +### 3.0.2 (2022-10-26) + +* Fixed support for the remember-me in the programmatic login when using the new authentication system of Symfony. +* Fixed some deprecations when using Symfony 5.4. + +### 3.0.1 (2022-08-27) + +* Fixed the wiring of controllers to avoid a deprecation warning when using Twig. + +### 3.0.0 (2022-04-28) + +* [BC break] Change the base class of controllers to use the `AbstractController` (but extending the controllers is not officially supported anymore). +* [BC break] Remove the group feature +* [BC break] Change the base class for events to `Symfony\Contracts\EventDispatcher\Event` instead of `Symfony\Component\EventDispatcher\Event` +* [BC break] Remove the `Symfony\Component\Security\Core\User\AdvancedUserInterface` methods from our `UserInterface` +* [BC break] The ResettingListener now longer blocks password resetting requests based on the `isAccountNonLocked` method of the `AdvancedUserInterface`. Projects customizing `isAccountNonLocked` for that purpose should instead register their own listener for the `FOSUserEvents::RESETTING_RESET_REQUEST` event to set a response instead of processing the request. +* [BC break] Made `\FOS\UserBundle\Model\User::serialize` and `\FOS\UserBundle\Model\User::unserialize` final. Child classes needing to extend the serialization must override `__serialize` and `__unserialize` instead. +* [BC break] `\FOS\UserBundle\Event\GetResponseNullableUserEvent` no longer inherits from `\FOS\UserBundle\Event\GetResponseUserEvent` and `\FOS\UserBundle\Event\UserEvent` as that was breaking variance rules. +* [BC break] A few methods of `FOS\UserBundle\Model\User` now have return types (in methods where Symfony 6 requires them) +* [BC break] The legacy mailer based on SwiftMailer and symfony/templating is no longer used by default. Selecting a mailer service is now mandatory when using a feature needing the mailer. +* [BC break] Remove the legacy mailer based on SwiftMailer and symfony/templating. Use `fos_user.mailer.twig_swift` or a custom mailer service. +* Add support for Symfony 5. +* Add return types in most methods. +* Add autowiring support for `FOS\UserBundle\Mailer\MailerInterface` + +### 2.2.4 (2022-01-14) + +* Fixed a deprecation warning reported by DebugClassLoader in the AdvancedUserInterface BC layer due to the change done in 2.2.3. + +### 2.2.3 (2022-01-14) + +* Added missing deprecations on some group-related event classes +* Fixed an invalid report of `UserInterface` being deprecated in static analyzers +* Fixed the documented return type for `\FOS\UserBundle\Event\GetResponseNullableUserEvent::getUser` + +### 2.2.2 (2021-09-08) + +* Fixed a deprecation warning about groups being triggered when loading all Doctrine metadata. + +### 2.2.1 (2021-09-08) + +* Fixed a deprecation warning about groups being triggered when loading the User class of the bundle. + +### 2.2.0 (2021-08-26) + +* Deprecated the Groups feature. +* Marked all controllers final. +* Marked internal classes as such. +* Added Mongolian translation. +* Added an email provider. +* Added a custom user checker. +* Added PHP 7.4 and PHP 8.0 support. +* Removed fieldName attribute in MongoDB mapping. +* Registration confirmation now redirects to login page if token is invalid. +* User model will not rely on `AdvancedUserInterface` anymore. +* Self-salting password encoders will not create a salt anymore. +* FlashListener constructor now accepts `SessionInterface`. +* Fixed several Symfony deprecation notices. +* Fixed several translations. +* Bumped the min PHP version to 7.1.3. +* Bumped the min Symfony version to 4.4. +* Added compatibility with Twig 3. +* Added compatibility with doctrine/persistence 2. + +### 2.1.2 (2018-03-08) + +* Fixed compatibility of controllers with Symfony 2.8 + +### 2.1.1 (2018-02-20) + +* Fixed the check for the required session, to account for the fact it is not always required. + +### 2.1.0 (2018-02-16) + +* Dropped Symfony < 2.8 support. +* Add Symfony 4 compatibility. +* Refactored controllers and commands to use DI. Projects extending these classes will need to adapt their code (but should rather use supported extension points when possible). +* Redirect to login when requesting resetting password with invalid token. +* Added autocomplete hints for password inputs. +* Fixed several incorrect Turkish translations. + +### 2.0.2 (2017-11-29) + +* Fix empty password in ChangePasswordFormType. +* Fix empty password in ProfileFormType. +* Introduced aliases for autowiring user and group managers. +* Added Bengali translation. +* Added Galician translation. +* Updated Danish translation. +* Updated Japanese translation. + +### 2.0.1 (2017-05-31) + +* Add SwiftMailer 6 compatibility. +* Inject firewall `user_checker` into `LoginManager`. +* Updated English translation. +* Updated Estonian translation. +* Updated Persian translation. +* Updated Turkish translation. +* Updated several docs. + +### 2.0.0 (2017-03-29) + +* Removed default `fos_user.from_email` configuration values. +* Removed usage of internal Twig APIs when rendering emails. +* Add a timeout for the reset retry request. +* Add Esperanto translations. +* Fixed incorrect confirmation url. +* Commented outdated entries in several translation files. +* [BC break] Use `UserManager::getRepository()` instead of `UserManager::$repository`. +* [BC break] Use `UserManager::getClass()` instead of `UserManager::$class`. + +### 2.0.0-beta2 (2017-01-31) + +* Use ceil in `ResettingController` for a better token lifetime approximation. +* Removed unused translation keys. +* Removed form deprecations. +* Use `@`-based Twig syntax for templates. +* Improved several language files. +* Improved documentation. +* Ability to disable the authentication listener. +* Removed `DateUtil` class. +* [BC break] Changed validation max length to match the database structure. + +### 2.0.0-beta1 (2016-11-29) + +* Dropped Symfony < 2.7 support. +* Dropped PHP < 5.5 support. +* Exclude tests from autoloader. +* Allow to use POST for logout. +* Fix UserPassword constraint validation groups. +* Harmonized email detection in `UserManager`. +* Added unique index for `confirmation_token` field. +* Added Kyrgyz translation files. +* Added user manipulator events. +* Replaced `checkPostAuth` by `checkPreAuth` in `AuthenticationListener`. +* [BC break] Method `ResettingController::getObfuscatedEmail` has been removed. +* [BC break] Renamed templates to underscore case. +* [BC break] Removed `UserManager::refreshUser`. +* [BC break] Removed `UserManager::loadUserByUsername`. +* [BC break] Removed `UserManager::supportsClass`. +* [BC break] Removed `FOS\UserBundle\Model\User` properties `$locked`, `$expired`, `$expiredAt`, `$credentialsExpired`, `$credentialsExpiredAt` and associated setter and getter ([see here](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Upgrade.md#200-alpha3-to-200-beta1)). +* [BC break] The signature of the `Initializer` constructor has changed. +* [BC break] The signature of the `LoginManager` constructor has changed. +* [BC break] The signature of the `UserListener` constructor has changed. +* [BC break] The signature of the `UserManager` constructor has changed. +* [BC break] The translation key `resetting.request.invalid_username` has been removed. +* [BC break] The propel dependency was dropped. +* [BC break] The `salt` field of the `User` class is now nullable. + +### 2.0.0-alpha3 (2015-09-15) + +* Reverted the removed of the `expired` and `credentialsExpired` properties as the BC break could lead to corrupted objects being created if server sessions are not cleared when upgrading the bundle. + +### 2.0.0-alpha2 (2015-09-15) + +* The minimum requirement for Doctrine is now ORM 2.4 and MongoDB ODM 1.0-alpha10. +* [BC break] The deprecated entity classes have been removed. +* The minimum requirement for Symfony has been bumped to 2.3 (older versions are already EOLed). +* [BC break] ``UserInterface::isUser`` has been removed as it was used only by the old validation logic removed a long time ago. +* [BC break] The ``FOSUserBundle:Security:login.html.twig`` template now receives an AuthenticationException in the ``error`` + variable rather than an error message. +* [BC break] The templating engine configuration has been removed, as well as the related code. +* [BC break] Changed the XML namespace to `http://friendsofsymfony.github.io/schema/dic/user` +* [BC break] Added `UserInterface::getId`. +* [BC break][Reverted] Removed unused properties `expired` and `credentialsExpired` including corresponding methods. This may break code, + which makes use of this methods, extending classes, and/or existing installations because of missing mappings for required db fields. + +### 2.0.0-alpha1 (2014-09-26) + +* Updated many translations. +* Changed the way to pass the email to the page asking to check the email to avoid issues with non-blocking sessions. +* Changed the fos_user_security_check route to enforce POST. +* Removed the deprecated UserManager and GroupManager classes for the different Doctrine implementations. +* [BC break] Refactored the structure of controller to dispatch events instead of using form handlers. +* Removed all form handlers. +* [BC break] Changed Datetime properties of default User entity that were nullable to default to null when no value supplied. +* [BC break] Updated schema.xml for Propel BaseUser class to allow nullable and typehint accordingly. + +### 1.3.8 (xxxx-xx-xx) + +* Fixed invalid `isAccountNonExpired` timestamp when year is 2038 +* Fixed validation of blank passwords +* Removed any new lines in email subjects +* Added trailing dot flash messages +* Added trailing dot validator messages +* Added Galician translation +* Use `random_bytes` to generate tokens + +### 1.3.7 (2016-11-22) + +* Fixed some yaml errors in translation files +* Fixed bad credentials translations +* Fixed canonicalizer with illegal chars +* Fixed deprecated routing configuration +* Fixed class name check in `UserProvider::refreshUser()` +* Updated several translation files +* Removed colons from translation files +* Updated several documentation examples +* Converted documentation to rst format + +### 1.3.6 (2015-06-01) + +* Fix compatibility with Symfony 2.7 #1777 + +### 1.3.5 (2014-09-04) + +This release fixes a security issue. You are encouraged to update +as soon as possible. + +BC break: The characters used in generated tokens have changed. They +now include dashes and underscores as well. Any routing requirement +matching them should be updated to ``[\w\-]+``. + +* Fixed the TokenGenerator to preserve entropy. + +### 1.3.4 (2014-06-13) + +* Fixed the compatibility with FrameworkBundle 2.5 +* Fixed a few issues in translations +* Enforce the POST method for the login_check route + +### 1.3.3 (2013-09-23) + +This releases prevents a potential DOS attack. You are encouraged to update +as soon as possible. + +* Added a max length validation on the password + +### 1.3.2 (2013-05-25) + +* Changed the flash message handling to use the non-deprecated api +* Updated the composer constraint to allow Symfony 2.3 + +### 1.3.1 (2012-12-22) + +* Replaced the deprecated validation constraints by the new ones +* Added an error message when the repeated password is invalid +* Updated many translations +* Made the composer requirement compatible with Symfony 2.2.* +* Fixed the handling of the target url after the registration + +### 1.3.0 (2012-10-06) + +* Refactored the Propel implementation to get rid of the UserProxy +* Changed the expectation for `FOS\UserBundle\Model\GroupableInterface#getGroups` to `Traversable` +* Moved the role constants to the UserInterface instead of the abstract User class +* Refactored the Doctrine implementations to use the same manager classes +* Removed the custom uniqueness validation in favor of the core constraints +* Added getRedirectionUrl method to ProfileController +* Added an extension point in the registration handler +* Moved the generation of the token to a dedicated class +* Added new user provider classes. They should be preferred over using the UserManager as UserProvider. +* Removed the custom password validation in favor of the Symfony 2.1 constraint +* Refactored the translation of form labels using the translation_domain option of Symfony 2.1 +* Bumped the requirement to Symfony 2.1 + +### 1.2.5 (2013-09-23) + +This releases prevents a potential DOS attack. You are encouraged to update +as soon as possible. + +* Added a max length on the password field +* Fixed a Yaml parsing error in the Japanese translations + +### 1.2.4 (2012-07-10) + +This release fixes another security issue. Please update to it as soon as possible. + +* Fixes a security issue where the session could be hijacked + +### 1.2.3 (2012-07-10) + +* Fixed the serialization of users to include the id + +### 1.2.2 (2012-07-10) + +* Fixed a bug in the previous fix + +### 1.2.1 (2012-07-10) + +This release fixes a security issue. You are encouraged to update to it as soon +as possible. + +* Fixed the user refreshing to check the identity by primary key instead of username + +### 1.2.0 (2012-04-12) + +* Prefixed fos table names in propel schema with "fos_" to avoid using reserved sql words +* Added a fluent interface for the entities +* Added a mailer able to use twig blocks for the each part of the message +* Fixed the authentication in case of locked or disabled users. Github issue #464 +* Add CSRF protection to the login form +* Added translations: bg, hr +* Updated translations +* Added translations for the validation errors and the login error +* Removed the user-level algorithm. Use FOSAdvancedEncoderBundle instead if you need such feature. +* Fixed resetting password clearing the token but not the token expiration. Github issue #501 +* Renamed UsernameToUsernameTransformer to UserToUsernameTransformer and changed its service ID to `fos_user.user_to_username_transformer`. + +### 1.1.0 (2011-12-15) + +* Added "custom" as valid driver +* Hide part of the email when requesting a password reset +* Changed the validation messages to translation keys +* Added the default validation group by default +* Fixed updating of changed fields in listener. Github issue #403 +* Added support for Propel +* Added composer.json +* Made it possible to override the role constants in derived User class +* Updated translations: da, de, en, es, et, fr, hu, lb, nl, pl, pt_BR, pt_PT, ru +* Added translations: ca, cs, it, ja, ro, sk, sl, sv +* Changed the instanceof check for refreshUser to class instead of interface to allow multiple firewalls and correct use of UnsupportedUserException +* Added an extension point in the form handlers. Closes #291 +* Rewrote the documentation entirely + +### 1.0.0 (2011-08-01) + +* Initial release diff --git a/Command/ActivateUserCommand.php b/Command/ActivateUserCommand.php deleted file mode 100644 index 70c4b7fb45..0000000000 --- a/Command/ActivateUserCommand.php +++ /dev/null @@ -1,92 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * ActivateUserCommand. - * - * @package Bundle - * @subpackage FOS\UserBundle - * @author Antoine Hérault - */ -class ActivateUserCommand extends BaseCommand -{ - /** - * @see Command - */ - protected function configure() - { - $this - ->setName('fos:user:activate') - ->setDescription('Activate a user') - ->setDefinition(array( - new InputArgument('username', InputArgument::REQUIRED, 'The username'), - )) - ->setHelp(<<doctrine:user:activate command activates a super (will be able to log in) - - php app/console fos:user:activate matthieu -EOT - ); - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->container->get('security.context')->setToken(new UsernamePasswordToken('command.line', null, array(User::ROLE_SUPERADMIN))); - - $userManager = $this->container->get('fos_user.user_manager'); - $user = $userManager->findUserByUsername($input->getArgument('username')); - - if (!$user) { - throw new \InvalidArgumentException(sprintf('The user "%s" does not exist', $input->getArgument('username'))); - } - $user->setEnabled(true); - - $userManager->updateUser($user); - - $output->writeln(sprintf('User "%s" has been activated.', $user->getUsername())); - } - - /** - * @see Command - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - if (!$input->getArgument('username')) { - $username = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose a username:', - function($username) - { - if (empty($username)) { - throw new \Exception('Username can not be empty'); - } - return $username; - } - ); - $input->setArgument('username', $username); - } - } -} diff --git a/Command/ChangePasswordCommand.php b/Command/ChangePasswordCommand.php deleted file mode 100644 index 38586b556f..0000000000 --- a/Command/ChangePasswordCommand.php +++ /dev/null @@ -1,97 +0,0 @@ -setName('fos:user:changePassword') - ->setDescription('Change the password of a user.') - ->setDefinition(array( - new InputArgument('username', InputArgument::REQUIRED, 'The username'), - new InputArgument('password', InputArgument::REQUIRED, 'The password'), - )) - ->setHelp(<<fos:user:changePassword command changes the password of a user: - - php app/console fos:user:changePassword matthieu - -This interactive shell will first ask you for a password. - -You can alternatively specify the password as a second argument: - - php app/console fos:user:changePassword matthieu mypassword - -EOT - ); - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->container->get('security.context')->setToken(new UsernamePasswordToken('command.line', null, array(User::ROLE_SUPERADMIN))); - - $userManager = $this->container->get('fos_user.user_manager'); - $user = $userManager->findUserByUsername($input->getArgument('username')); - if (!$user) { - throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $input->getArgument('username'))); - } - $user->setPlainPassword($input->getArgument('password')); - $userManager->updateUser($user); - - $output->writeln(sprintf('Changed password for user %s', $user->getUsername())); - } - - /** - * @see Command - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - if (!$input->getArgument('username')) { - $username = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please give the username:', - function($username) - { - if (empty($username)) { - throw new \Exception('Username can not be empty'); - } - return $username; - } - ); - $input->setArgument('username', $username); - } - - if (!$input->getArgument('password')) { - $password = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please enter the new password:', - function($password) - { - if (empty($password)) { - throw new \Exception('Password can not be empty'); - } - return $password; - } - ); - $input->setArgument('password', $password); - } - } -} diff --git a/Command/CreateUserCommand.php b/Command/CreateUserCommand.php deleted file mode 100644 index 0da55ec579..0000000000 --- a/Command/CreateUserCommand.php +++ /dev/null @@ -1,155 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * CreateUserCommand. - * - * @package Bundle - * @subpackage FOS\UserBundle - * @author Matthieu Bontemps - * @author Thibault Duplessis - */ -class CreateUserCommand extends BaseCommand -{ - /** - * @see Command - */ - protected function configure() - { - $this - ->setName('fos:user:create') - ->setDescription('Create a user.') - ->setDefinition(array( - new InputArgument('username', InputArgument::REQUIRED, 'The username'), - new InputArgument('email', InputArgument::REQUIRED, 'The email'), - new InputArgument('password', InputArgument::REQUIRED, 'The password'), - new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Set the user as super admin'), - new InputOption('inactive', null, InputOption::VALUE_NONE, 'Set the user as inactive'), - )) - ->setHelp(<<fos:user:create command creates a user: - - php app/console fos:user:create matthieu - -This interactive shell will first ask you for a password. - -You can alternatively specify the password as a second argument: - - php app/console fos:user:create matthieu mypassword - -You can create a super admin via the super-admin flag: - - php app/console fos:user:create admin --super-admin - -You can create an inactive user (will not be able to log in): - - php app/console fos:user:create thibault --inactive - -EOT - ); - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->container->get('security.context')->setToken(new UsernamePasswordToken('command.line', null, array(User::ROLE_SUPERADMIN))); - - $userManager = $this->container->get('fos_user.user_manager'); - $user = $userManager->createUser(); - $user->setUsername($input->getArgument('username')); - $user->setEmail($input->getArgument('email')); - $user->setPlainPassword($input->getArgument('password')); - $user->setEnabled(!$input->getOption('inactive')); - $user->setSuperAdmin(!!$input->getOption('super-admin')); - $userManager->updateUser($user); - - if ($this->container->has('security.acl.provider')) { - $provider = $this->container->get('security.acl.provider'); - $oid = ObjectIdentity::fromDomainObject($user); - $acl = $provider->createAcl($oid); - $acl->insertObjectAce(UserSecurityIdentity::fromAccount($user), MaskBuilder::MASK_OWNER); - $provider->updateAcl($acl); - } - - $output->writeln(sprintf('Created user %s', $user->getUsername())); - } - - /** - * @see Command - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - if (!$input->getArgument('username')) { - $username = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose a username:', - function($username) - { - if (empty($username)) { - throw new \Exception('Username can not be empty'); - } - return $username; - } - ); - $input->setArgument('username', $username); - } - - if (!$input->getArgument('email')) { - $email = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose an email:', - function($email) - { - if (empty($email)) { - throw new \Exception('Email can not be empty'); - } - return $email; - } - ); - $input->setArgument('email', $email); - } - - if (!$input->getArgument('password')) { - $password = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose a password:', - function($password) - { - if (empty($password)) { - throw new \Exception('Password can not be empty'); - } - return $password; - } - ); - $input->setArgument('password', $password); - } - } -} diff --git a/Command/DeactivateUserCommand.php b/Command/DeactivateUserCommand.php deleted file mode 100644 index def7d5cb5d..0000000000 --- a/Command/DeactivateUserCommand.php +++ /dev/null @@ -1,92 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * DeactivateUserCommand. - * - * @package Bundle - * @subpackage FOS\UserBundle - * @author Antoine Hérault - */ -class DeactivateUserCommand extends BaseCommand -{ - /** - * @see Command - */ - protected function configure() - { - $this - ->setName('fos:user:deactivate') - ->setDescription('Deactivate a user') - ->setDefinition(array( - new InputArgument('username', InputArgument::REQUIRED, 'The username'), - )) - ->setHelp(<<fos:user:deactivate command deactivates a user (will not be able to log in) - - php app/console fos:user:deactivate matthieu -EOT - ); - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->container->get('security.context')->setToken(new UsernamePasswordToken('command.line', null, array(User::ROLE_SUPERADMIN))); - - $userManager = $this->container->get('fos_user.user_manager'); - $user = $userManager->findUserByUsername($input->getArgument('username')); - - if (!$user) { - throw new \InvalidArgumentException(sprintf('The user "%s" does not exist', $input->getArgument('username'))); - } - $user->setEnabled(false); - - $userManager->updateUser($user); - - $output->writeln(sprintf('User "%s" has been deactivated.', $user->getUsername())); - } - - /** - * @see Command - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - if (!$input->getArgument('username')) { - $username = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose a username:', - function($username) - { - if (empty($username)) { - throw new \Exception('Username can not be empty'); - } - return $username; - } - ); - $input->setArgument('username', $username); - } - } -} diff --git a/Command/DemoteSuperAdminCommand.php b/Command/DemoteSuperAdminCommand.php deleted file mode 100644 index c67c44dc0b..0000000000 --- a/Command/DemoteSuperAdminCommand.php +++ /dev/null @@ -1,92 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * DemoteSuperAdminCommand. - * - * @package Bundle - * @subpackage FOS\UserBundle - * @author Antoine Hérault - */ -class DemoteSuperAdminCommand extends BaseCommand -{ - /** - * @see Command - */ - protected function configure() - { - $this - ->setName('fos:user:demote') - ->setDescription('Demote a super administrator as a simple user') - ->setDefinition(array( - new InputArgument('username', InputArgument::REQUIRED, 'The username'), - )) - ->setHelp(<<fos:user:demote command demotes a super administrator as a simple user - - php app/console fos:user:demote matthieu -EOT - ); - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->container->get('security.context')->setToken(new UsernamePasswordToken('command.line', null, array(User::ROLE_SUPERADMIN))); - - $userManager = $this->container->get('fos_user.user_manager'); - $user = $userManager->findUserByUsername($input->getArgument('username')); - - if (!$user) { - throw new \InvalidArgumentException(sprintf('The user "%s" does not exist', $input->getArgument('username'))); - } - $user->setSuperAdmin(false); - - $userManager->updateUser($user); - - $output->writeln(sprintf('Super administrator "%s" has been demoted as a simple user.', $user->getUsername())); - } - - /** - * @see Command - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - if (!$input->getArgument('username')) { - $username = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose a username:', - function($username) - { - if (empty($username)) { - throw new \Exception('Username can not be empty'); - } - return $username; - } - ); - $input->setArgument('username', $username); - } - } -} diff --git a/Command/InstallAcesCommand.php b/Command/InstallAcesCommand.php deleted file mode 100644 index 0c814714c6..0000000000 --- a/Command/InstallAcesCommand.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * This command installs global access control entries (ACEs) - * - * @author Johannes M. Schmitt - */ -class InstallAcesCommand extends BaseCommand -{ - /** - * @see Command - */ - protected function configure() - { - $this - ->setName('fos:user:installAces') - ->setDescription('Installs global ACEs') - ->setHelp(<<container->has('security.acl.provider')) { - $output->writeln('You must setup the ACL system, see the Symfony2 documentation for how to do this.'); - return; - } - - $provider = $this->container->get('security.acl.provider'); - $oid = new ObjectIdentity('class', $this->container->get('fos_user.user_manager')->getClass()); - try { - $acl = $provider->createAcl($oid); - } catch (AclAlreadyExistsException $exists) { - $output->writeln('You already installed the global ACEs.'); - return; - } - - // insert ACEs for the super admin - $sid = new RoleSecurityIdentity(User::ROLE_SUPERADMIN); - $acl->insertClassAce($sid, MaskBuilder::MASK_IDDQD); - - $provider->updateAcl($acl); - - $output->writeln('Global ACEs have been installed.'); - } -} diff --git a/Command/PromoteSuperAdminCommand.php b/Command/PromoteSuperAdminCommand.php deleted file mode 100644 index 8540607481..0000000000 --- a/Command/PromoteSuperAdminCommand.php +++ /dev/null @@ -1,93 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * CreateUserCommand. - * - * @package Bundle - * @subpackage FOS\UserBundle - * @author Matthieu Bontemps - * @author Thibault Duplessis - */ -class PromoteSuperAdminCommand extends BaseCommand -{ - /** - * @see Command - */ - protected function configure() - { - $this - ->setName('fos:user:promote') - ->setDescription('Promotes a user as a super administrator') - ->setDefinition(array( - new InputArgument('username', InputArgument::REQUIRED, 'The username'), - )) - ->setHelp(<<fos:user:promote command promotes a user as a super administrator - - php app/console fos:user:promote matthieu -EOT - ); - } - - /** - * @see Command - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->container->get('security.context')->setToken(new UsernamePasswordToken('command.line', null, array(User::ROLE_SUPERADMIN))); - - $userManager = $this->container->get('fos_user.user_manager'); - $user = $userManager->findUserByUsername($input->getArgument('username')); - - if (!$user) { - throw new \InvalidArgumentException(sprintf('The user "%s" does not exist', $input->getArgument('username'))); - } - $user->setSuperAdmin(true); - - $userManager->updateUser($user); - - $output->writeln(sprintf('User "%s" has been promoted as a super administrator.', $user->getUsername())); - } - - /** - * @see Command - */ - protected function interact(InputInterface $input, OutputInterface $output) - { - if (!$input->getArgument('username')) { - $username = $this->getHelper('dialog')->askAndValidate( - $output, - 'Please choose a username:', - function($username) - { - if (empty($username)) { - throw new \Exception('Username can not be empty'); - } - return $username; - } - ); - $input->setArgument('username', $username); - } - } -} diff --git a/Controller/GroupController.php b/Controller/GroupController.php deleted file mode 100644 index c5098f73b2..0000000000 --- a/Controller/GroupController.php +++ /dev/null @@ -1,156 +0,0 @@ - - * (c) Christophe Coevoet - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Controller; - -use Symfony\Component\DependencyInjection\ContainerAware; -use Symfony\Component\HttpFoundation\RedirectResponse; - -use FOS\UserBundle\Model\Group; - -/** - * RESTful controller managing group CRUD - */ -class GroupController extends ContainerAware -{ - /** - * Show all groups - */ - public function listAction() - { - $groups = $this->container->get('fos_user.group_manager')->findGroups(); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Group:list.html.'.$this->getEngine(), array('groups' => $groups)); - } - - /** - * Show one group - */ - public function showAction($groupname) - { - $group = $this->findGroupBy('name', $groupname); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Group:show.html.'.$this->getEngine(), array('group' => $group)); - } - - /** - * Edit one group, show the edit form - */ - public function editAction($groupname) - { - $group = $this->findGroupBy('name', $groupname); - $form = $this->container->get('fos_user.form.group'); - - $form->process($group); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Group:edit.html.'.$this->getEngine(), array( - 'form' => $form, - 'groupname' => $group->getName() - )); - } - - /** - * Update a group - */ - public function updateAction($groupname) - { - $group = $this->findGroupBy('name', $groupname); - $form = $this->container->get('fos_user.form.group'); - - $process = $form->process($group); - if ($process) { - $this->setFlash('fos_user_group_update', 'success'); - $groupUrl = $this->container->get('router')->generate('fos_user_group_show', array('groupname' => $group->getName())); - return new RedirectResponse($groupUrl); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Group:edit.html.'.$this->getEngine(), array( - 'form' => $form, - 'groupname' => $group->getName() - )); - } - - /** - * Show the new form - */ - public function newAction() - { - $form = $this->container->get('fos_user.form.group'); - - $form->process(); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Group:new.html.'.$this->getEngine(), array( - 'form' => $form - )); - } - - /** - * Create a group - */ - public function createAction() - { - $form = $this->container->get('fos_user.form.group'); - - $process = $form->process(); - if ($process) { - $this->container->get('session')->setFlash('fos_user_group_update', 'success'); - $parameters = array('groupname' => $form->getData('group')->getName()); - $url = $this->container->get('router')->generate('fos_user_group_show', $parameters); - return new RedirectResponse($url); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Group:new.html.'.$this->getEngine(), array( - 'form' => $form - )); - } - - /** - * Delete one group - */ - public function deleteAction($groupname) - { - $group = $this->findGroupBy('name', $groupname); - $this->container->get('fos_user.group_manager')->deleteGroup($group); - $this->setFlash('fos_user_group_delete', 'success'); - - return new RedirectResponse( $this->container->get('router')->generate('fos_user_group_list')); - } - - /** - * Find a group by a specific property - * - * @param string $key property name - * @param mixed $value property value - * @throws NotFoundException if user does not exist - * @return Group - */ - protected function findGroupBy($key, $value) - { - if (!empty($value)) { - $group = $this->container->get('fos_user.group_manager')->{'findGroupBy'.ucfirst($key)}($value); - } - - if (empty($group)) { - throw new NotFoundHttpException(sprintf('The group with "%s" does not exist for value "%s"', $key, $value)); - } - - return $group; - } - - protected function getEngine() - { - return $this->container->getParameter('fos_user.template.engine'); - } - - protected function setFlash($action, $value) - { - $this->container->get('session')->setFlash($action, $value); - } -} diff --git a/Controller/SecurityController.php b/Controller/SecurityController.php deleted file mode 100644 index 5eb303a0cf..0000000000 --- a/Controller/SecurityController.php +++ /dev/null @@ -1,42 +0,0 @@ -container->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { - $error = $this->container->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR); - } else { - $error = $this->container->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR); - $this->container->get('request')->getSession()->remove(SecurityContext::AUTHENTICATION_ERROR); - } - - if ($error) { - // TODO: this is a potential security risk (see http://trac.symfony-project.org/ticket/9523) - $error = $error->getMessage(); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:Security:login.html.'.$this->getEngine(), array( - // last username entered by the user - 'last_username' => $this->container->get('request')->getSession()->get(SecurityContext::LAST_USERNAME), - 'error' => $error, - )); - } - - public function logoutAction() - { - throw new \RuntimeException('You must activate the logout in your security firewall configuration.'); - } - - protected function getEngine() - { - return $this->container->getParameter('fos_user.template.engine'); - } -} diff --git a/Controller/UserController.php b/Controller/UserController.php deleted file mode 100644 index f3a994574b..0000000000 --- a/Controller/UserController.php +++ /dev/null @@ -1,391 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Controller; - -use Symfony\Component\Security\Acl\Permission\MaskBuilder; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\DependencyInjection\ContainerAware; -use Symfony\Component\HttpFoundation\RedirectResponse; - -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; - -use FOS\UserBundle\Model\UserInterface; - -/** - * RESTful controller managing user CRUD - */ -class UserController extends ContainerAware -{ - /** - * Show all users - */ - public function listAction() - { - $users = $this->container->get('fos_user.user_manager')->findUsers(); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:list.html.'.$this->getEngine(), array('users' => $users)); - } - - /** - * Show one user - */ - public function showAction($username) - { - $user = $this->findUserBy('username', $username); - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:show.html.'.$this->getEngine(), array('user' => $user)); - } - - /** - * Edit one user, show the edit form - */ - public function editAction($username) - { - $user = $this->findUserBy('username', $username); - $form = $this->container->get('fos_user.form.user'); - - $form->process($user); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:edit.html.'.$this->getEngine(), array( - 'form' => $form, - 'username' => $user->getUsername() - )); - } - - /** - * Update a user - */ - public function updateAction($username) - { - $user = $this->findUserBy('username', $username); - $form = $this->container->get('fos_user.form.user'); - - $process = $form->process($user); - if ($process) { - $this->setFlash('fos_user_user_update', 'success'); - $userUrl = $this->container->get('router')->generate('fos_user_user_show', array('username' => $user->getUsername())); - return new RedirectResponse($userUrl); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:edit.html.'.$this->getEngine(), array( - 'form' => $form, - 'username' => $user->getUsername() - )); - } - - /** - * Show the new form - */ - public function newAction() - { - $form = $this->container->get('fos_user.form.user'); - - $form->process(); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:new.html.'.$this->getEngine(), array( - 'form' => $form - )); - } - - /** - * Create a user and send a confirmation email - */ - public function createAction() - { - $form = $this->container->get('fos_user.form.user'); - - $process = $form->process(null, $this->container->getParameter('fos_user.email.confirmation.enabled')); - if ($process) { - - $user = $form->getData(); - - if ($this->container->getParameter('fos_user.email.confirmation.enabled')) { - $this->container->get('fos_user.util.mailer')->sendConfirmationEmailMessage($user, $this->getEngine()); - $this->container->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail()); - $route = 'fos_user_user_check_confirmation_email'; - } else { - $this->authenticateUser($user); - $route = 'fos_user_user_confirmed'; - } - - if ($this->container->has('security.acl.provider')) { - $provider = $this->container->get('security.acl.provider'); - $acl = $provider->createAcl(ObjectIdentity::fromDomainObject($user)); - $acl->insertObjectAce(UserSecurityIdentity::fromAccount($user), MaskBuilder::MASK_OWNER); - $provider->updateAcl($acl); - } - - $this->setFlash('fos_user_user_create', 'success'); - $url = $this->container->get('router')->generate($route); - return new RedirectResponse($url); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:new.html.'.$this->getEngine(), array( - 'form' => $form - )); - } - - /** - * Tell the user to check his email provider - */ - public function checkConfirmationEmailAction() - { - $email = $this->container->get('session')->get('fos_user_send_confirmation_email/email'); - $this->container->get('session')->remove('fos_user_send_confirmation_email/email'); - $user = $this->findUserBy('email', $email); - - $this->setFlash('fos_user_user_confirm', 'success'); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:checkConfirmationEmail.html.'.$this->getEngine(), array( - 'user' => $user, - )); - } - - /** - * Receive the confirmation token from user email provider, login the user - */ - public function confirmAction($token) - { - $user = $this->findUserBy('confirmationToken', $token); - $user->setConfirmationToken(null); - $user->setEnabled(true); - - $this->container->get('fos_user.user_manager')->updateUser($user); - $this->authenticateUser($user); - - return new RedirectResponse( $this->container->get('router')->generate('fos_user_user_confirmed')); - } - - /** - * Tell the user his account is now confirmed - */ - public function confirmedAction() - { - $user = $this->getUser(); - - $this->setFlash('fos_user_user_confirmed', 'success'); - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:confirmed.html.'.$this->getEngine(), array( - 'user' => $user, - )); - } - - /** - * Delete one user - */ - public function deleteAction($username) - { - $user = $this->findUserBy('username', $username); - $this->container->get('fos_user.user_manager')->deleteUser($user); - $this->setFlash('fos_user_user_delete', 'success'); - - return new RedirectResponse( $this->container->get('router')->generate('fos_user_user_list')); - } - - /** - * Change user password: show form - */ - public function changePasswordAction() - { - $user = $this->getUser(); - $form = $this->container->get('fos_user.form.change_password'); - $form->process($user); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:changePassword.html.'.$this->getEngine(), array( - 'form' => $form - )); - } - - /** - * Change user password: submit form - */ - public function changePasswordUpdateAction() - { - $user = $this->getUser(); - $form = $this->container->get('fos_user.form.change_password'); - - $process = $form->process($user); - if ($process) { - $this->setFlash('fos_user_user_password', 'success'); - $url = $this->container->get('router')->generate('fos_user_user_show', array('username' => $user->getUsername())); - return new RedirectResponse($url); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:changePassword.html.'.$this->getEngine(), array( - 'form' => $form - )); - } - - /** - * Request reset user password: show form - */ - public function requestResetPasswordAction() - { - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:requestResetPassword.html.'.$this->getEngine()); - } - - /** - * Request reset user password: submit form and send email - */ - public function sendResettingEmailAction() - { - $user = $this->findUserBy('username', $this->container->get('request')->get('username')); - - if ($user->isPasswordRequestNonExpired($this->getPasswordRequestTtl())) { - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:passwordAlreadyRequested.html.'.$this->getEngine()); - } - - $user->generateConfirmationToken(); - $user->setPasswordRequestedAt(new \DateTime()); - $this->container->get('fos_user.user_manager')->updateUser($user); - $this->container->get('session')->set('fos_user_send_resetting_email/email', $user->getEmail()); - $this->container->get('fos_user.util.mailer')->sendResettingEmailMessage($user, $this->getEngine()); - - return new RedirectResponse( $this->container->get('router')->generate('fos_user_user_check_resetting_email')); - } - - /** - * Tell the user to check his email provider - */ - public function checkResettingEmailAction() - { - $email = $this->container->get('session')->get('fos_user_send_resetting_email/email'); - $this->container->get('session')->remove('fos_user_send_resetting_email/email'); - $user = $this->container->get('fos_user.user_manager')->findUserByEmail($email); - if (empty($user)) { - return new RedirectResponse( $this->container->get('router')->generate('fos_user_user_request_reset_password')); - } - - $this->setFlash('fos_user_user_reset', 'success'); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:checkResettingEmail.html.'.$this->getEngine(), array( - 'user' => $user, - )); - } - - /** - * Reset user password: show form - */ - public function resetPasswordAction($token) - { - $user = $this->findUserBy('confirmationToken', $token); - - if (!$user->isPasswordRequestNonExpired($this->getPasswordRequestTtl())) { - return new RedirectResponse( $this->container->get('router')->generate('fos_user_user_request_reset_password')); - } - - $form = $this->container->get('fos_user.form.reset_password'); - $form->process($user); - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:resetPassword.html.'.$this->getEngine(), array( - 'token' => $token, - 'form' => $form - )); - } - - /** - * Reset user password: submit form - */ - public function resetPasswordUpdateAction($token) - { - $user = $this->findUserBy('confirmationToken', $token); - - if (!$user->isPasswordRequestNonExpired($this->getPasswordRequestTtl())) { - return new RedirectResponse( $this->container->get('router')->generate('fos_user_user_request_reset_password')); - } - - $form = $this->container->get('fos_user.form.reset_password'); - - $process = $form->process($user); - if ($process) { - $this->authenticateUser($user); - - $this->setFlash('fos_user_user_resetted', 'success'); - $url = $this->container->get('router')->generate('fos_user_user_show', array('username' => $user->getUsername())); - return new RedirectResponse($url); - } - - return $this->container->get('templating')->renderResponse('FOSUserBundle:User:resetPassword.html.'.$this->getEngine(), array( - 'token' => $token, - 'form' => $form - )); - } - - /** - * Get a user from the security context - * - * @throws AccessDeniedException if no user is authenticated - * @return User - */ - protected function getUser() - { - $user = $this->container->get('security.context')->getToken()->getUser(); - if (!$user) { - throw new AccessDeniedException('A logged in user is required.'); - } - - return $user; - } - - /** - * Find a user by a specific property - * - * @param string $key property name - * @param mixed $value property value - * @throws NotFoundException if user does not exist - * @return User - */ - protected function findUserBy($key, $value) - { - if (!empty($value)) { - $user = $this->container->get('fos_user.user_manager')->{'findUserBy'.ucfirst($key)}($value); - } - - if (empty($user)) { - throw new NotFoundHttpException(sprintf('The user with "%s" does not exist for value "%s"', $key, $value)); - } - - return $user; - } - - /** - * Authenticate a user with Symfony Security - * - * @param Boolean $reAuthenticate - * @return null - */ - protected function authenticateUser(UserInterface $user, $reAuthenticate = false) - { - $providerKey = $this->container->getParameter('fos_user.provider_key'); - $token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles()); - - if (true === $reAuthenticate) { - $token->setAuthenticated(false); - } - - $this->container->get('security.context')->setToken($token); - } - - protected function setFlash($action, $value) - { - $this->container->get('session')->setFlash($action, $value); - } - - protected function getPasswordRequestTtl() - { - return $this->container->getParameter('fos_user.email.resetting_password.token_ttl'); - } - - protected function getEngine() - { - return $this->container->getParameter('fos_user.template.engine'); - } -} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php deleted file mode 100644 index 703d32caca..0000000000 --- a/DependencyInjection/Configuration.php +++ /dev/null @@ -1,186 +0,0 @@ - - */ -class Configuration -{ - /** - * Generates the configuration tree. - * - * @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface - */ - public function getConfigTree() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('fos_user', 'array'); - - $rootNode - ->scalarNode('db_driver')->cannotBeOverwritten()->isRequired()->cannotBeEmpty()->end() - ->scalarNode('provider_key')->isRequired()->cannotBeEmpty()->end(); - - $this->addClassSection($rootNode); - $this->addServiceSection($rootNode); - $this->addEncoderSection($rootNode); - $this->addFormNameSection($rootNode); - $this->addFormValidationGroupsSection($rootNode); - $this->addEmailSection($rootNode); - $this->addTemplateSection($rootNode); - - return $treeBuilder->buildTree(); - } - - private function addClassSection(NodeBuilder $node) - { - $node - ->arrayNode('class') - ->isRequired() - ->addDefaultsIfNotSet() - ->arrayNode('model') - ->isRequired() - ->scalarNode('user')->isRequired()->cannotBeEmpty()->end() - ->scalarNode('group')->isRequired()->cannotBeEmpty()->end() - ->end() - ->arrayNode('form') - ->addDefaultsIfNotSet() - ->scalarNode('user')->defaultValue('FOS\\UserBundle\\Form\\UserForm')->end() - ->scalarNode('group')->defaultValue('FOS\\UserBundle\\Form\\GroupForm')->end() - ->scalarNode('change_password')->defaultValue('FOS\\UserBundle\\Form\\ChangePasswordForm')->end() - ->scalarNode('reset_password')->defaultValue('FOS\\UserBundle\\Form\\ResetPasswordForm')->end() - ->end() - ->arrayNode('controller') - ->addDefaultsIfNotSet() - ->scalarNode('user')->defaultValue('FOS\\UserBundle\\Controller\\UserController')->end() - ->scalarNode('group')->defaultValue('FOS\\UserBundle\\Controller\\GroupController')->end() - ->scalarNode('security')->defaultValue('FOS\\UserBundle\\Controller\\SecurityController')->end() - ->end() - ->arrayNode('util') - ->addDefaultsIfNotSet() - ->scalarNode('email_canonicalizer')->defaultValue('FOS\\UserBundle\\Util\\Canonicalizer')->end() - ->scalarNode('username_canonicalizer')->defaultValue('FOS\\UserBundle\\Util\\Canonicalizer')->end() - ->end() - ->end(); - } - - private function addServiceSection(NodeBuilder $node) - { - $node - ->arrayNode('service') - ->arrayNode('util') - ->scalarNode('mailer')->end() - ->end() - ->end(); - } - - private function addEncoderSection(NodeBuilder $node) - { - $node - ->arrayNode('encoder') - ->addDefaultsIfNotSet() - ->scalarNode('algorithm')->defaultValue('sha512')->end() - ->booleanNode('encode_as_base64')->defaultFalse()->end() - ->scalarNode('iterations')->defaultValue(1)->end() - ->end(); - } - - private function addFormNameSection(NodeBuilder $node) - { - $node - ->arrayNode('form_name') - ->addDefaultsIfNotSet() - ->scalarNode('user') - ->defaultValue('fos_user_user_form') - ->cannotBeEmpty() - ->end() - ->scalarNode('group') - ->defaultValue('fos_user_group_form') - ->cannotBeEmpty() - ->end() - ->scalarNode('change_password') - ->defaultValue('fos_user_change_password_form') - ->cannotBeEmpty() - ->end() - ->scalarNode('reset_password') - ->defaultValue('fos_user_reset_password_form') - ->cannotBeEmpty() - ->end() - ->end(); - } - - private function addFormValidationGroupsSection(NodeBuilder $node) - { - $node - ->arrayNode('form_validation_groups') - ->addDefaultsIfNotSet() - ->arrayNode('user') - ->addDefaultsIfNotSet() - ->prototype('scalar')->end() - ->defaultValue(array('Registration')) - ->end() - ->arrayNode('change_password') - ->addDefaultsIfNotSet() - ->prototype('scalar')->end() - ->defaultValue(array('ChangePassword')) - ->end() - ->arrayNode('reset_password') - ->addDefaultsIfNotSet() - ->prototype('scalar')->end() - ->defaultValue(array('ResetPassword')) - ->end() - ->arrayNode('group') - ->addDefaultsIfNotSet() - ->prototype('scalar')->end() - ->defaultValue(array('Registration')) - ->end() - ->end(); - } - - private function addEmailSection(NodeBuilder $node) - { - $node - ->arrayNode('email') - ->addDefaultsIfNotSet() - ->arrayNode('from_email') - ->addDefaultsIfNotSet() - ->useAttributeAsKey('address') - ->prototype('scalar') - ->beforeNormalization() - ->ifTrue(function ($v) { return is_array($v) && isset ($v['name']); }) - ->then(function ($v) { return $v['name']; }) - ->end() - ->end() - ->defaultValue(array('webmaster@example.com' => 'webmaster')) - ->end() - ->arrayNode('confirmation') - ->addDefaultsIfNotSet() - ->booleanNode('enabled')->defaultFalse()->end() - ->scalarNode('template')->defaultValue('FOSUserBundle:User:confirmationEmail')->end() - ->end() - ->arrayNode('resetting_password') - ->addDefaultsIfNotSet() - ->scalarNode('template')->defaultValue('FOSUserBundle:User:resettingPasswordEmail')->end() - ->scalarNode('token_ttl')->defaultValue(86400)->end() - ->end() - ->end(); - } - - private function addTemplateSection(NodeBuilder $node) - { - $node - ->arrayNode('template') - ->addDefaultsIfNotSet() - ->scalarNode('engine')->defaultValue('twig')->end() - ->scalarNode('theme')->defaultValue('TwigBundle::form.html.twig')->end() - ->end(); - } -} diff --git a/DependencyInjection/FOSUserExtension.php b/DependencyInjection/FOSUserExtension.php deleted file mode 100644 index f9339b1d44..0000000000 --- a/DependencyInjection/FOSUserExtension.php +++ /dev/null @@ -1,90 +0,0 @@ -process($configuration->getConfigTree(), $configs); - - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - - if (!in_array(strtolower($config['db_driver']), array('orm', 'mongodb'))) { - throw new \InvalidArgumentException(sprintf('Invalid db driver "%s".', $config['db_driver'])); - } - $loader->load(sprintf('%s.xml', $config['db_driver'])); - - foreach (array('controller', 'templating', 'twig', 'form', 'validator', 'security', 'util', 'listener') as $basename) { - $loader->load(sprintf('%s.xml', $basename)); - } - - if (!empty($config['service']['util']['mailer'])) { - $container->setAlias('fos_user.util.mailer', $config['service']['util']['mailer']); - } - - $this->remapParametersNamespaces($config, $container, array( - '' => array( - 'provider_key' => 'fos_user.provider_key' - ), - 'encoder' => 'fos_user.encoder.%s', - 'template' => 'fos_user.template.%s', - 'form_name' => 'fos_user.form.%s.name', - 'form_validation_groups' => 'fos_user.form.%s.validation_groups', - )); - - $this->remapParametersNamespaces($config['class'], $container, array( - 'model' => 'fos_user.model.%s.class', - 'form' => 'fos_user.form.%s.class', - 'controller' => 'fos_user.controller.%s.class', - 'util' => 'fos_user.util.%s.class', - )); - - $this->remapParametersNamespaces($config['email'], $container, array( - '' => array('from_email' => 'fos_user.email.from_email'), - 'confirmation' => 'fos_user.email.confirmation.%s', - 'resetting_password' => 'fos_user.email.resetting_password.%s', - )); - } - - protected function remapParameters(array $config, ContainerBuilder $container, array $map) - { - foreach ($map as $name => $paramName) { - if (isset($config[$name])) { - $container->setParameter($paramName, $config[$name]); - } - } - } - - protected function remapParametersNamespaces(array $config, ContainerBuilder $container, array $namespaces) - { - foreach ($namespaces as $ns => $map) { - if ($ns) { - if (!isset($config[$ns])) { - continue; - } - $namespaceConfig = $config[$ns]; - } else { - $namespaceConfig = $config; - } - if (is_array($map)) { - $this->remapParameters($namespaceConfig, $container, $map); - } else { - foreach ($namespaceConfig as $name => $value) { - if (null !== $value) { - $container->setParameter(sprintf($map, $name), $value); - } - } - } - } - } -} diff --git a/Document/DefaultGroup.php b/Document/DefaultGroup.php deleted file mode 100644 index b3a799cfe9..0000000000 --- a/Document/DefaultGroup.php +++ /dev/null @@ -1,9 +0,0 @@ -dm = $dm; - $this->repository = $dm->getRepository($class); - - $metadata = $dm->getClassMetadata($class); - $this->class = $metadata->name; - } - - /** - * {@inheritDoc} - */ - public function deleteGroup(GroupInterface $group) - { - $this->dm->remove($group); - $this->dm->flush(); - } - - /** - * {@inheritDoc} - */ - public function getClass() - { - return $this->class; - } - - /** - * {@inheritDoc} - */ - public function findGroupBy(array $criteria) - { - return $this->repository->findOneBy($criteria); - } - - /** - * {@inheritDoc} - */ - public function findGroups() - { - return $this->repository->findAll(); - } - - /** - * {@inheritDoc} - */ - public function updateGroup(GroupInterface $group) - { - $this->dm->persist($group); - $this->dm->flush(); - } -} diff --git a/Document/User.php b/Document/User.php deleted file mode 100644 index bad1bb1590..0000000000 --- a/Document/User.php +++ /dev/null @@ -1,18 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Document; - -use FOS\UserBundle\Model\User as AbstractUser; - -abstract class User extends AbstractUser -{ - -} diff --git a/Document/UserManager.php b/Document/UserManager.php deleted file mode 100644 index 69a2d96109..0000000000 --- a/Document/UserManager.php +++ /dev/null @@ -1,148 +0,0 @@ -dm = $dm; - $this->repository = $dm->getRepository($class); - - $metadata = $dm->getClassMetadata($class); - $this->class = $metadata->name; - } - - /** - * {@inheritDoc} - */ - public function deleteUser(UserInterface $user) - { - $this->dm->remove($user); - $this->dm->flush(); - } - - /** - * {@inheritDoc} - */ - public function getClass() - { - return $this->class; - } - - /** - * {@inheritDoc} - */ - public function findUserBy(array $criteria) - { - return $this->repository->findOneBy($criteria); - } - - /** - * {@inheritDoc} - */ - public function findUsers() - { - return $this->repository->findAll(); - } - - /** - * {@inheritDoc} - */ - public function updateUser(UserInterface $user) - { - $this->updateCanonicalFields($user); - $this->updatePassword($user); - - $this->dm->persist($user); - $this->dm->flush(); - } - - /** - * {@inheritDoc} - */ - public function validateUnique($value, Constraint $constraint) - { - $classMetadata = $this->dm->getClassMetadata($this->class); - // TODO: ODM seems to be missing handling for multiple properties - // $fields = array_map('trim', explode(',', $constraint->property)); - $query = $this->getQueryArray($classMetadata, $value, $constraint->property); - - $document = $this->findUserBy($query); - if (null === $document) { - return true; - } - - // check if document in mongodb is the same document as the checked one - if ($document->isUser($value)) { - return true; - } - // check if returned document is proxy and initialize the minimum identifier if needed - if ($document instanceof Proxy) { - $classMetadata->setIdentifierValue($document, $document->__identifier); - } - // check if document has the same identifier as the current one - if ($classMetadata->getIdentifierValue($document) === $classMetadata->getIdentifierValue($value)) { - return true; - } - - return false; - } - - protected function getQueryArray($classMetadata, $value, $fieldName) - { - $field = $this->getFieldNameFromPropertyPath($fieldName); - if (!isset($classMetadata->fieldMappings[$field])) { - throw new \LogicException("Mapping for '$fieldName' doesn't exist for " . $this->class); - } - - $mapping = $classMetadata->fieldMappings[$field]; - if (isset($mapping['reference']) && $mapping['reference']) { - throw new \LogicException('Cannot determine uniqueness of referenced document values'); - } - - $criteria[$field] = $value instanceOf UserInterface ? $classMetadata->getFieldValue($value, $field) : $value; - - return $criteria; - } - - /** - * Returns the actual document field value - * - * E.g. document.someVal -> document - * user.emails -> user - * username -> username - * - * @param string $field - * @return string - */ - protected function getFieldNameFromPropertyPath($field) - { - $pieces = explode('.', $field); - return $pieces[0]; - } -} diff --git a/Entity/DefaultGroup.php b/Entity/DefaultGroup.php deleted file mode 100644 index 6864af1fec..0000000000 --- a/Entity/DefaultGroup.php +++ /dev/null @@ -1,9 +0,0 @@ -em = $em; - $this->repository = $em->getRepository($class); - - $metadata = $em->getClassMetadata($class); - $this->class = $metadata->name; - } - - /** - * {@inheritDoc} - */ - public function deleteGroup(GroupInterface $group) - { - $this->em->remove($group); - $this->em->flush(); - } - - /** - * {@inheritDoc} - */ - public function getClass() - { - return $this->class; - } - - /** - * {@inheritDoc} - */ - public function findGroupBy(array $criteria) - { - return $this->repository->findOneBy($criteria); - } - - /** - * {@inheritDoc} - */ - public function findGroups() - { - return $this->repository->findAll(); - } - - /** - * {@inheritDoc} - */ - public function updateGroup(GroupInterface $group) - { - $this->em->persist($group); - $this->em->flush(); - } -} diff --git a/Entity/User.php b/Entity/User.php deleted file mode 100644 index 9eccd831d2..0000000000 --- a/Entity/User.php +++ /dev/null @@ -1,18 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Entity; - -use FOS\UserBundle\Model\User as AbstractUser; - -abstract class User extends AbstractUser -{ - -} diff --git a/Entity/UserManager.php b/Entity/UserManager.php deleted file mode 100644 index ab84dc1491..0000000000 --- a/Entity/UserManager.php +++ /dev/null @@ -1,157 +0,0 @@ -em = $em; - $this->repository = $em->getRepository($class); - - $metadata = $em->getClassMetadata($class); - $this->class = $metadata->name; - } - - /** - * {@inheritDoc} - */ - public function deleteUser(UserInterface $user) - { - $this->em->remove($user); - $this->em->flush(); - } - - /** - * {@inheritDoc} - */ - public function getClass() - { - return $this->class; - } - - /** - * {@inheritDoc} - */ - public function findUserBy(array $criteria) - { - return $this->repository->findOneBy($criteria); - } - - /** - * {@inheritDoc} - */ - public function findUsers() - { - return $this->repository->findAll(); - } - - /** - * {@inheritDoc} - */ - public function updateUser(UserInterface $user) - { - $this->updateCanonicalFields($user); - $this->updatePassword($user); - - $this->em->persist($user); - $this->em->flush(); - } - - /** - * {@inheritDoc} - */ - public function validateUnique($value, Constraint $constraint) - { - $fields = array_map('trim', explode(',', $constraint->property)); - $users = $this->findConflictualUsers($value, $fields); - - // there is no conflictual user - if (empty($users)) { - return true; - } - - // there is no conflictual user which is not the same as the value - if ($this->anyIsUser($value, $users)) { - return true; - } - - return false; - } - - /** - * Indicates whether the given user and all compared objects correspond to the same record - * - * @param UserInterface $user - * @param array $comparisons - * @return boolean - */ - protected function anyIsUser($user, array $comparisons) - { - foreach ($comparisons as $comparison) { - if (!$user->isUser($comparison)) { - return false; - } - } - - return true; - } - - /** - * Gets conflictual users for the given user and constraint - * - * @param UserInterface $value - * @param array $fields - * @return array - */ - protected function findConflictualUsers($value, array $fields) - { - return $this->repository->findBy($this->getCriteria($value, $fields)); - } - - /** - * Gets the criteria used to find conflictual entities - * - * @param UserInterface $value - * @param array $constraint - * @return array - */ - protected function getCriteria($value, array $fields) - { - $classMetadata = $this->em->getClassMetadata($this->class); - - $criteria = array(); - foreach ($fields as $field) { - if (!$classMetadata->hasField($field)) { - throw new \InvalidArgumentException(sprintf('The "%s" class metadata does not have any "%s" field or association mapping.', $this->class, $field)); - } - - $criteria[$field] = $classMetadata->getFieldValue($value, $field); - } - - return $criteria; - } -} diff --git a/FOSUserBundle.php b/FOSUserBundle.php deleted file mode 100644 index 9e02296e7e..0000000000 --- a/FOSUserBundle.php +++ /dev/null @@ -1,17 +0,0 @@ - - * (c) Thibault Duplessis - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle; - -use Symfony\Component\HttpKernel\Bundle\Bundle; - -class FOSUserBundle extends Bundle -{ -} diff --git a/Form/ChangePassword.php b/Form/ChangePassword.php deleted file mode 100644 index 14eb9f81b8..0000000000 --- a/Form/ChangePassword.php +++ /dev/null @@ -1,12 +0,0 @@ -add(new PasswordField('current')); - parent::configure(); - } - - public function process(UserInterface $user) - { - $this->setData(new ChangePassword($user)); - - if ('POST' == $this->request->getMethod()) { - $this->bind($this->request); - - if ($this->isValid()) { - $user->setPlainPassword($this->getNewPassword()); - $this->userManager->updateUser($user); - - return true; - } - } - - return false; - } -} diff --git a/Form/GroupForm.php b/Form/GroupForm.php deleted file mode 100644 index f709ab95b8..0000000000 --- a/Form/GroupForm.php +++ /dev/null @@ -1,69 +0,0 @@ -addOption('theme'); - - parent::__construct($name, $options); - } - - public function setRequest(Request $request) - { - $this->request = $request; - } - - public function setGroupManager(GroupManagerInterface $groupManager) - { - $this->groupManager = $groupManager; - } - - public function configure() - { - $this->add(new TextField('name')); - } - - public function process(GroupInterface $group = null) - { - if (null === $group) { - $group = $this->groupManager->createGroup(''); - } - - $this->setData($group); - - if ('POST' == $this->request->getMethod()) { - $this->bind($this->request); - - if ($this->isValid()) { - $this->groupManager->updateGroup($group); - return true; - } - } - - return false; - } -} diff --git a/Form/ResetPassword.php b/Form/ResetPassword.php deleted file mode 100644 index 3e21c2d80c..0000000000 --- a/Form/ResetPassword.php +++ /dev/null @@ -1,24 +0,0 @@ -user = $user; - } -} diff --git a/Form/ResetPasswordForm.php b/Form/ResetPasswordForm.php deleted file mode 100644 index 4086f38f32..0000000000 --- a/Form/ResetPasswordForm.php +++ /dev/null @@ -1,75 +0,0 @@ -addOption('theme'); - - parent::__construct($name, $options); - } - - public function setRequest(Request $request) - { - $this->request = $request; - } - - public function setUserManager(UserManagerInterface $userManager) - { - $this->userManager = $userManager; - } - - public function configure() - { - $this->add(new RepeatedField(new PasswordField('new'))); - } - - public function getNewPassword() - { - return $this->getData()->new; - } - - public function process(UserInterface $user) - { - $this->setData(new ResetPassword($user)); - - if ('POST' == $this->request->getMethod()) { - $this->bind($this->request); - - if ($this->isValid()) { - $user->setPlainPassword($this->getNewPassword()); - $user->setConfirmationToken(null); - $user->setEnabled(true); - $this->userManager->updateUser($user); - - return true; - } - } - - return false; - } -} diff --git a/Form/UserForm.php b/Form/UserForm.php deleted file mode 100644 index f86481d12a..0000000000 --- a/Form/UserForm.php +++ /dev/null @@ -1,103 +0,0 @@ -addOption('theme'); - - parent::__construct($title, $options); - } - - public function setRequest(Request $request) - { - $this->request = $request; - } - - public function setUserManager(UserManagerInterface $userManager) - { - $this->userManager = $userManager; - } - - public function configure() - { - $this->add(new TextField('username')); - $this->add(new TextField('email')); - $this->add(new RepeatedField(new PasswordField('plainPassword'))); - } - - public function bind(Request $request, $data = null) - { - if (!$this->getName()) { - throw new FormException('You cannot bind anonymous forms. Please give this form a name'); - } - - // Store object from which to read the default values and where to - // write the submitted values - if (null !== $data) { - $this->setData($data); - } - - // Store the submitted data in case of a post request - if ('POST' == $request->getMethod()) { - $values = $request->request->get($this->getName(), array()); - $files = $request->files->get($this->getName(), array()); - - $this->submit(self::deepArrayUnion($values, $files)); - - $this->userManager->updateCanonicalFields($this->getData()); - - $this->validate(); - } - } - - public function process(UserInterface $user = null, $confirmation = null) - { - if (null === $user) { - $user = $this->userManager->createUser(); - } - - $this->setData($user); - - if ('POST' == $this->request->getMethod()) { - $this->bind($this->request); - - if ($this->isValid()) { - if (true === $confirmation) { - $user->setEnabled(false); - } else if (false === $confirmation) { - $user->setConfirmationToken(null); - $user->setEnabled(true); - } - - $this->userManager->updateUser($user); - - return true; - } - } - - return false; - } -} diff --git a/Resources/meta/LICENSE b/LICENSE similarity index 96% rename from Resources/meta/LICENSE rename to LICENSE index 8183c43e70..8a55c7f720 100644 --- a/Resources/meta/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010 knpLabs +Copyright (c) 2010-2016 FriendsOfSymfony Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..c150f9886b --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +cs: + ./vendor/bin/php-cs-fixer fix --verbose + +cs_dry_run: + ./vendor/bin/php-cs-fixer fix --verbose --dry-run + +test: + ./vendor/bin/phpunit --coverage-text diff --git a/Model/Group.php b/Model/Group.php deleted file mode 100644 index dced156d98..0000000000 --- a/Model/Group.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Model; - -abstract class Group implements GroupInterface -{ - protected $id; - protected $name; - protected $roles; - - public function __construct($name, $roles = array()) - { - $this->name = $name; - $this->roles = $roles; - } - - public function addRole($role) - { - if (!$this->hasRole($role)) { - $this->roles[] = strtoupper($role); - } - } - - public function getId() - { - return $this->id; - } - - public function getName() - { - return $this->name; - } - - public function hasRole($role) - { - return in_array(strtoupper($role), $this->roles, true); - } - - public function getRoles() - { - return $this->roles; - } - - public function removeRole($role) - { - if (false !== $key = array_search(strtoupper($role), $this->roles, true)) { - unset($this->roles[$key]); - $this->roles = array_values($this->roles); - } - } - - public function setName($name) - { - $this->name = $name; - } - - public function setRoles(array $roles) - { - $this->roles = $roles; - } -} \ No newline at end of file diff --git a/Model/GroupInterface.php b/Model/GroupInterface.php deleted file mode 100644 index 73a42a5246..0000000000 --- a/Model/GroupInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * (c) Christophe Coevoet - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Model; - -interface GroupInterface -{ - function addRole($role); - - function getId(); - - function getName(); - - function hasRole($role); - - function getRoles(); - - function removeRole($role); - - function setName($name); - - function setRoles(array $roles); -} \ No newline at end of file diff --git a/Model/GroupManager.php b/Model/GroupManager.php deleted file mode 100644 index 7206c86231..0000000000 --- a/Model/GroupManager.php +++ /dev/null @@ -1,35 +0,0 @@ - - */ -abstract class GroupManager implements GroupManagerInterface -{ - /** - * Returns an empty group instance - * - * @param string $name - * @return Group - */ - public function createGroup($name) - { - $class = $this->getClass(); - - return new $class($name); - } - /** - * Finds a group by name - * - * @param string $name - * @return GroupInterface - */ - public function findGroupByName($name) - { - return $this->findGroupBy(array('name' => $name)); - } -} diff --git a/Model/GroupManagerInterface.php b/Model/GroupManagerInterface.php deleted file mode 100644 index 844fcaec97..0000000000 --- a/Model/GroupManagerInterface.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Model; - -/** - * Interface to be implemented by group managers. This adds an additional level - * of abstraction between your application, and the actual repository. - * - * All changes to groups should happen through this interface. - * - * @author Christophe Coevoet - */ -interface GroupManagerInterface -{ - /** - * Returns an empty group instance - * - * @param string $name - * @return GroupInterface - */ - function createGroup($name); - - /** - * Deletes a group - * - * @param GroupInterface $group - * @return void - */ - function deleteGroup(GroupInterface $group); - - /** - * Finds one group by the given criteria - * - * @param array $criteria - * @return GroupInterface - */ - function findGroupBy(array $criteria); - - /** - * Finds a group by name - * - * @param string $name - * @return GroupInterface - */ - function findGroupByName($name); - - /** - * Returns a collection with all user instances - * - * @return Traversable - */ - function findGroups(); - - /** - * Returns the group's fully qualified class name - * - * @return string - */ - function getClass(); - - /** - * Updates a group - * - * @param GroupInterface $group - * @return void - */ - function updateGroup(GroupInterface $group); -} diff --git a/Model/User.php b/Model/User.php deleted file mode 100644 index d9bfcfe6d9..0000000000 --- a/Model/User.php +++ /dev/null @@ -1,746 +0,0 @@ - - * (c) Johannes M. Schmitt - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Model; - -use Symfony\Component\Security\Core\Role\RoleInterface; -use Doctrine\Common\Collections\Collection; -use Doctrine\Common\Collections\ArrayCollection; -use Symfony\Component\Security\Core\User\AccountInterface; -use Symfony\Component\Security\Core\User\AdvancedAccountInterface; -use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; - -/** - * Storage agnostic user object - * Has validator annotation, but database mapping must be done in a subclass. - * - */ -abstract class User implements UserInterface -{ - const ROLE_DEFAULT = 'ROLE_USER'; - const ROLE_SUPERADMIN = 'ROLE_SUPERADMIN'; - - protected $id; - - /** - * @var string - */ - protected $username; - - /** - * @var string - */ - protected $usernameCanonical; - - /** - * @var string - */ - protected $email; - - /** - * @var string - */ - protected $emailCanonical; - - /** - * @var boolean - */ - protected $enabled; - - /** - * The algorithm to use for hashing - * - * @var string - */ - protected $algorithm; - - /** - * The salt to use for hashing - * - * @var string - */ - protected $salt; - - /** - * Encrypted password. Must be persisted. - * - * @var string - */ - protected $password; - - /** - * Plain password. Used for model validation. Must not be persisted. - * - * @var string - */ - protected $plainPassword; - - /** - * @var \DateTime - */ - protected $createdAt; - - /** - * @var \DateTime - */ - protected $updatedAt; - - /** - * @var \DateTime - */ - protected $lastLogin; - - /** - * Random string sent to the user email address in order to verify it - * - * @var string - */ - protected $confirmationToken; - - /** - * @var \DateTime - */ - protected $passwordRequestedAt; - - /** - * @var Collection - */ - protected $groups; - - /** - * @var boolean - */ - protected $locked; - - /** - * @var boolean - */ - protected $expired; - - /** - * @var DateTime - */ - protected $expiresAt; - - /** - * @var array - */ - protected $roles; - - /** - * @var boolean - */ - protected $credentialsExpired; - - /** - * @var DateTime - */ - protected $credentialsExpireAt; - - public function __construct() - { - $this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36); - $this->generateConfirmationToken(); - $this->enabled = false; - $this->locked = false; - $this->expired = false; - $this->roles = array(); - $this->credentialsExpired = false; - } - - public function addRole($role) - { - $role = strtoupper($role); - if ($role === self::ROLE_DEFAULT) { - return; - } - - if (!in_array($role, $this->roles, true)) { - $this->roles[] = $role; - } - } - - /** - * Implementation of AccountInterface. - * - * @param AccountInterface $account - * @return boolean - */ - public function equals(AccountInterface $account) - { - if (!$account instanceof User) { - return false; - } - - if ($this->password !== $account->getPassword()) { - return false; - } - if ($this->getSalt() !== $account->getSalt()) { - return false; - } - if ($this->usernameCanonical !== $account->getUsernameCanonical()) { - return false; - } - if ($this->isAccountNonExpired() !== $account->isAccountNonExpired()) { - return false; - } - if (!$this->locked !== $account->isAccountNonLocked()) { - return false; - } - if ($this->isCredentialsNonExpired() !== $account->isCredentialsNonExpired()) { - return false; - } - if ($this->enabled !== $account->isEnabled()) { - return false; - } - - return true; - } - - /** - * Removes sensitive data from the user. - * Implements AccountInterface - */ - public function eraseCredentials() - { - $this->plainPassword = null; - } - - /** - * Return the user unique id - * - * @return mixed - */ - public function getId() - { - return $this->id; - } - - /** - * @return string - */ - public function getUsername() - { - return $this->username; - } - - /** - * Get the canonical username in search and sort queries - * - * @return string - **/ - public function getUsernameCanonical() - { - return $this->usernameCanonical; - } - - /** - * Implementation of AccountInterface - * @return string - */ - public function getSalt() - { - return $this->salt; - } - - public function getAlgorithm() - { - return $this->algorithm; - } - - /** - * Get email - * @return string - */ - public function getEmail() - { - return $this->email; - } - - /** - * Get the canonical email in search and sort queries - * - * @return string - **/ - public function getEmailCanonical() - { - return $this->emailCanonical; - } - - /** - * Implements AccountInterface - * Get the encrypted password - * @return string - */ - public function getPassword() - { - return $this->password; - } - - public function getPlainPassword() - { - return $this->plainPassword; - } - - /** - * @return \DateTime - */ - public function getCreatedAt() - { - return $this->createdAt; - } - - /** - * @return \DateTime - */ - public function getUpdatedAt() - { - return $this->updatedAt; - } - - /** - * @return \DateTime - */ - public function getLastLogin() - { - return $this->lastLogin; - } - - /** - * Get confirmationToken - * @return string - */ - public function getConfirmationToken() - { - return $this->confirmationToken; - } - - /** - * Return the user roles - * Implements AccountInterface - * - * @return array The roles - **/ - public function getRoles() - { - $roles = $this->roles; - - foreach ($this->getGroups() as $group) { - $roles = array_merge($roles, $group->getRoles()); - } - - // we need to make sure to have at least one role - $roles[] = self::ROLE_DEFAULT; - - return array_unique($roles); - } - - /** - * Never use this to check if this user has access to anything! - * - * Use the SecurityContext, or an implementation of AccessDecisionManager - * instead, e.g. - * - * $securityContext->vote('ROLE_USER'); - * - * @param string $role - * @return void - */ - public function hasRole($role) - { - return in_array(strtoupper($role), $this->getRoles(), true); - } - - /** - * Checks whether the user's account has expired. - * Implements AdvancedAccountInterface - * - * @return Boolean true if the user's account is non expired, false otherwise - */ - public function isAccountNonExpired() - { - if (true === $this->expired) { - return false; - } - - if (null !== $this->expiresAt && $this->expiresAt->getTimestamp() < time()) { - return false; - } - - return true; - } - - /** - * Checks whether the user is locked. - * Implements AdvancedAccountInterface - * - * @return Boolean true if the user is not locked, false otherwise - */ - public function isAccountNonLocked() - { - return !$this->locked; - } - - /** - * Checks whether the user's credentials (password) has expired. - * Implements AdvancedAccountInterface - * - * @return Boolean true if the user's credentials are non expired, false otherwise - */ - public function isCredentialsNonExpired() - { - if (true === $this->credentialsExpired) { - return false; - } - - if (null !== $this->credentialsExpireAt && $this->credentialsExpireAt->getTimestamp() < time()) { - return false; - } - - return true; - } - - public function isCredentialsExpired() - { - return !$this->isCredentialsNonExpired(); - } - - /** - * Checks whether the user is enabled. - * Implements AdvancedAccountInterface - * - * @return Boolean true if the user is enabled, false otherwise - */ - public function isEnabled() - { - return $this->enabled; - } - - public function isExpired() - { - return !$this->isAccountNonExpired(); - } - - public function isLocked() - { - return $this->locked; - } - - /** - * Tell if the the given user has the super admin role - * - * @return Boolean - */ - public function isSuperAdmin() - { - return $this->hasRole(self::ROLE_SUPERADMIN); - } - - /** - * Tell if the the given user is this user - * Useful when not hydrating all fields. - * - * @param User $user - * @return boolean - */ - public function isUser(UserInterface $user = null) - { - return null !== $user && $this->getId() === $user->getId(); - } - - public function incrementCreatedAt() - { - if (null === $this->createdAt) { - $this->createdAt = new \DateTime(); - } - $this->updatedAt = new \DateTime(); - } - - public function incrementUpdatedAt() - { - $this->updatedAt = new \DateTime(); - } - - public function removeRole($role) - { - if (false !== $key = array_search(strtoupper($role), $this->roles, true)) { - unset($this->roles[$key]); - $this->roles = array_values($this->roles); - } - } - - /** - * Set username. - * - * @param string $username - */ - public function setUsername($username) - { - $this->username = $username; - } - - /** - * Set usernameCanonical. - * - * @param string $usernameCanonical - */ - public function setUsernameCanonical($usernameCanonical) - { - $this->usernameCanonical = $usernameCanonical; - } - - public function setAlgorithm($algorithm) - { - $this->algorithm = $algorithm; - } - - public function setCredentialsExpireAt(\DateTime $date) - { - $this->credentialsExpireAt = $date; - } - - public function setCredentialsExpired($boolean) - { - $this->credentialsExpired = $boolean; - } - - /** - * Set email. - * - * @param string $email - */ - public function setEmail($email) - { - $this->email = $email; - } - - /** - * Set emailCanonical. - * - * @param string $emailCanonical - */ - public function setEmailCanonical($emailCanonical) - { - $this->emailCanonical = $emailCanonical; - } - - /** - * @param bool $boolean - */ - public function setEnabled($boolean) - { - $this->enabled = $boolean; - } - - /** - * Sets this user to expired - * - * @param boolean $boolean - * @return void - */ - public function setExpired($boolean) - { - $this->expired = $boolean; - } - - public function setExpiresAt(\DateTime $date) - { - $this->expiresAt = $date; - } - - /** - * Sets the hashed password. - * - * @param string $password - * @return void - */ - public function setPassword($password) - { - $this->password = $password; - } - - /** - * Sets the super admin status - * - * @param boolean $boolean - * @return void - */ - public function setSuperAdmin($boolean) - { - if (true === $boolean) { - $this->addRole(self::ROLE_SUPERADMIN); - } else { - $this->removeRole(self::ROLE_SUPERADMIN); - } - } - - public function setPlainPassword($password) - { - $this->plainPassword = $password; - } - - /** - * @param \DateTime $time - */ - public function setLastLogin(\DateTime $time) - { - $this->lastLogin = $time; - } - - public function setLocked($boolean) - { - $this->locked = $boolean; - } - - /** - * Set confirmationToken - * - * @param string - * @return null - */ - public function setConfirmationToken($confirmationToken) - { - $this->confirmationToken = $confirmationToken; - } - - /** - * Set the timestamp that the user requested a password reset. - * - * @param DateTime $date - */ - public function setPasswordRequestedAt(\DateTime $date) - { - $this->passwordRequestedAt = $date; - } - - /** - * Get the timestamp that the user requested a password reset. - * - * @return DateTime - */ - public function getPasswordRequestedAt() - { - return $this->passwordRequestedAt; - } - - /** - * Checks whether the password reset request has expired. - * - * @param integer $ttl Requests older than this many seconds will be considered expired - * @return boolean true if the users's password request is non expired, false otherwise - */ - public function isPasswordRequestNonExpired($ttl) - { - return $this->passwordRequestedAt instanceof \DateTime && - $this->passwordRequestedAt->getTimestamp() + $ttl > time(); - } - - /** - * Generate confirmationToken if it is not set - * - * @return null - */ - public function generateConfirmationToken() - { - if (null === $this->confirmationToken) { - $bytes = false; - if (function_exists('openssl_random_pseudo_bytes') && 0 !== stripos(PHP_OS, 'win')) { - $bytes = openssl_random_pseudo_bytes(32, $strong); - - if (true !== $strong) { - $bytes = false; - } - } - - // let's just hope we got a good seed - if (false === $bytes) { - $bytes = hash('sha256', uniqid(mt_rand(), true), true); - } - - $this->confirmationToken = base_convert(bin2hex($bytes), 16, 36); - } - } - - public function setRoles(array $roles) - { - $this->roles = array(); - - foreach ($roles as $role) { - $this->addRole($role); - } - } - - /** - * Get groups granted to the user - * - * @return Collection - */ - public function getGroups() - { - return $this->groups ?: $this->groups = new ArrayCollection(); - } - - /** - * Gets the name of the groups which includes the user - * - * @return array - */ - public function getGroupNames() - { - $names = array(); - foreach ($this->getGroups() as $group) { - $names[] = $group->getName(); - } - - return $names; - } - - /** - * Indicates whether the user belongs to the specified group or not - * - * @param string $name Name of the group - * @return boolean - */ - public function hasGroup($name) - { - return in_array($name, $this->getGroupNames()); - } - - /** - * Add a group to the user groups - * - * @param GroupInterface $group - * @return null - **/ - public function addGroup(GroupInterface $group) - { - if (!$this->getGroups()->contains($group)) { - $this->getGroups()->add($group); - } - } - - /** - * Remove a group from the user groups - * - * @param GroupInterface $group - * @return null - **/ - public function removeGroup(GroupInterface $group) - { - if ($this->getGroups()->contains($group)) { - $this->getGroups()->remove($group); - } - } - - public function __toString() - { - return (string) $this->getUsername(); - } -} diff --git a/Model/UserInterface.php b/Model/UserInterface.php deleted file mode 100644 index 45aaa5db97..0000000000 --- a/Model/UserInterface.php +++ /dev/null @@ -1,70 +0,0 @@ - - * (c) Johannes M. Schmitt - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Model; - -use Symfony\Component\Security\Core\User\AdvancedAccountInterface; - -interface UserInterface extends AdvancedAccountInterface -{ - function addRole($role); - - function getAlgorithm(); - - function setAlgorithm($algorithm); - - function setUsername($username); - - function getUsernameCanonical(); - - function setUsernameCanonical($usernameCanonical); - - function getEmail(); - - function setEmail($email); - - function getEmailCanonical(); - - function setEmailCanonical($emailCanonical); - - function getPlainPassword(); - - function setPlainPassword($password); - - function setPassword($password); - - function isSuperAdmin(); - - function isUser(UserInterface $user = null); - - function setEnabled($boolean); - - function setSuperAdmin($boolean); - - function getConfirmationToken(); - - function setConfirmationToken($confirmationToken); - - function hasRole($role); - - function setRoles(array $roles); - - function removeRole($role); - - function getGroups(); - - function getGroupNames(); - - function hasGroup($name); - - function addGroup(GroupInterface $group); - - function removeGroup(GroupInterface $group); -} diff --git a/Model/UserManager.php b/Model/UserManager.php deleted file mode 100644 index 686c117a46..0000000000 --- a/Model/UserManager.php +++ /dev/null @@ -1,170 +0,0 @@ - - */ -abstract class UserManager implements UserManagerInterface, UserProviderInterface -{ - protected $encoderFactory; - protected $algorithm; - protected $usernameCanonicalizer; - protected $emailCanonicalizer; - - /** - * Constructor. - * - * @param EncoderFactoryInterface $encoderFactory - * @param string $algorithm - * @param CanonicalizerInterface $usernameCanonicalizer - * @param CanonicalizerInterface $emailCanonicalizer - */ - public function __construct(EncoderFactoryInterface $encoderFactory, $algorithm, CanonicalizerInterface $usernameCanonicalizer, CanonicalizerInterface $emailCanonicalizer) - { - $this->encoderFactory = $encoderFactory; - $this->algorithm = $algorithm; - $this->usernameCanonicalizer = $usernameCanonicalizer; - $this->emailCanonicalizer = $emailCanonicalizer; - } - - /** - * Returns an empty user instance - * - * @return User - */ - public function createUser() - { - $class = $this->getClass(); - $user = new $class; - $user->setAlgorithm($this->algorithm); - - return $user; - } - - /** - * Finds a user by email - * - * @param string $email - * @return User - */ - public function findUserByEmail($email) - { - return $this->findUserBy(array('emailCanonical' => $this->emailCanonicalizer->canonicalize($email))); - } - - /** - * Finds a user by username - * - * @param string $username - * @return User - */ - public function findUserByUsername($username) - { - return $this->findUserBy(array('usernameCanonical' => $this->usernameCanonicalizer->canonicalize($username))); - } - - /** - * Finds a user either by email, or username - * - * @param string $usernameOrEmail - * @return User - */ - public function findUserByUsernameOrEmail($usernameOrEmail) - { - if (filter_var($usernameOrEmail, FILTER_VALIDATE_EMAIL)) { - return $this->findUserByEmail($usernameOrEmail); - } - - return $this->findUserByUsername($usernameOrEmail); - } - - public function findUserByConfirmationToken($token) - { - return $this->findUserBy(array('confirmationToken' => $token)); - } - - /** - * Finds a user by account - * - * It is strongly discouraged to use this method manually as it bypasses - * all ACL checks. - * - * @param AccountInterface $user - * @return User - */ - public function loadUserByAccount(AccountInterface $user) - { - if (!$user instanceof User) { - throw new UnsupportedAccountException('Account is not supported.'); - } - - return $this->loadUserByUsername($user->getUsername()); - } - - /** - * Loads a user by username - * - * It is strongly discouraged to call this method manually as it bypasses - * all ACL checks. - * - * @RunAs(roles="ROLE_SUPERADMIN") - * @param string $username - * @return AccountInterface - */ - public function loadUserByUsername($username) - { - $user = $this->findUserByUsername($username); - - if (!$user) { - throw new UsernameNotFoundException(sprintf('No user with name "%s" was found.', $username)); - } - - return $user; - } - - /** - * {@inheritDoc} - */ - public function updateCanonicalFields(UserInterface $user) - { - $user->setUsernameCanonical($this->usernameCanonicalizer->canonicalize($user->getUsername())); - $user->setEmailCanonical($this->emailCanonicalizer->canonicalize($user->getEmail())); - } - - /** - * {@inheritDoc} - */ - public function updatePassword(UserInterface $user) - { - if (0 !== strlen($password = $user->getPlainPassword())) { - $user->setAlgorithm($this->algorithm); - $encoder = $this->getEncoder($user); - $user->setPassword($encoder->encodePassword($password, $user->getSalt())); - $user->eraseCredentials(); - } - } - - protected function getEncoder(UserInterface $user) - { - return $this->encoderFactory->getEncoder($user); - } - - /** - * {@inheritDoc} - */ - public function supportsClass($class) - { - return $class === $this->getClass(); - } -} diff --git a/Model/UserManagerInterface.php b/Model/UserManagerInterface.php deleted file mode 100644 index 0676ca9bcc..0000000000 --- a/Model/UserManagerInterface.php +++ /dev/null @@ -1,133 +0,0 @@ - - * (c) Thibault Duplessis - * (c) Johannes M. Schmitt - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Model; - -use Symfony\Component\Validator\Constraint; - -/** - * Interface to be implemented by user managers. This adds an additional level - * of abstraction between your application, and the actual repository. - * - * All changes to users should happen through this interface. - * - * The class also contains ACL annotations which will only work if you have the - * SecurityExtraBundle installed, otherwise they will simply be ignored. - * - * @author Gordon Franke - * @author Thibault Duplessis - * @author Johannes M. Schmitt - */ -interface UserManagerInterface -{ - /** - * Creates an empty user instance - * - * @return User - */ - function createUser(); - - /** - * Deletes a user - * - * @SecureParam(name="user", permissions="DELETE") - * @param User $user - * @return void - */ - function deleteUser(UserInterface $user); - - /** - * Finds one user by the given criteria - * - * @SecureReturn(permissions="VIEW") - * @param array $criteria - * @return User - */ - function findUserBy(array $criteria); - - /** - * Find a user by its username - * @param string $username - * @return User or null if user does not exist - */ - function findUserByUsername($username); - - /** - * Find a user by its email - * @param string $email - * @return User or null if user does not exist - */ - function findUserByEmail($email); - - /** - * Find a user by its username or email - * @param string $usernameOrEmail - * @return User or null if user does not exist - */ - function findUserByUsernameOrEmail($usernameOrEmail); - - /** - * Find a user by its confirmationToken - * @param string $token - * @return User or null if user does not exist - */ - function findUserByConfirmationToken($token); - - /** - * Returns a collection with all user instances - * - * @return Traversable - */ - function findUsers(); - - /** - * Returns the user's fully qualified class name - * - * @return string - */ - function getClass(); - - /** - * Updates a user - * - * @SecureParam(name="user", permissions="EDIT") - * @param User $user - * @return void - */ - function updateUser(UserInterface $user); - - /** - * Updates the canonical username and email fields for a user - * - * @SecureParam(name="user", permissions="EDIT") - * @param User $user - * @return void - */ - function updateCanonicalFields(UserInterface $user); - - /** - * Updates a user password if a plain password is set - * - * @SecureParam(name="user", permissions="EDIT") - * @param User $user - * @return void - */ - function updatePassword(UserInterface $user); - - /** - * Checks the uniqueness of the given fields, returns true if its unique - * - * @param UserInterface $value - * @param Constraint $constraint - * @return boolean - */ - function validateUnique($value, Constraint $constraint); -} diff --git a/README.markdown b/README.markdown deleted file mode 100644 index 0efcb9a4f0..0000000000 --- a/README.markdown +++ /dev/null @@ -1,16 +0,0 @@ -For documentation, see: - - Resources/doc/index.rst - - -License: - - Resources/meta/LICENSE - -UserBundle is a [knplabs](https://github.com/knplabs) initiative. - -**Caution:** This bundles is developed in sync with [fabpot's repository](https://github.com/fabpot/symfony) - -The *PR6* tag marks the commit in sync with the PR6 release of Symfony but it is -not a maintained release of the bundle. All bug fixes will be made in the master -branch. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..4974a2b84f --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +FOSUserBundle +============= + +The FOSUserBundle adds support for a database-backed user system in Symfony2+. +It provides a flexible framework for user management that aims to handle +common tasks such as user registration and password retrieval. + +Features include: + +- Users can be stored via Doctrine ORM or MongoDB ODM +- Registration support, with an optional confirmation per email +- Password reset support +- Unit tested + +**Note:** This bundle does *not* provide an authentication system but can +provide the user provider for the core [SecurityBundle](https://symfony.com/doc/current/book/security.html). + +[![Build Status](https://github.com/FriendsOfSymfony/FOSUserBundle/workflows/CI/badge.svg?branch=master)](https://github.com/FriendsOfSymfony/FOSUserBundle/actions?query=workflow%3ACI+branch%3Amaster) [![Total Downloads](https://poser.pugx.org/friendsofsymfony/user-bundle/downloads.svg)](https://packagist.org/packages/friendsofsymfony/user-bundle) [![Latest Stable Version](https://poser.pugx.org/friendsofsymfony/user-bundle/v/stable.svg)](https://packagist.org/packages/friendsofsymfony/user-bundle) + +Maintenance status +------------------ + +The package only receives minimal maintenance to allow existing projects to upgrade. Existing projects are expected to plan a migration away from this bundle. + +**New projects should not use this bundle.** It is actually much easier to implement the User entity in the project (allowing to fit exactly the need of the project). Regarding the extra features of the bundle: + +- the EntityUserProvider of Symfony already provides the UserProvider when using the Doctrine ORM (and other object managers integrated with Symfony have an equivalent feature) +- change password is easy to implement in the project. That's a simple form relying on core Symfony features (the validator for the current password is in core since years) +- email verification is provided by https://github.com/SymfonyCasts/verify-email-bundle +- password reset is provided by https://github.com/SymfonyCasts/reset-password-bundle +- registration is easier to implement in the project directly to fit the need of the project, especially when the User entity is in the project. symfony/form already provides everything you need +- the ProfileController showing a profile page for the user is better done in projects needing it, as the content is likely not enough anyway + +Documentation +------------- + +The source of the documentation is stored in the `docs/` folder +in this bundle. + +[Read the Documentation for the current branch](docs/index.rst) + +License +------- + +This bundle is under the MIT license. See the complete license [in the bundle](LICENSE) + +About +----- + +UserBundle is a [knplabs](https://github.com/knplabs) initiative. +See also the list of [contributors](https://github.com/FriendsOfSymfony/FOSUserBundle/contributors). + +Reporting an issue or a feature request +--------------------------------------- + +Issues and feature requests are tracked in the [Github issue tracker](https://github.com/FriendsOfSymfony/FOSUserBundle/issues). + +When reporting a bug, it may be a good idea to reproduce it in a basic project +built using the [Symfony Standard Edition](https://github.com/symfony/symfony-standard) +to allow developers of the bundle to reproduce the issue by simply cloning it +and following some steps. diff --git a/Resources/config/controller.xml b/Resources/config/controller.xml deleted file mode 100644 index e4e810b0a7..0000000000 --- a/Resources/config/controller.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.DefaultGroup.dcm.xml b/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.DefaultGroup.dcm.xml deleted file mode 100644 index 63224bc60e..0000000000 --- a/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.DefaultGroup.dcm.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.Group.dcm.xml b/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.Group.dcm.xml deleted file mode 100644 index 883958cebe..0000000000 --- a/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.Group.dcm.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.User.dcm.xml b/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.User.dcm.xml deleted file mode 100644 index 04de861db2..0000000000 --- a/Resources/config/doctrine/metadata/mongodb/FOS.UserBundle.Document.User.dcm.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.DefaultGroup.dcm.xml b/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.DefaultGroup.dcm.xml deleted file mode 100644 index 5a68c74fb6..0000000000 --- a/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.DefaultGroup.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - diff --git a/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.Group.dcm.xml b/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.Group.dcm.xml deleted file mode 100644 index c16fec453d..0000000000 --- a/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.Group.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - diff --git a/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.User.dcm.xml b/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.User.dcm.xml deleted file mode 100644 index fc92ae72b9..0000000000 --- a/Resources/config/doctrine/metadata/orm/FOS.UserBundle.Entity.User.dcm.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/form.xml b/Resources/config/form.xml deleted file mode 100644 index 7e2f34905c..0000000000 --- a/Resources/config/form.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - %fos_user.form.user.name% - - %fos_user.template.theme% - %fos_user.form.user.validation_groups% - - - - - - - - - - - - %fos_user.form.change_password.name% - - %fos_user.template.theme% - %fos_user.form.change_password.validation_groups% - - - - - - - - - - - - %fos_user.form.reset_password.name% - - %fos_user.template.theme% - %fos_user.form.reset_password.validation_groups% - - - - - - - - - - - - %fos_user.form.group.name% - - %fos_user.template.theme% - %fos_user.form.group.validation_groups% - - - - - - - - - - - - diff --git a/Resources/config/listener.xml b/Resources/config/listener.xml deleted file mode 100644 index 60c896694c..0000000000 --- a/Resources/config/listener.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - FOS\UserBundle\Security\InteractiveLoginListener - - - - - - - diff --git a/Resources/config/mongodb.xml b/Resources/config/mongodb.xml deleted file mode 100644 index a3361a204a..0000000000 --- a/Resources/config/mongodb.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - FOS\UserBundle\Document\UserManager - FOS\UserBundle\Document\GroupManager - - - - - - %fos_user.encoder.algorithm% - - - - %fos_user.model.user.class% - - - - %fos_user.model.group.class% - - - - diff --git a/Resources/config/orm.xml b/Resources/config/orm.xml deleted file mode 100644 index 871fe9d75a..0000000000 --- a/Resources/config/orm.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - FOS\UserBundle\Entity\UserManager - FOS\UserBundle\Entity\GroupManager - - - - - - %fos_user.encoder.algorithm% - - - - %fos_user.model.user.class% - - - - %fos_user.model.group.class% - - - - diff --git a/Resources/config/routing/group.xml b/Resources/config/routing/group.xml deleted file mode 100644 index d529f737c4..0000000000 --- a/Resources/config/routing/group.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - fos_user.controller.group:listAction - GET - - - - fos_user.controller.group:newAction - GET - - - - fos_user.controller.group:createAction - POST - - - - fos_user.controller.group:showAction - GET - - - - fos_user.controller.group:editAction - GET - - - - fos_user.controller.group:updateAction - POST - - - - fos_user.controller.group:deleteAction - GET - - - diff --git a/Resources/config/routing/security.xml b/Resources/config/routing/security.xml deleted file mode 100644 index f7b6acc31e..0000000000 --- a/Resources/config/routing/security.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - fos_user.controller.security:loginAction - - - - fos_user.controller.security:loginAction - - - - fos_user.controller.security:logoutAction - - - diff --git a/Resources/config/routing/user.xml b/Resources/config/routing/user.xml deleted file mode 100644 index 63f388f977..0000000000 --- a/Resources/config/routing/user.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - fos_user.controller.user:listAction - GET - - - - fos_user.controller.user:newAction - GET - - - - fos_user.controller.user:createAction - POST - - - - fos_user.controller.user:checkConfirmationEmailAction - GET - - - - fos_user.controller.user:confirmAction - GET - - - - fos_user.controller.user:confirmedAction - GET - - - - fos_user.controller.user:changePasswordAction - GET - - - - fos_user.controller.user:changePasswordUpdateAction - POST - - - - fos_user.controller.user:requestResetPasswordAction - GET - - - - fos_user.controller.user:sendResettingEmailAction - POST - - - - fos_user.controller.user:checkResettingEmailAction - GET - - - - fos_user.controller.user:resetPasswordAction - GET - - - - fos_user.controller.user:resetPasswordUpdateAction - POST - - - - fos_user.controller.user:showAction - GET - - - - fos_user.controller.user:editAction - GET - - - - fos_user.controller.user:updateAction - POST - - - - fos_user.controller.user:deleteAction - GET - - - diff --git a/Resources/config/security.xml b/Resources/config/security.xml deleted file mode 100644 index 33c65c2ced..0000000000 --- a/Resources/config/security.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - FOS\UserBundle\Security\Encoder\EncoderFactory - - - - - %security.encoder.digest.class% - %fos_user.encoder.encode_as_base64% - %fos_user.encoder.iterations% - - - - - - - \ No newline at end of file diff --git a/Resources/config/templating.xml b/Resources/config/templating.xml deleted file mode 100644 index 3233933811..0000000000 --- a/Resources/config/templating.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - FOS\UserBundle\Templating\Helper\SecurityHelper - - - - - - - - - diff --git a/Resources/config/twig.xml b/Resources/config/twig.xml deleted file mode 100644 index 4ac6321c1a..0000000000 --- a/Resources/config/twig.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - FOS\UserBundle\Twig\UserExtension - - - - - - - - - \ No newline at end of file diff --git a/Resources/config/util.xml b/Resources/config/util.xml deleted file mode 100644 index 52e03c93af..0000000000 --- a/Resources/config/util.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - %fos_user.email.confirmation.template% - %fos_user.email.resetting_password.template% - - %fos_user.email.from_email% - %fos_user.email.from_email% - - - - - - - diff --git a/Resources/config/validation.xml b/Resources/config/validation.xml deleted file mode 100644 index 99144c61cd..0000000000 --- a/Resources/config/validation.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/validator.xml b/Resources/config/validator.xml deleted file mode 100644 index 8dc2014adc..0000000000 --- a/Resources/config/validator.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - FOS\UserBundle\Validator\PasswordValidator - FOS\UserBundle\Validator\UniqueValidator - - - - - - - - - - - - - - - - - - - diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst deleted file mode 100644 index 17f910acbc..0000000000 --- a/Resources/doc/index.rst +++ /dev/null @@ -1,453 +0,0 @@ -Provides user persistence for your Symfony2 Project. - -Features -======== - -- Compatible with Doctrine ORM **and** ODM thanks to a generic repository. -- Model is extensible at will -- REST-ful authentication -- Current user available in your controllers and views -- Unit tested and functionally tested - -Installation -============ - -Add UserBundle to your src/ dir -------------------------------------- - -:: - - $ git submodule add git://github.com/FriendsOfSymfony/UserBundle.git src/FOS/UserBundle - -Add the FOS namespace to your autoloader ----------------------------------------- - -:: - // app/autoload.php - $loader->registerNamespaces(array( - 'FOS' => __DIR__.'/../src', - // your other namespaces - ); - -Add UserBundle to your application kernel ------------------------------------------ - -:: - - // app/AppKernel.php - - public function registerBundles() - { - return array( - // ... - new FOS\UserBundle\FOSUserBundle(), - // ... - ); - } - -Create your User class ----------------------- - -You must create a User class that extends either the entity or document abstract -User class in UserBundle. All fields on the base class are mapped, except for -``id`` and ``groups``; this is intentional, so you can select the generator that -best suits your application, and are able to use a custom Group model class. -Feel free to add additional properties and methods to your custom class. - -ORM User class: -~~~~~~~~~~~~~~~ - -:: - - // src/MyProject/MyBundle/Entity/User.php - - namespace MyProject\MyBundle\Entity; - use FOS\UserBundle\Entity\User as BaseUser; - - /** - * @orm:Entity - */ - class User extends BaseUser - { - /** - * @orm:Id - * @orm:Column(type="integer") - * @orm:generatedValue(strategy="AUTO") - */ - protected $id; - - /** - * @orm:ManyToMany(targetEntity="FOS\UserBundle\Entity\DefaultGroup") - * @orm:JoinTable(name="fos_user_user_group", - * joinColumns={@orm:JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@orm:JoinColumn(name="group_id", referencedColumnName="id")} - * ) - */ - protected $groups; - } - -MongoDB User class: -~~~~~~~~~~~~~~~~~~~ - -:: - - // src/MyProject/MyBundle/Document/User.php - - namespace MyProject\MyBundle\Document; - use FOS\UserBundle\Document\User as BaseUser; - - /** - * @mongodb:Document - */ - class User extends BaseUser - { - /** @mongodb:Id(strategy="auto") */ - protected $id; - - /** @mongodb:ReferenceMany(targetDocument="FOS\UserBundle\Document\DefaultGroup") */ - protected $groups; - } - -Group class ------------ - -To customize the Group class you can define your own entity extending the mapped -superclass of the Bundle ``FOS\UserBundle\Entity\Group``. If you don't want to -extend it you can use the entity provided by the bundle which is -``FOS\UserBundle\Entity\DefaultGroup``. -Same is available for MongoDB in the ``Document`` subnamespace. - -Configure your project ----------------------- - -The UserBundle works with the Symfony Security Component, so make sure that is -enabled in your kernel and in your project's configuration:: - - // app/AppKernel.php - public function registerBundles() - { - return array( - // ... - new Symfony\Bundle\SecurityBundle\SecurityBundle(), - // ... - ); - } - - # app/config/config.yml - security: - providers: - fos_user: - id: fos_user.user_manager - -Note:: - - You need to activate SwiftmailerBundle to be able to use the functionalities - using emails (confirmation of the account, resetting of the password). - See the `Emails` section to know how using another mailer. - -The login form and all the routes used to create a user and reset the password -have to be available to unauthenticated users but using the same firewall as -the pages you want to securize with the bundle. Assuming you import the -user.xml routing file with the ``/user`` prefix they will be:: - - /login - /user/new - /user/check-confirmation-email - /user/confirm/{token} - /user/confirmed - /user/request-reset-password - /user/send-resetting-email - /user/check-resetting-email - /user/reset-password/{token} - -You also have to include the UserBundle in your Doctrine mapping configuration, -along with the bundle containing your custom User class:: - - # app/config/config.yml - doctrine: - orm: - mappings: - FOSUserBundle: ~ - MyProjectMyBundle: ~ - # your other bundles - -The above example assumes an ORM configuration, but the `mappings` configuration -block would be the same for MongoDB ODM. - -Minimal configuration ---------------------- - -At a minimum, your configuration must define your DB driver ("orm" or "mongodb"), -a User class and the provider key. The provider key matches the key in the firewall -configuration that is used for users with the UserController. - -For example for a security configuration like the following the provider_key would -have to be set to "fos_userbundle", as shown in the proceeding examples: - -:: - - # app/config/config.yml - security: - providers: - fos_userbundle: - id: fos_user.user_manager - - firewalls: - main: - form_login: - provider: fos_userbundle - -ORM -~~~ - -In YAML: - -:: - - # app/config/config.yml - fos_user: - db_driver: orm - provider_key: fos_userbundle - class: - model: - user: MyProject\MyBundle\Entity\User - group: FOS\UserBundle\Entity\DefaultGroup - -Or if you prefer XML: - -:: - - # app/config/config.xml - - - - - - - -ODM -~~~ - -In YAML: - -:: - - # app/config/config.yml - fos_user: - db_driver: mongodb - provider_key: fos_userbundle - class: - model: - user: MyProject\MyBundle\Document\User - group: FOS\UserBundle\Document\DefaultGroup - -Or if you prefer XML: - -:: - - # app/config/config.xml - - - - - - - - -Add authentication routes -------------------------- - -If you want ready to use login and logout pages, include the built-in -routes: - -:: - - # app/config/routing.yml - fos_user_security: - resource: @FOSUserBundle/Resources/config/routing/security.xml - - fos_user_user: - resource: @FOSUserBundle/Resources/config/routing/user.xml - prefix: /user - -:: - - # app/config/routing.xml - - - - -You now can login at http://app.com/login - -You can also import the group.xml file to use the builtin controllers to -manipulate the groups. - -Command line -============ - -UserBundle provides command line utilities to help manage your -application users. - -Create user ------------ - -This command creates a new user:: - - $ php app/console fos:user:create username email password - -If you don't provide the required arguments, a interactive prompt will -ask them to you:: - - $ php app/console fos:user:create - -Promote user as a super administrator -------------------------------------- - -This command promotes a user as a super administrator:: - - $ php app/console fos:user:promote - -User manager service -==================== - -UserBundle works with both ORM and ODM. To make it possible, it wraps -all the operation on users in a UserManager. The user manager is a -service of the container. - -If you configure the db_driver to orm, this service is an instance of -``FOS\UserBundle\Entity\UserManager``. - -If you configure the db_driver to odm, this service is an instance of -``FOS\UserBundle\Document\UserManager``. - -Both these classes implement ``FOS\UserBundle\Model\UserManagerInterface``. - -Access the user manager service -------------------------------- - -If you want to manipulate users in a way that will work as well with -ORM and ODM, use the fos_user.user_manager service:: - - $userManager = $container->get('fos_user.user_manager'); - -That's the way UserBundle's internal controllers are built. - -Access the current user class ------------------------------ - -A new instance of your User class can be created by the user manager:: - - $user = $userManager->createUser(); - -`$user` is now an Entity or a Document, depending on the configuration. - -Configuration example: -====================== - -All configuration options are listed below:: - - fos_user: - db_driver: mongodb - provider_key: fos_userbundle - class: - model: - user: MyProject\MyBundle\Document\User - group: MyProject\MyBundle\Document\Group - form: - user: ~ - group: ~ - change_password: ~ - reset_password: ~ - controller: - user: ~ - security: ~ - group: ~ - util: - email_canonicalizer: ~ - username_canonicalizer: ~ - service: - util: - mailer: ~ - encoder: - algorithm: ~ - encode_as_base64: ~ - iterations: ~ - form_name: - user: ~ - group: ~ - change_password: ~ - reset_password: ~ - form_validation_groups: - user: ~ # This value is an array of groups - email: - from_email: ~ # { admin@example.com: Sender_name } - confirmation: - enabled: ~ - template: ~ - resetting_password: - template: ~ - token_ttl: ~ - template: - engine: ~ - theme: ~ - -Templating ----------- - -The template names are not configurable, however Symfony2 makes it possible -to extend a bundle by creating a new Bundle and implementing a getParent() -method inside that new Bundle's definition: - - class MyProjectUserBundle extends Bundle - { - public function getParent() - { - return 'FOSUserBundle'; - } - } - -For example ``src/FOS/UserBundle/Resources/views/User/new.twig`` can be -replaced inside an application by putting a file with alternative content in -``src/MyProject/FOS/UserBundle/Resources/views/User/new.twig``. - -You can use a different templating engine by configuring it but you will have to -create all the needed templates as only twig templates are provided. - -Validation ----------- - -The ``Resources/config/validation.xml`` file contains definitions for custom -validator rules for various classes. The rules for the ``User`` class are all in -the ``Registration`` validation group so you can choose not to use them. - -Emails ------- - -The default mailer relies on Swiftmailer to send the mails of the bundle. If you -want to use another mailer in your project you can change it by defining your -own service implementing ``FOS\UserBundle\Util\MailerInterface`` and setting its -id in the configuration:: - - fos_user: - # ... - service: - util: - mailer: custom_mailer_id - -Canonicalization ----------------- - -``Canonicalizer`` services are used to canonicalize the username and the email -fields for database storage. By default, username and email fields are canonicalized -in the same manner using ``mb_convert_case()``. You may configure your own class -for each field provided it implements ``FOS\UserBundle\Util\CanonicalizerInterface``. - -Note:: - If you do not have the mbstring extension installed you will need to - define your own ``canonicalizer``. diff --git a/Resources/views/Group/edit.html.twig b/Resources/views/Group/edit.html.twig deleted file mode 100644 index a94db9b4da..0000000000 --- a/Resources/views/Group/edit.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:Group:edit_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/Group/edit_content.html.twig b/Resources/views/Group/edit_content.html.twig deleted file mode 100644 index ba3c48e42b..0000000000 --- a/Resources/views/Group/edit_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% form_theme form form.getOption('theme') %} -
- {{ form_field(form) }} -
- -
-
diff --git a/Resources/views/Group/list.html.twig b/Resources/views/Group/list.html.twig deleted file mode 100644 index 12e30c4171..0000000000 --- a/Resources/views/Group/list.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:Group:list_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/Group/list_content.html.twig b/Resources/views/Group/list_content.html.twig deleted file mode 100644 index e2b831a12f..0000000000 --- a/Resources/views/Group/list_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -
- -
diff --git a/Resources/views/Group/new.html.twig b/Resources/views/Group/new.html.twig deleted file mode 100644 index cfb691272f..0000000000 --- a/Resources/views/Group/new.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:Group:new_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/Group/new_content.html.twig b/Resources/views/Group/new_content.html.twig deleted file mode 100644 index 9998ab9c1f..0000000000 --- a/Resources/views/Group/new_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% form_theme form form.getOption('theme') %} -
- {{ form_field(form) }} -
- -
-
diff --git a/Resources/views/Group/show.html.twig b/Resources/views/Group/show.html.twig deleted file mode 100644 index ac33936c30..0000000000 --- a/Resources/views/Group/show.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:Group:show_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/Group/show_content.html.twig b/Resources/views/Group/show_content.html.twig deleted file mode 100644 index 1351f0d25c..0000000000 --- a/Resources/views/Group/show_content.html.twig +++ /dev/null @@ -1,3 +0,0 @@ -
-

{% trans "Groupname" from 'FOSUserBundle' %}: {{ group.getName() }}

-
diff --git a/Resources/views/Security/login.html.twig b/Resources/views/Security/login.html.twig deleted file mode 100644 index ef1d29c850..0000000000 --- a/Resources/views/Security/login.html.twig +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% if error %} -
{{ error }}
-{% endif %} - -
- - - - - - - - - - -
-

- {% trans 'Forgotten password' from 'FOSUserBundle' %} -

-{% endblock content %} diff --git a/Resources/views/User/changePassword.html.twig b/Resources/views/User/changePassword.html.twig deleted file mode 100644 index 9a7f7eb9dc..0000000000 --- a/Resources/views/User/changePassword.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:changePassword_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/changePassword_content.html.twig b/Resources/views/User/changePassword_content.html.twig deleted file mode 100644 index a93c5e020b..0000000000 --- a/Resources/views/User/changePassword_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% form_theme form form.getOption('theme') %} -
- {{ form_field(form) }} -
- -
-
diff --git a/Resources/views/User/checkConfirmationEmail.html.twig b/Resources/views/User/checkConfirmationEmail.html.twig deleted file mode 100644 index 0083284369..0000000000 --- a/Resources/views/User/checkConfirmationEmail.html.twig +++ /dev/null @@ -1,4 +0,0 @@ -{% set email = user.getEmail() %} -{% trans from 'FOSUserBundle' %} -An email has been sent to %email%. It contains an activation link you must click to activate your account. -{% endtrans %} diff --git a/Resources/views/User/checkResettingEmail.html.twig b/Resources/views/User/checkResettingEmail.html.twig deleted file mode 100644 index 0528b5f2ff..0000000000 --- a/Resources/views/User/checkResettingEmail.html.twig +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% set email = user.getEmail() %} -

-{% trans from 'FOSUserBundle' %} -An email has been sent to %email%. It contains an link you must click to reset your password. -{% endtrans %} -

-{% endblock %} diff --git a/Resources/views/User/confirmationEmail.txt.twig b/Resources/views/User/confirmationEmail.txt.twig deleted file mode 100644 index 08b7e368c9..0000000000 --- a/Resources/views/User/confirmationEmail.txt.twig +++ /dev/null @@ -1,11 +0,0 @@ -{% set username = user.getUsername() %} -{% trans from 'FOSUserBundle' %} - Welcome %username%! - Hello %username%! - - To finish validating your account - please visit %confirmationUrl% - - Regards, - - the Team. -{% endtrans %} diff --git a/Resources/views/User/confirmed.html.twig b/Resources/views/User/confirmed.html.twig deleted file mode 100644 index 62438d48b4..0000000000 --- a/Resources/views/User/confirmed.html.twig +++ /dev/null @@ -1,4 +0,0 @@ -{% set username = user.getUsername() %} -{% trans from 'FOSUserBundle' %} - Congrats %username%, your account is now confirmed. -{% endtrans %} diff --git a/Resources/views/User/edit.html.twig b/Resources/views/User/edit.html.twig deleted file mode 100644 index 170c499155..0000000000 --- a/Resources/views/User/edit.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:edit_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/edit_content.html.twig b/Resources/views/User/edit_content.html.twig deleted file mode 100755 index 9a44163649..0000000000 --- a/Resources/views/User/edit_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% form_theme form form.getOption('theme') %} -
- {{ form_field(form) }} -
- -
-
diff --git a/Resources/views/User/list.html.twig b/Resources/views/User/list.html.twig deleted file mode 100644 index d93ecf8964..0000000000 --- a/Resources/views/User/list.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:list_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/list_content.html.twig b/Resources/views/User/list_content.html.twig deleted file mode 100644 index 5b3b06216c..0000000000 --- a/Resources/views/User/list_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -
- -
diff --git a/Resources/views/User/new.html.twig b/Resources/views/User/new.html.twig deleted file mode 100644 index 06b2c36a43..0000000000 --- a/Resources/views/User/new.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:new_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/new_content.html.twig b/Resources/views/User/new_content.html.twig deleted file mode 100755 index 46631b922a..0000000000 --- a/Resources/views/User/new_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% form_theme form form.getOption('theme') %} -
- {{ form_field(form) }} -
- -
-
diff --git a/Resources/views/User/passwordAlreadyRequested.html.twig b/Resources/views/User/passwordAlreadyRequested.html.twig deleted file mode 100644 index b0a45bea4f..0000000000 --- a/Resources/views/User/passwordAlreadyRequested.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -The password for this user has already been requested within the last 24 hours. -{% endblock content %} diff --git a/Resources/views/User/requestResetPassword.html.twig b/Resources/views/User/requestResetPassword.html.twig deleted file mode 100644 index a459505418..0000000000 --- a/Resources/views/User/requestResetPassword.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:requestResetPassword_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/requestResetPassword_content.html.twig b/Resources/views/User/requestResetPassword_content.html.twig deleted file mode 100644 index 1b2e67144d..0000000000 --- a/Resources/views/User/requestResetPassword_content.html.twig +++ /dev/null @@ -1,9 +0,0 @@ -
-
- - -
-
- -
-
\ No newline at end of file diff --git a/Resources/views/User/resetPassword.html.twig b/Resources/views/User/resetPassword.html.twig deleted file mode 100644 index a1dce38729..0000000000 --- a/Resources/views/User/resetPassword.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:resetPassword_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/resetPassword_content.html.twig b/Resources/views/User/resetPassword_content.html.twig deleted file mode 100644 index dbfb54ce77..0000000000 --- a/Resources/views/User/resetPassword_content.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% form_theme form form.getOption('theme') %} -
- {{ form_field(form) }} -
- -
-
diff --git a/Resources/views/User/resettingPasswordEmail.txt.twig b/Resources/views/User/resettingPasswordEmail.txt.twig deleted file mode 100644 index c0e77827db..0000000000 --- a/Resources/views/User/resettingPasswordEmail.txt.twig +++ /dev/null @@ -1,11 +0,0 @@ -{% set username = user.getUsername() %} -{% trans from 'FOSUserBundle' %} - Welcome %username%! - Hello %username%! - - To reset your password - please visit %confirmationUrl% - - Regards, - - the Team. -{% endtrans %} diff --git a/Resources/views/User/show.html.twig b/Resources/views/User/show.html.twig deleted file mode 100644 index 34aa601b9f..0000000000 --- a/Resources/views/User/show.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "FOSUserBundle::layout.html.twig" %} - -{% block content %} -{% include "FOSUserBundle:User:show_content.html.twig" %} -{% endblock content %} diff --git a/Resources/views/User/show_content.html.twig b/Resources/views/User/show_content.html.twig deleted file mode 100644 index f56e164f3e..0000000000 --- a/Resources/views/User/show_content.html.twig +++ /dev/null @@ -1,4 +0,0 @@ -
-

{% trans "Username" from 'FOSUserBundle' %}: {{ user.getUsername() }}

-

{% trans "Email" from 'FOSUserBundle' %}: {{ user.getEmail() }}

-
diff --git a/Resources/views/layout.html.twig b/Resources/views/layout.html.twig deleted file mode 100644 index b6c916dc54..0000000000 --- a/Resources/views/layout.html.twig +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - {% for key, flash in app.session.getFlashes() %} -
- {% trans key from 'FOSUserBundle' %} -
- {% endfor %} - -
- {% block content %} - {% endblock content %} -
- - diff --git a/Security/Encoder/EncoderFactory.php b/Security/Encoder/EncoderFactory.php deleted file mode 100644 index a24ff4ed50..0000000000 --- a/Security/Encoder/EncoderFactory.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Security\Encoder; - -use FOS\UserBundle\Model\UserInterface; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; -use Symfony\Component\Security\Core\User\AccountInterface; - -/** - * This factory assumes MessageDigestPasswordEncoder's constructor. - * - * @author Johannes M. Schmitt - * @author Jeremy Mikola - */ -class EncoderFactory implements EncoderFactoryInterface -{ - protected $encoders; - protected $encoderClass; - protected $encodeHashAsBase64; - protected $iterations; - protected $genericFactory; - - /** - * Constructor. - * - * @param string $encoderClass Encoder class - * @param Boolean $encodeHashAsBase64 - * @param integer $iterations - * @param EncoderFactoryInterface $genericFactory - */ - public function __construct($encoderClass, $encodeHashAsBase64, $iterations, EncoderFactoryInterface $genericFactory) - { - $this->encoders = array(); - $this->encoderClass = $encoderClass; - $this->encodeHashAsBase64 = $encodeHashAsBase64; - $this->iterations = $iterations; - $this->genericFactory = $genericFactory; - } - - /** - * @see Symfony\Component\Security\Core\Encoder\EncoderFactory::getEncoder() - */ - public function getEncoder(AccountInterface $account) - { - if (!$account instanceof UserInterface) { - return $this->genericFactory->getEncoder($account); - } - - if (isset($this->encoders[$algorithm = $account->getAlgorithm()])) { - return $this->encoders[$algorithm]; - } - - return $this->encoders[$algorithm] = $this->createEncoder($algorithm); - } - - /** - * Creates an encoder for the given algorithm. - * - * @param string $algorithm - * @return PasswordEncoderInterface - */ - protected function createEncoder($algorithm) - { - $class = $this->encoderClass; - - return new $class( - $algorithm, - $this->encodeHashAsBase64, - $this->iterations - ); - } -} \ No newline at end of file diff --git a/Security/InteractiveLoginListener.php b/Security/InteractiveLoginListener.php deleted file mode 100644 index 39e8fcb6e3..0000000000 --- a/Security/InteractiveLoginListener.php +++ /dev/null @@ -1,29 +0,0 @@ -userManager = $userManager; - } - - public function listenToInteractiveLogin(Event $event) - { - $user = $event->get('token')->getUser(); - - if ($user instanceof User) { - $user->setLastLogin(new DateTime()); - $this->userManager->updateUser($user); - } - } -} diff --git a/Templating/Helper/SecurityHelper.php b/Templating/Helper/SecurityHelper.php deleted file mode 100644 index 223c128714..0000000000 --- a/Templating/Helper/SecurityHelper.php +++ /dev/null @@ -1,65 +0,0 @@ -context->getToken()->getUser(); - } - - /** - * Tells whether the authenticated user is this user - * - * @return bool - **/ - public function isUser(User $user) - { - $authenticatedUser = $this->getUser(); - - return $authenticatedUser instanceof User && $authenticatedUser->isUser($user); - } - - /** - * Tell whether or not a user is logged in - * - * @return bool - **/ - public function isAuthenticated() - { - return null !== $this->context->getToken(); - } - - /** - * Tell whether or not the user is anonymous - * - * @return bool - **/ - public function isAnonymous() - { - return false === $this->vote('IS_AUTHENTICATED_FULLY'); - } - - /** - * Returns the canonical name of this helper. - * - * @return string The canonical name - */ - public function getName() - { - return 'fos_user_security'; - } -} diff --git a/Test/WebTestCase.php b/Test/WebTestCase.php deleted file mode 100644 index 69dbb1a9a5..0000000000 --- a/Test/WebTestCase.php +++ /dev/null @@ -1,49 +0,0 @@ -createKernel(); - $kernel->boot(); - - $application = new Application($kernel); - $application->setAutoExit(false); - - $input = new ArrayInput($params); - $input->setInteractive(false); - - $ouput = new NullOutput(0); - - $application->run($input, $ouput); - } - - protected function getService($name) - { - if (null === $this->kernel) { - $this->kernel = $this->createKernel(); - $this->kernel->boot(); - } - - return $this->kernel->getContainer()->get($name); - } - - protected function removeTestUser() - { - $userManager = $this->getService('fos_user.user_manager'); - if ($user = $userManager->findUserByUsername('test_username')) { - $userManager->deleteUser($user); - } - } -} diff --git a/Tests/Command/ActivateUserCommandTest.php b/Tests/Command/ActivateUserCommandTest.php deleted file mode 100644 index 35e483fe2d..0000000000 --- a/Tests/Command/ActivateUserCommandTest.php +++ /dev/null @@ -1,58 +0,0 @@ -createKernel(); - $command = new ActivateUserCommand(); - $application = new Application($kernel); - $application->setAutoExit(false); - $tester = new ApplicationTester($application); - - $username = 'test_username'; - $password = 'test_password'; - $email = 'test_email@email.org'; - - $userManager = $this->getService('fos_user.user_manager'); - - $user = $userManager->createUser(); - $user->setUsername($username); - $user->setEmail($email); - $user->setPlainPassword($password); - $user->setEnabled(false); - - $userManager->updateUser($user); - - $this->assertFalse($user->isEnabled()); - - $tester->run(array( - 'command' => $command->getFullName(), - 'username' => $username, - ), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - - $this->getService('doctrine.orm.default_entity_manager')->clear(); - - $userManager = $this->getService('fos_user.user_manager'); - $user = $userManager->findUserByUsername($username); - - $this->assertTrue($user instanceof User); - $this->assertTrue($user->isEnabled()); - - $userManager->updateUser($user); - } - - protected function tearDown() - { - $this->removeTestUser(); - } -} diff --git a/Tests/Command/CreateUserCommandTest.php b/Tests/Command/CreateUserCommandTest.php deleted file mode 100644 index c81ec815d2..0000000000 --- a/Tests/Command/CreateUserCommandTest.php +++ /dev/null @@ -1,74 +0,0 @@ -createKernel(); - $command = new CreateUserCommand(); - $application = new Application($kernel); - $application->setAutoExit(false); - $tester = new ApplicationTester($application); - $username = 'test_username'; - $password = 'test_password'; - $email = 'test_email@email.org'; - $tester->run(array( - 'command' => $command->getFullName(), - 'username' => $username, - 'password' => $password, - 'email' => $email, - ), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - - $userManager = $this->getService('fos_user.user_manager'); - $user = $userManager->findUserByUsername($username); - - $this->assertTrue($user instanceof User); - $this->assertEquals($email, $user->getEmail()); - - $userManager->deleteUser($user); - } - - public function testUserCreationWithOptions() - { - $kernel = $this->createKernel(); - $command = new CreateUserCommand(); - $application = new Application($kernel); - $application->setAutoExit(false); - $tester = new ApplicationTester($application); - $username = 'test_username'; - $password = 'test_password'; - $email = 'test_email@email.org'; - $tester->run(array( - 'command' => $command->getFullName(), - 'username' => $username, - 'password' => $password, - 'email' => $email, - '--inactive' => true, - '--super-admin' => true - ), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - - $userManager = $this->getService('fos_user.user_manager'); - $user = $userManager->findUserByUsername($username); - - $this->assertTrue($user instanceof User); - $this->assertEquals($email, $user->getEmail()); - $this->assertFalse($user->isEnabled()); - $this->assertTrue($user->hasRole('ROLE_SUPERADMIN')); - - $userManager->deleteUser($user); - } - - protected function tearDown() - { - $this->removeTestUser(); - } -} diff --git a/Tests/Command/DeactivateUserCommandTest.php b/Tests/Command/DeactivateUserCommandTest.php deleted file mode 100644 index ca87277241..0000000000 --- a/Tests/Command/DeactivateUserCommandTest.php +++ /dev/null @@ -1,58 +0,0 @@ -createKernel(); - $command = new DeactivateUserCommand(); - $application = new Application($kernel); - $application->setAutoExit(false); - $tester = new ApplicationTester($application); - - $username = 'test_username'; - $password = 'test_password'; - $email = 'test_email@email.org'; - - $userManager = $this->getService('fos_user.user_manager'); - - $user = $userManager->createUser(); - $user->setUsername($username); - $user->setEmail($email); - $user->setPlainPassword($password); - $user->setEnabled(true); - - $userManager->updateUser($user); - - $this->assertTrue($user->isEnabled()); - - $tester->run(array( - 'command' => $command->getFullName(), - 'username' => $username, - ), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - - $this->getService('doctrine.orm.default_entity_manager')->clear(); - - $userManager = $this->getService('fos_user.user_manager'); - $user = $userManager->findUserByUsername($username); - - $this->assertTrue($user instanceof User); - $this->assertFalse($user->isEnabled()); - - $userManager->deleteUser($user); - } - - protected function tearDown() - { - $this->removeTestUser(); - } -} diff --git a/Tests/Command/DemoteSuperUserCommandTest.php b/Tests/Command/DemoteSuperUserCommandTest.php deleted file mode 100644 index ec1dde9b0f..0000000000 --- a/Tests/Command/DemoteSuperUserCommandTest.php +++ /dev/null @@ -1,58 +0,0 @@ -createKernel(); - $command = new DemoteSuperAdminCommand(); - $application = new Application($kernel); - $application->setAutoExit(false); - $tester = new ApplicationTester($application); - - $username = 'test_username'; - $password = 'test_password'; - $email = 'test_email@email.org'; - - $userManager = $this->getService('fos_user.user_manager'); - - $user = $userManager->createUser(); - $user->setUsername($username); - $user->setEmail($email); - $user->setPlainPassword($password); - $user->addRole('ROLE_SUPERADMIN'); - - $userManager->updateUser($user); - - $this->assertTrue($user->hasRole('ROLE_SUPERADMIN')); - - $tester->run(array( - 'command' => $command->getFullName(), - 'username' => $username, - ), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - - $this->getService('doctrine.orm.default_entity_manager')->clear(); - - $userManager = $this->getService('fos_user.user_manager'); - $user = $userManager->findUserByUsername($username); - - $this->assertTrue($user instanceof User); - $this->assertFalse($user->hasRole('ROLE_SUPERADMIN')); - - $userManager->deleteUser($user); - } - - protected function tearDown() - { - $this->removeTestUser(); - } -} diff --git a/Tests/Command/PromoteSuperUserCommandTest.php b/Tests/Command/PromoteSuperUserCommandTest.php deleted file mode 100644 index 43d6f8f356..0000000000 --- a/Tests/Command/PromoteSuperUserCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -createKernel(); - $command = new PromoteSuperAdminCommand(); - $application = new Application($kernel); - $application->setAutoExit(false); - $tester = new ApplicationTester($application); - $username = 'test_username'; - $password = 'test_password'; - $email = 'test_email@email.org'; - $userManager = $kernel->getContainer()->get('fos_user.user_manager'); - $user = $userManager->createUser(); - $user->setUsername($username); - $user->setEmail($email); - $user->setPlainPassword($password); - $userManager->updateUser($user); - $this->assertFalse($user->hasRole('ROLE_SUPERADMIN')); - $tester->run(array( - 'command' => $command->getFullName(), - 'username' => $username, - ), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - - $userManager = $this->getService('fos_user.user_manager'); - $user = $userManager->findUserByUsername($username); - - $this->assertTrue($user instanceof User); - $this->assertTrue($user->hasRole('ROLE_SUPERADMIN')); - - $userManager->deleteUser($user); - } - - protected function tearDown() - { - $this->removeTestUser(); - } -} diff --git a/Tests/DependencyInjection/FOSUserExtensionTest.php b/Tests/DependencyInjection/FOSUserExtensionTest.php deleted file mode 100644 index 9215cf71db..0000000000 --- a/Tests/DependencyInjection/FOSUserExtensionTest.php +++ /dev/null @@ -1,397 +0,0 @@ -getEmptyConfig(); - unset($config['db_driver']); - $loader->load(array($config), new ContainerBuilder()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testUserLoadThrowsExceptionUnlessDatabaseDriverIsValid() - { - $loader = new FOSUserExtension(); - $config = $this->getEmptyConfig(); - $config['db_driver'] = 'foo'; - $loader->load(array($config), new ContainerBuilder()); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testUserLoadThrowsExceptionUnlessProviderKeySet() - { - $loader = new FOSUserExtension(); - $config = $this->getEmptyConfig(); - unset($config['provider_key']); - $loader->load(array($config), new ContainerBuilder()); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testUserLoadThrowsExceptionUnlessGroupModelClassSet() - { - $loader = new FOSUserExtension(); - $config = $this->getEmptyConfig(); - unset($config['class']['model']['group']); - $loader->load(array($config), new ContainerBuilder()); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testUserLoadThrowsExceptionUnlessUserModelClassSet() - { - $loader = new FOSUserExtension(); - $config = $this->getEmptyConfig(); - unset($config['class']['model']['user']); - $loader->load(array($config), new ContainerBuilder()); - } - - public function testUserLoadModelClassWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('Acme\MyBundle\Document\User', 'fos_user.model.user.class'); - } - - public function testUserLoadModelClass() - { - $this->createFullConfiguration(); - - $this->assertParameter('Acme\MyBundle\Entity\User', 'fos_user.model.user.class'); - } - - public function testUserLoadManagerClassWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertHasDefinition('fos_user.user_manager'); - } - - public function testUserLoadManagerClass() - { - $this->createFullConfiguration(); - - $this->assertHasDefinition('fos_user.user_manager'); - } - - public function testUserLoadFormClassWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('FOS\UserBundle\Form\UserForm', 'fos_user.form.user.class'); - $this->assertParameter('FOS\UserBundle\Form\GroupForm', 'fos_user.form.group.class'); - $this->assertParameter('FOS\UserBundle\Form\ChangePasswordForm', 'fos_user.form.change_password.class'); - $this->assertParameter('FOS\UserBundle\Form\ResetPasswordForm', 'fos_user.form.reset_password.class'); - } - - public function testUserLoadFormClass() - { - $this->createFullConfiguration(); - - $this->assertParameter('Acme\MyBundle\Form\User', 'fos_user.form.user.class'); - $this->assertParameter('Acme\MyBundle\Form\Group', 'fos_user.form.group.class'); - $this->assertParameter('Acme\MyBundle\Form\ChangePassword', 'fos_user.form.change_password.class'); - $this->assertParameter('Acme\MyBundle\Form\ResetPassword', 'fos_user.form.reset_password.class'); - } - - public function testUserLoadFormNameWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('fos_user_user_form', 'fos_user.form.user.name'); - $this->assertParameter('fos_user_group_form', 'fos_user.form.group.name'); - $this->assertParameter('fos_user_change_password_form', 'fos_user.form.change_password.name'); - $this->assertParameter('fos_user_reset_password_form', 'fos_user.form.reset_password.name'); - } - - public function testUserLoadFormName() - { - $this->createFullConfiguration(); - - $this->assertParameter('acme_user_form', 'fos_user.form.user.name'); - $this->assertParameter('acme_group_form', 'fos_user.form.group.name'); - $this->assertParameter('acme_change_form', 'fos_user.form.change_password.name'); - $this->assertParameter('acme_reset_form', 'fos_user.form.reset_password.name'); - } - - public function testUserLoadFormServiceWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertHasDefinition('fos_user.form.user'); - $this->assertHasDefinition('fos_user.form.group'); - $this->assertHasDefinition('fos_user.form.change_password'); - $this->assertHasDefinition('fos_user.form.reset_password'); - } - - public function testUserLoadFormService() - { - $this->createFullConfiguration(); - - $this->assertHasDefinition('fos_user.form.user'); - $this->assertHasDefinition('fos_user.form.group'); - $this->assertHasDefinition('fos_user.form.change_password'); - $this->assertHasDefinition('fos_user.form.reset_password'); - } - - public function testUserLoadControllerClassWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('FOS\UserBundle\Controller\UserController', 'fos_user.controller.user.class'); - $this->assertParameter('FOS\UserBundle\Controller\GroupController', 'fos_user.controller.group.class'); - $this->assertParameter('FOS\UserBundle\Controller\SecurityController', 'fos_user.controller.security.class'); - } - - public function testUserLoadControllerClass() - { - $this->createFullConfiguration(); - - $this->assertParameter('Acme\MyBundle\Controller\UserController', 'fos_user.controller.user.class'); - $this->assertParameter('Acme\MyBundle\Controller\GroupController', 'fos_user.controller.group.class'); - $this->assertParameter('Acme\MyBundle\Controller\SecurityController', 'fos_user.controller.security.class'); - } - - public function testUserLoadControllerServiceWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertHasDefinition('fos_user.controller.user'); - $this->assertHasDefinition('fos_user.controller.group'); - $this->assertHasDefinition('fos_user.controller.security'); - } - - public function testUserLoadControllerService() - { - $this->createFullConfiguration(); - - $this->assertHasDefinition('fos_user.controller.user'); - $this->assertHasDefinition('fos_user.controller.group'); - $this->assertHasDefinition('fos_user.controller.security'); - } - - public function testUserLoadConfirmationEmailWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter(false, 'fos_user.email.confirmation.enabled'); - $this->assertParameter(array('webmaster@example.com' => 'webmaster'), 'fos_user.email.from_email'); - $this->assertParameter('FOSUserBundle:User:confirmationEmail', 'fos_user.email.confirmation.template'); - $this->assertParameter('FOSUserBundle:User:resettingPasswordEmail', 'fos_user.email.resetting_password.template'); - $this->assertParameter(86400, 'fos_user.email.resetting_password.token_ttl'); - } - - public function testUserLoadConfirmationEmail() - { - $this->createFullConfiguration(); - - $this->assertParameter(true, 'fos_user.email.confirmation.enabled'); - $this->assertParameter(array('admin@acme.org' => 'Acme Corp'), 'fos_user.email.from_email'); - $this->assertParameter('AcmeMyBundle:Mail:confirmation', 'fos_user.email.confirmation.template'); - $this->assertParameter('AcmeMyBundle:Mail:resetting', 'fos_user.email.resetting_password.template'); - $this->assertParameter(1800, 'fos_user.email.resetting_password.token_ttl'); - } - - public function testUserLoadTemplateConfigWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('twig', 'fos_user.template.engine'); - $this->assertParameter('TwigBundle::form.html.twig', 'fos_user.template.theme'); - } - - public function testUserLoadTemplateConfig() - { - $this->createFullConfiguration(); - - $this->assertParameter('php', 'fos_user.template.engine'); - $this->assertParameter('AcmeMyBundle:Form:theme.html.twig', 'fos_user.template.theme'); - } - - public function testUserLoadEncoderConfigWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('sha512', 'fos_user.encoder.algorithm'); - $this->assertParameter(false, 'fos_user.encoder.encode_as_base64'); - $this->assertParameter(1, 'fos_user.encoder.iterations'); - } - - public function testUserLoadEncoderConfig() - { - $this->createFullConfiguration(); - - $this->assertParameter('sha1', 'fos_user.encoder.algorithm'); - $this->assertParameter(true, 'fos_user.encoder.encode_as_base64'); - $this->assertParameter(3, 'fos_user.encoder.iterations'); - } - - public function testUserLoadUtilClassWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertParameter('FOS\UserBundle\Util\Canonicalizer', 'fos_user.util.email_canonicalizer.class'); - $this->assertParameter('FOS\UserBundle\Util\Canonicalizer', 'fos_user.util.username_canonicalizer.class'); - } - - public function testUserLoadUtilClass() - { - $this->createFullConfiguration(); - - $this->assertParameter('Acme\MyBundle\Util\EmailCanonicalizer', 'fos_user.util.email_canonicalizer.class'); - $this->assertParameter('Acme\MyBundle\Util\UsernameCanonicalizer', 'fos_user.util.username_canonicalizer.class'); - } - - public function testUserLoadUtilServiceWithDefaults() - { - $this->createEmptyConfiguration(); - - $this->assertAlias('fos_user.util.mailer.real', 'fos_user.util.mailer'); - } - - public function testUserLoadUtilService() - { - $this->createFullConfiguration(); - - $this->assertAlias('acme_my.util.mailer', 'fos_user.util.mailer'); - } - - /** - * @return ContainerBuilder - */ - protected function createEmptyConfiguration() - { - $this->configuration = new ContainerBuilder(); - $loader = new FOSUserExtension(); - $config = $this->getEmptyConfig(); - $loader->load(array($config), $this->configuration); - $this->assertTrue($this->configuration instanceof ContainerBuilder); - } - - /** - * @return ContainerBuilder - */ - protected function createFullConfiguration() - { - $this->configuration = new ContainerBuilder(); - $loader = new FOSUserExtension(); - $config = $this->getFullConfig(); - $loader->load(array($config), $this->configuration); - $this->assertTrue($this->configuration instanceof ContainerBuilder); - } - - /** - * getEmptyConfig - * - * @return array - */ - protected function getEmptyConfig() - { - $yaml = <<parse($yaml); - } - - protected function getFullConfig() - { - $yaml = <<parse($yaml); - } - - public function assertAlias($value, $key) - { - $this->assertEquals($value, (string) $this->configuration->getAlias($key), sprintf('%s alias is correct', $key)); - } - - public function assertParameter($value, $key) - { - $this->assertEquals($value, $this->configuration->getParameter($key), sprintf('%s parameter is correct', $key)); - } - - public function assertHasDefinition($id) - { - $this->assertTrue(($this->configuration->hasDefinition($id) ?: $this->configuration->hasAlias($id))); - } - - protected function tearDown() - { - unset($this->configuration); - } - -} diff --git a/Tests/Document/DocumentUserManagerTest.php b/Tests/Document/DocumentUserManagerTest.php deleted file mode 100644 index 044de9131e..0000000000 --- a/Tests/Document/DocumentUserManagerTest.php +++ /dev/null @@ -1,109 +0,0 @@ -userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('usernameCanonical' => 'jack'))); - - $this->userManager->findUserByUsername('jack'); - } - - public function testFindUserByUsernameLowercasesTheUsername() - { - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('usernameCanonical' => 'jack'))); - - $this->userManager->findUserByUsername('JaCk'); - } - - public function testFindUserByEmail() - { - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('email' => 'jack@email.org'))); - - $this->userManager->findUserByEmail('jack@email.org'); - } - - public function testFindUserByEmailLowercasesTheEmail() - { - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('email' => 'jack@email.org'))); - - $this->userManager->findUserByEmail('JaCk@EmAiL.oRg'); - } - - public function testFindUserByUsernameOrEmailWithUsername() - { - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('usernameCanonical' => 'jack'))); - - $this->userManager->findUserByUsernameOrEmail('JaCk'); - } - - public function testFindUserByUsernameOrEmailWithEmail() - { - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('email' => 'jack@email.org'))); - - $this->userManager->findUserByUsernameOrEmail('JaCk@EmAiL.oRg'); - } - - public function testLoadUserByUsernameWithExistingUser() - { - $userMock = $this->getMock('FOS\UserBundle\Document\User', array(), array('sha1')); - - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('usernameCanonical' => 'jack'))) - ->will($this->returnValue($userMock)); - - $this->userManager->loadUserByUsername('jack'); - } - - /** - * @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException - */ - public function testLoadUserByUsernameWithMissingUser() - { - $this->userManager->expects($this->once()) - ->method('findUserBy') - ->with($this->equalTo(array('usernameCanonical' => 'jack'))) - ->will($this->returnValue(null)); - - $this->userManager->loadUserByUsername('jack'); - } - - protected function setUp() - { - if (!class_exists('\Doctrine\ODM\MongoDB\DocumentManager')) { - $this->markTestSkipped('No ODM installed'); - } - - $this->userManager = $this->getManagerMock(); - } - - protected function tearDown() - { - unset($this->userManager); - } - - protected function getManagerMock() - { - $methods = array('findUserBy'); - $userManager = $this->getMock('FOS\UserBundle\Document\UserManager', $methods, array(), '', false); - - return $userManager; - } -} diff --git a/Tests/Model/UserManagerTest.php b/Tests/Model/UserManagerTest.php deleted file mode 100644 index 84a39782e6..0000000000 --- a/Tests/Model/UserManagerTest.php +++ /dev/null @@ -1,97 +0,0 @@ -encoderFactory = $this->getMockEncoderFactory(); - $this->algorithm = 'sha512'; - $this->usernameCanonicalizer = $this->getMockCanonicalizer(); - $this->emailCanonicalizer = $this->getMockCanonicalizer(); - - $this->manager = $this->getUserManager(array( - $this->encoderFactory, - $this->algorithm, - $this->usernameCanonicalizer, - $this->emailCanonicalizer, - )); - } - - public function testUpdateCanonicalFields() - { - $user = $this->getUser(); - $user->setUsername('Username'); - $user->setEmail('User@Example.com'); - - $this->usernameCanonicalizer->expects($this->once()) - ->method('canonicalize') - ->with('Username') - ->will($this->returnCallback('strtolower')); - - $this->emailCanonicalizer->expects($this->once()) - ->method('canonicalize') - ->with('User@Example.com') - ->will($this->returnCallback('strtolower')); - - $this->manager->updateCanonicalFields($user); - $this->assertEquals('username', $user->getUsernameCanonical()); - $this->assertEquals('user@example.com', $user->getEmailCanonical()); - } - - public function testUpdatePassword() - { - $encoder = $this->getMockPasswordEncoder(); - $user = $this->getUser(); - $user->setPlainPassword('password'); - - $this->encoderFactory->expects($this->once()) - ->method('getEncoder') - ->will($this->returnValue($encoder)); - - $encoder->expects($this->once()) - ->method('encodePassword') - ->with('password', $user->getSalt()) - ->will($this->returnValue('encodedPassword')); - - $this->manager->updatePassword($user); - $this->assertEquals($this->algorithm, $user->getAlgorithm(), '->updatePassword() sets algorithm'); - $this->assertEquals('encodedPassword', $user->getPassword(), '->updatePassword() sets encoded password'); - $this->assertNull($user->getPlainPassword(), '->updatePassword() erases credentials'); - } - - private function getMockCanonicalizer() - { - return $this->getMock('FOS\UserBundle\Util\CanonicalizerInterface'); - } - - private function getMockEncoderFactory() - { - return $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); - } - - private function getMockPasswordEncoder() - { - return $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); - } - - private function getUser() - { - return $this->getMockBuilder('FOS\UserBundle\Model\User') - ->getMockForAbstractClass(); - } - - private function getUserManager(array $args) - { - return $this->getMockBuilder('FOS\UserBundle\Model\UserManager') - ->setConstructorArgs($args) - ->getMockForAbstractClass(); - } -} diff --git a/Tests/Model/UserTest.php b/Tests/Model/UserTest.php deleted file mode 100644 index 07210511f5..0000000000 --- a/Tests/Model/UserTest.php +++ /dev/null @@ -1,46 +0,0 @@ -getUser(); - $this->assertNull($user->getUsername()); - - $user->setUsername('tony'); - $this->assertEquals('tony', $user->getUsername()); - } - - public function testEmail() - { - $user = $this->getUser(); - $this->assertNull($user->getEmail()); - - $user->setEmail('tony@mail.org'); - $this->assertEquals('tony@mail.org', $user->getEmail()); - } - - /** - * @covers FOS\UserBundle\Model\User::getPasswordRequestedAt - * @covers FOS\UserBundle\Model\User::setPasswordRequestedAt - * @covers FOS\UserBundle\Model\User::isPasswordRequestNonExpired - */ - public function testIsPasswordRequestNonExpired() - { - $user = $this->getUser(); - $passwordRequestedAt = new \DateTime('-10 seconds'); - - $user->setPasswordRequestedAt($passwordRequestedAt); - - $this->assertSame($passwordRequestedAt, $user->getPasswordRequestedAt()); - $this->assertTrue($user->isPasswordRequestNonExpired(15)); - $this->assertFalse($user->isPasswordRequestNonExpired(5)); - } - - protected function getUser() - { - return $this->getMockForAbstractClass('FOS\UserBundle\Model\User'); - } -} diff --git a/Tests/Security/Encoder/EncoderFactoryTest.php b/Tests/Security/Encoder/EncoderFactoryTest.php deleted file mode 100644 index 67cc501ff1..0000000000 --- a/Tests/Security/Encoder/EncoderFactoryTest.php +++ /dev/null @@ -1,51 +0,0 @@ -getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface') - ); - - $userAccount = $this->getMock('FOS\UserBundle\Model\UserInterface'); - - $userAccount->expects($this->once()) - ->method('getAlgorithm') - ->will($this->returnValue('sha512')); - - $encoder = $factory->getEncoder($userAccount); - - $expectedEncoder = new MessageDigestPasswordEncoder('sha512', false, 1); - - $this->assertEquals($expectedEncoder->encodePassword('foo', 'bar'), $encoder->encodePassword('foo', 'bar')); - } - - public function testGetEncoderWithGenericAccount() - { - $genericFactory = $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); - $encoder = $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); - - $genericFactory - ->expects($this->once()) - ->method('getEncoder') - ->will($this->returnValue($encoder)) - ; - - $factory = new EncoderFactory(null , false, 1, $genericFactory); - - $this->assertSame($encoder, $factory->getEncoder($this->getMock('Symfony\Component\Security\Core\User\AccountInterface'))); - } -} diff --git a/Tests/Validation/UserValidationTest.php b/Tests/Validation/UserValidationTest.php deleted file mode 100644 index 8fa4137479..0000000000 --- a/Tests/Validation/UserValidationTest.php +++ /dev/null @@ -1,80 +0,0 @@ -getService('fos_user.user_manager')->createUser(); - $violations = $this->getService('validator')->validate($user, 'Registration'); - $this->assertTrue($this->hasViolationForPropertyPath($violations, 'username')); - } - - public function testGoodUsernameValid() - { - $user = $this->getService('fos_user.user_manager')->createUser(); - $user->setUsername(uniqid()); - $violations = $this->getService('validator')->validate($user, 'Registration'); - $this->assertFalse($this->hasViolationForPropertyPath($violations, 'username')); - } - - public function testDuplicatedUsernameFail() - { - $username = uniqid(); - $userManager = $this->getService('fos_user.user_manager'); - $user1 = $userManager->createUser(); - $user1->setUsername($username); - $user1->setEmail(uniqid().'@mail.org'); - $user1->setPlainPassword(uniqid()); - $violations = $this->getService('validator')->validate($user1, 'Registration'); - $this->assertFalse($this->hasViolationForPropertyPath($violations, 'username')); - $userManager->updateUser($user1); - - $user2 = $userManager->createUser(); - $user2->setUsername($username); - $user1->setEmail(uniqid().'@mail.org'); - $user1->setPlainPassword(uniqid()); - $violations = $this->getService('validator')->validate($user2, 'Registration'); - $this->assertTrue($this->hasViolationForPropertyPath($violations, 'username')); - $userManager->deleteUser($user1); - } - - public function testDuplicatedEmailFail() - { - $email = uniqid().'@email.org'; - $userManager = $this->getService('fos_user.user_manager'); - $user1 = $userManager->createUser(); - $user1->setUsername(uniqid()); - $user1->setPlainPassword(uniqid()); - $user1->setEmail($email); - $violations = $this->getService('validator')->validate($user1, 'Registration'); - $this->assertFalse($this->hasViolationForPropertyPath($violations, 'email')); - $userManager->updateUser($user1); - - $user2 = $userManager->createUser(); - $user2->setUsername(uniqid()); - $user2->setPlainPassword(uniqid()); - $user2->setEmail($email); - $violations = $this->getService('validator')->validate($user2, 'Registration'); - $this->assertTrue($this->hasViolationForPropertyPath($violations, 'email')); - $userManager->deleteUser($user1); - } - - protected function hasViolationForPropertyPath($violations, $propertyPath) - { - if (!is_object($violations)) { - return false; - } - - foreach ($violations as $violation) { - if ($violation->getPropertyPath() == $propertyPath) { - return true; - } - } - - return false; - } -} diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php deleted file mode 100644 index 0388911ed7..0000000000 --- a/Tests/bootstrap.php +++ /dev/null @@ -1,18 +0,0 @@ -registerNamespace('Symfony', $_SERVER['SYMFONY']); -$loader->register(); - -spl_autoload_register(function($class) -{ - if (0 === strpos($class, 'FOS\\UserBundle\\')) { - $path = implode('/', array_slice(explode('\\', $class), 2)).'.php'; - require_once __DIR__.'/../'.$path; - return true; - } -}); diff --git a/Twig/UserExtension.php b/Twig/UserExtension.php deleted file mode 100644 index 425f9917a0..0000000000 --- a/Twig/UserExtension.php +++ /dev/null @@ -1,84 +0,0 @@ -securityContext = $securityContext; - } - - /** - * Returns a list of global functions to add to the existing list. - * - * @return array An array of global functions - */ - public function getFunctions() - { - return array( - 'fos_user_getUser' => new Twig_Function_Method($this, 'getUser'), - 'fos_user_isUser' => new Twig_Function_Method($this, 'isUser'), - 'fos_user_isAuthenticated' => new Twig_Function_Method($this, 'isAuthenticated'), - 'fos_user_isAnonymous' => new Twig_Function_Method($this, 'isAnonymous'), - ); - } - - /** - * Returns the authenticated user, if any - * - * @return FOS\UserBundle\Model\User - */ - public function getUser() - { - return $this->securityContext->getToken()->getUser(); - } - - /** - * Tells whether the authenticated user is this user - * - * @return bool - */ - public function isUser(User $user) - { - $authenticatedUser = $this->getUser(); - - return $authenticatedUser instanceof User && $authenticatedUser->isUser($user); - } - - /** - * Tell whether or not a user is logged in - * - * @return boolean - */ - public function isAuthenticated() - { - return null !== $this->securityContext->getToken(); - } - - /** - * Tell whether or not the user is anonymous - * - * @return boolean - */ - public function isAnonymous() - { - return false === $this->securityContext->vote('IS_AUTHENTICATED_FULLY'); - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName() - { - return 'fos_user'; - } -} diff --git a/Upgrade.md b/Upgrade.md new file mode 100644 index 0000000000..f87e36a709 --- /dev/null +++ b/Upgrade.md @@ -0,0 +1,333 @@ +Upgrade instruction +=================== + +This document describes the changes needed when upgrading because of a BC +break. For the full list of changes, please look at the Changelog file. + +## 2.0 to 2.1 + +Controllers and commands now use DI and are defined as services. + +Projects overriding them will need to adapt their code to fit with these changes. +Note that backward compatibility is not ensured for such usage. Relying on +extension points of the bundle (mostly through events) is preferred. + +## 2.0.0-alpha3 to 2.0.0-beta1 + +Methods and properties removed from `FOS\UserBundle\Model\User` + +- `$locked` +- `$expired` and `$expiredAt` +- `$credentialsExpired` and `$credentialsExpiredAt` +- `setLocked()` and `isLocked()` +- `setExpired()` and `setExpiresAt()` +- `setCredentialsExpired()` and `setCredentialsExpireAt()` + +These properties were used to implement advanced features of the AdvancedUserInterface +from the Symfony component, but neither Symfony nor this bundle are providing +ways to use these features fully (expired credentials would just prevent +logging in for instance). +Projects needing to use these advanced feature should add the fields they +need in their User class and override the corresponding method to provide +an implementation fitting their requirement. Projects wanting to keep the +previous behavior of the bundle can copy the condition used in 1.3.7. + +You need to drop the removed fields from your database schema, because they +aren't mapped anymore. + +### Propel + +Propel integration has been moved to a separate bundle and can be installed using +composer: + +``` +composer require friendsofsymfony/propel1-user-bundle +``` + +Once installed, walk through the +[readme](https://github.com/FriendsOfSymfony/propel1-user-bundle/blob/master/README.md) +to enable Propel support. + +### LoginManager + +The signature of the LoginManager constructor has changed. + +Before: + +```php +class LoginManager +{ + public function __construct( + TokenStorageInterface $tokenStorage, + UserCheckerInterface $userChecker, + SessionAuthenticationStrategyInterface $sessionStrategy, + ContainerInterface $container + ); +} +``` + +After: + +```php +class LoginManager +{ + public function __construct( + TokenStorageInterface $tokenStorage, + UserCheckerInterface $userChecker, + SessionAuthenticationStrategyInterface $sessionStrategy, + RequestStack $requestStack, + RememberMeServicesInterface $rememberMeService = null + ); +} +``` + +### Templates + +Following Symfony coding standards template names are now lowercase. Please +rename if you use custom templates. + +Before: + +```php +public function checkEmailAction() +{ + return $this->render('FOSUserBundle:Registration:checkEmail.html.twig'); +} +``` + +After: + +```php +public function checkEmailAction() +{ + return $this->render('@FOSUser/Registration/check_email.html.twig'); +} +``` + +### UserListener + +The signature of the UserListener constructor has changed and now requires an +implementation of `PasswordUpdaterInterface` and `CanonicalFieldsUpdater`. + +Before: + +```php +class UserListener +{ + public function __construct(ContainerInterface $container); +} + +``` + +After: + +```php +class UserListener +{ + public function __construct( + PasswordUpdaterInterface $passwordUpdater, + CanonicalFieldsUpdater $canonicalFieldsUpdater + ); +} +``` + +### UserManager + +The public methods `refreshUser`, `loadUserByUsername` and `supportsClass` have been +removed. Please use the UserProvider instead. + +Before: + +```php +$userManager->refreshUser($user); +$userManager->loadUserByUsername($username); +$userManager->supportsClass($class); + +``` + +After: + +```php +$userProvider->refreshUser($user); +$userProvider->loadUserByUsername($username); +$userProvider->supportsClass($class); + +``` + +The signature of the UserManager constructor has changed and now requires an +implementation of `PasswordUpdaterInterface` and `CanonicalFieldsUpdater`. + +Before: + +```php +class UserManager +{ + public function __construct( + EncoderFactoryInterface $encoderFactory, + CanonicalizerInterface $usernameCanonicalizer, + CanonicalizerInterface $emailCanonicalizer + ); +} +``` + +After: + +```php +class UserManager +{ + public function __construct( + PasswordUpdaterInterface $passwordUpdater, + CanonicalFieldsUpdater $canonicalFieldsUpdater + ); +} +``` + +### Validator + +The signature of the Validator Initializer constructor has changed and now +requires an implementation of `CanonicalFieldsUpdater`. + +Before: + +```php +class Initializer +{ + public function __construct(UserManagerInterface $userManager); +} +``` + +After: + +```php +class Initializer +{ + public function __construct(CanonicalFieldsUpdater $canonicalFieldsUpdater); +} +``` + +## 1.3 to 2.0.0-alpha1 + +### User Provider + +Using the UserManager as a user provider is no longer supported and is +deprecated. Change your security.yml's provider section to look like: + +```yml +security: + # ... + providers: + fos_userbundle: + id: fos_user.user_provider.username +``` + +### Configuration + +The XML namespace is changed from `http://example.org/schema/dic/fos_user` to +`http://friendsofsymfony.github.io/schema/dic/user`. + +### User and Group class + +This bundle now [registers mappings](http://symfony.com/doc/master/cookbook/doctrine/mapping_model_classes.html) +for the classes in the Model namespace directly, instead +of having empty extending classes. The User and Group classes in the Entity and +Document namespaces are deprecated, you should update your User and Group +classes to extend the classes found in `Model`. +The old classes will are still available for now, but throw deprecated warnings. They will be removed +in the next alpha. + +## 1.3.4 to 1.3.5 + +The characters used in generated tokens have changed. They now include dashes +and underscores as well. Any routing requirement matching them should be +updated to ``[\w\-]+``. + +Before: + +```yaml +my_route: + path: /{token} + requirement: + token: \w+ +``` + +After: + +```yaml +my_route: + path: /{token} + requirement: + token: '[\w\-]+' +``` + +## 1.2 to 1.3 + +### Forms + +The profile form no longer wraps the user in a CheckPassword class. If you +were overriding the form handler, you will need to update it to pass the +user object directly. + +### Groups + +The `FOS\UserBundle\Model\GroupableInterface` interface now expects the `getGroups` +method to return a Traversable instead of expecting a Doctrine Collection. +Doctrine-based implementation are unchanged but the Propel implementation +now returns the PropelCollection instead of wrapping it. + +### Manager classes + +The different Doctrine-based manager classes are deprecated and will be removed +in 2.0 in favor of the common implementation. If you were extending the UserManager +class for a Doctrine implementation, you need to change the parent class +to `FOS\UserBundle\Doctrine\UserManager`. + +### Propel implementation + +The Propel backend does not require the UserProxy anymore as the UserInterface +is now implementated on the model itself. you will have to change your config: + +Before: + +```yaml +fos_user: + user_class: FOS\UserBundle\Propel\UserProxy + propel_user_class: FOS\UserBundle\Propel\User +``` + +After: + +```yaml +fos_user: + user_class: FOS\UserBundle\Propel\User +``` + +### Token generation + +The generation of the token is not done by the User class anymore. If you +were using the `generateToken` or `generateConfirmationToken` in your own +code, you need to use the `fos_user.util.token_generator` service to generate +the token. + +## 1.1 to 1.2 + +This file describes the needed changes when upgrading from 1.1 to 1.2 + +### Removed the user-level algorithm. + +If you are experiencing the exception +`No encoder has been configured for account "Acme\DemoBundle\Entity\User"` +after upgrading, please consider the following. + +The encoder now needs to be configured in the SecurityBundle configuration +as described in the official documentation. If you were using the default +value of the bundle, the config should look like this to reuse the same settings: + +```yaml +#app/config/security.yml +security: + encoders: + "FOS\UserBundle\Model\UserInterface": + algorithm: sha512 + encode_as_base64: false + iterations: 1 +``` diff --git a/Util/Canonicalizer.php b/Util/Canonicalizer.php deleted file mode 100644 index 01b382b239..0000000000 --- a/Util/Canonicalizer.php +++ /dev/null @@ -1,11 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Util; - -use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; -use Symfony\Component\Routing\RouterInterface; -use FOS\UserBundle\Model\UserInterface; - -class Mailer implements MailerInterface -{ - protected $mailer; - protected $router; - protected $templating; - protected $parameters; - - public function __construct($mailer, RouterInterface $router, EngineInterface $templating, array $parameters) - { - $this->mailer = $mailer; - $this->router = $router; - $this->templating = $templating; - $this->parameters = $parameters; - } - - public function sendConfirmationEmailMessage(UserInterface $user, $engine) - { - $template = $this->parameters['confirmation.template']; - $url = $this->router->generate('fos_user_user_confirm', array('token' => $user->getConfirmationToken()), true); - $rendered = $this->templating->render($template.'.txt.'.$engine, array( - 'user' => $user, - 'confirmationUrl' => $url - )); - $this->sendEmailMessage($rendered, $this->getSenderEmail('confirmation'), $user->getEmail()); - } - - public function sendResettingEmailMessage(UserInterface $user, $engine) - { - $template = $this->parameters['resetting_password.template']; - $url = $this->router->generate('fos_user_user_reset_password', array('token' => $user->getConfirmationToken()), true); - $rendered = $this->templating->render($template.'.txt.'.$engine, array( - 'user' => $user, - 'confirmationUrl' => $url - )); - $this->sendEmailMessage($rendered, $this->getSenderEmail('resetting_password'), $user->getEmail()); - } - - protected function sendEmailMessage($renderedTemplate, $fromEmail, $toEmail) - { - // Render the email, use the first line as the subject, and the rest as the body - $renderedLines = explode("\n", trim($renderedTemplate)); - $subject = $renderedLines[0]; - $body = implode("\n", array_slice($renderedLines, 1)); - - $message = \Swift_Message::newInstance() - ->setSubject($subject) - ->setFrom($fromEmail) - ->setTo($toEmail) - ->setBody($body); - - $this->mailer->send($message); - } - - protected function getSenderEmail($type) - { - return $this->parameters['from_email'][$type]; - } -} diff --git a/Util/MailerInterface.php b/Util/MailerInterface.php deleted file mode 100644 index 26cf7958da..0000000000 --- a/Util/MailerInterface.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace FOS\UserBundle\Util; - -use FOS\UserBundle\Model\UserInterface; - -interface MailerInterface -{ - function sendConfirmationEmailMessage(UserInterface $user, $engine); - - function sendResettingEmailMessage(UserInterface $user, $engine); -} diff --git a/Validator/Password.php b/Validator/Password.php deleted file mode 100644 index f207aa18ce..0000000000 --- a/Validator/Password.php +++ /dev/null @@ -1,27 +0,0 @@ -encoderFactory = $factory; - } - - public function isValid($object, Constraint $constraint) - { - if (!is_object($object)) { - throw new \RuntimeException('This is a class constraint.'); - } - - $raw = $object->{$constraint->passwordProperty}; - $user = null === $constraint->userProperty ? $object : $object->{$constraint->userProperty}; - $encoder = $this->encoderFactory->getEncoder($user); - - if (!$encoder->isPasswordValid($user->getPassword(), $raw, $user->getSalt())) { - $this->setMessage($constraint->message); - return false; - } - - return true; - } -} diff --git a/Validator/Unique.php b/Validator/Unique.php deleted file mode 100644 index 5354842ee2..0000000000 --- a/Validator/Unique.php +++ /dev/null @@ -1,34 +0,0 @@ -userManager = $userManager; - } - - /** - * Sets the user manager - * - * @param UserManager $userManager - */ - public function setUserManager(UserManager $userManager) - { - $this->userManager = $userManager; - } - - /** - * Gets the user manager - * - * @return UserManager - */ - public function getUserManager() - { - return $this->userManager; - } - - /** - * Indicates whether the constraint is valid - * - * @param Entity $value - * @param Constraint $constraint - */ - public function isValid($value, Constraint $constraint) - { - if (!$this->getUserManager()->validateUnique($value, $constraint)) { - $this->setMessage($constraint->message, array( - 'property' => $constraint->property - )); - return false; - } - - return true; - } -} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000..ed3bc85bdc --- /dev/null +++ b/composer.json @@ -0,0 +1,77 @@ +{ + "name": "friendsofsymfony/user-bundle", + "type": "symfony-bundle", + "description": "Symfony FOSUserBundle", + "keywords": [ + "User management" + ], + "homepage": "http://friendsofsymfony.github.com", + "license": "MIT", + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + }, + { + "name": "Thibault Duplessis" + }, + { + "name": "FriendsOfSymfony Community", + "homepage": "https://github.com/friendsofsymfony/FOSUserBundle/contributors" + } + ], + "require": { + "php": "^8.1", + "ext-dom": "*", + "ext-json": "*", + "symfony/config": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/event-dispatcher": "^6.4 || ^7.0", + "symfony/event-dispatcher-contracts": "^1.1 || ^2.0 || ^3.0", + "symfony/form": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", + "symfony/http-foundation": "^6.4 || ^7.0", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/options-resolver": "^6.4 || ^7.0", + "symfony/password-hasher": "^6.4 || ^7.0", + "symfony/routing": "^6.4 || ^7.0", + "symfony/security-bundle": "^6.4 || ^7.0", + "symfony/security-core": "^6.4 || ^7.0", + "symfony/translation": "^6.4 || ^7.0", + "symfony/twig-bundle": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "twig/twig": "^2.13 || ^3.0" + }, + "conflict": { + "doctrine/doctrine-bundle": "<1.3", + "doctrine/persistence": "<1.3", + "symfony/doctrine-bridge": "<6.4" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^1.3 || ^2", + "friendsofphp/php-cs-fixer": "^3.0.2, !=3.5.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/mailer": "^6.4 || ^7.0", + "symfony/mime": "^6.4 || ^7.0", + "symfony/phpunit-bridge": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "FOS\\UserBundle\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "FOS\\UserBundle\\Tests\\": "tests" + } + } +} diff --git a/docs/adding_invitation_registration.rst b/docs/adding_invitation_registration.rst new file mode 100644 index 0000000000..b5b66ad0c2 --- /dev/null +++ b/docs/adding_invitation_registration.rst @@ -0,0 +1,268 @@ +FOSUserBundle Invitation +======================== + +Require an invitation to create a new user is a pattern mostly used for +early stage of a project. User enter their invitation code in order to +register. + +Invitation model +---------------- + +First we need to add the invitation entity. An invitation is represented +by a unique code/identifier generated in the constructor: + +.. code-block:: php + + code = substr(md5(uniqid(rand(), true)), 0, 6); + } + + public function getCode() + { + return $this->code; + } + + public function getEmail() + { + return $this->email; + } + + public function setEmail($email) + { + $this->email = $email; + } + + public function isSent() + { + return $this->sent; + } + + public function send() + { + $this->sent = true; + } + } + +Next we map our ``Invitation`` entity to our ``User`` with a one-to-one association: + +.. code-block:: php + + invitation = $invitation; + } + + public function getInvitation() + { + return $this->invitation; + } + } + +Add invitation to RegistrationFormType +-------------------------------------- + +Override the default registration form with your own: + +.. code-block:: php + + add('invitation', InvitationFormType::class); + } + + public function getParent() + { + return RegistrationFormType::class; + } + + public function getBlockPrefix() + { + return 'app_user_registration'; + } + } + +Create the invitation field: + +.. code-block:: php + + entityManager = $entityManager; + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addModelTransformer(new InvitationToCodeTransformer($this->entityManager)); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'class' => 'AppBundle\Entity\Invitation', + 'required' => true, + )); + } + + public function getParent() + { + return TextType::class; + } + + public function getBlockPrefix() + { + return 'app_invitation_type'; + } + } + +Create the custom data transformer: + +.. code-block:: php + + entityManager = $entityManager; + } + + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof Invitation) { + throw new UnexpectedTypeException($value, Invitation::class); + } + + return $value->getCode(); + } + + public function reverseTransform($value) + { + if (null === $value || '' === $value) { + return null; + } + + if (!is_string($value)) { + throw new UnexpectedTypeException($value, 'string'); + } + + $dql = <<entityManager + ->createQuery($dql) + ->setParameter('code', $value) + ->setMaxResults(1) + ->getOneOrNullResult(); + } + } + +Next overwrite the default ``RegistrationFormType`` with the one just created : + +.. code-block:: yaml + + # app/config/config.yml + + fos_user: + registration: + form: + type: AppBundle\Form\RegistrationFormType + +You are done, go to your registration form to see the result. diff --git a/docs/canonicalizer.rst b/docs/canonicalizer.rst new file mode 100644 index 0000000000..418ccc5950 --- /dev/null +++ b/docs/canonicalizer.rst @@ -0,0 +1,47 @@ +FOSUserBundle Canonicalization +============================== + +FOSUserBundle stores canonicalized versions of the username and the email +which are used when querying and checking for uniqueness. +The default implementation simply makes them case-insensitive to avoid having +users whose username only differs because of the case. It uses :phpfunction:`mb_convert_case` +to achieve this result. + +.. caution:: + + If you do not have the mbstring extension installed you will need to + define your own canonicalizer. + +Replacing the canonicalizers +---------------------------- + +If you want to change the way the canonical fields are populated, simply +create a class implementing ``FOS\UserBundle\Util\CanonicalizerInterface`` +and register it as a service: + +.. code-block:: yaml + + # app/config/services.yml + services: + app.my_canonicalizer: + class: AppBundle\Util\CustomCanonicalizer + public: false + + +You can now configure FOSUserBundle to use your own implementation: + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + # ... + service: + email_canonicalizer: app.my_canonicalizer + username_canonicalizer: app.my_canonicalizer + +You can of course use different services for each field if you don't want +to use the same logic. + +.. note:: + + The default implementation has the id ``fos_user.util.canonicalizer.default``. diff --git a/docs/command_line_tools.rst b/docs/command_line_tools.rst new file mode 100644 index 0000000000..0ee48164b9 --- /dev/null +++ b/docs/command_line_tools.rst @@ -0,0 +1,168 @@ +FOSUserBundle Command Line Tools +================================ + +The FOSUserBundle provides a number of command line utilities to help manage your +application's users. Commands are available for the following tasks: + +1. Create a User +2. Activate a User +3. Deactivate a User +4. Promote a User +5. Demote a User +6. Change a User's Password + +.. note:: + + You must have correctly installed and configured the FOSUserBundle before + using these commands. + +.. note:: + + This documentation references the console as ``bin/console``, which is + the Symfony 3 location. If you use Symfony 2.x, use ``app/console`` instead. + +Create a User +------------- + +You can use the ``fos:user:create`` command to create a new user for your application. +The command takes three arguments, the ``username``, ``email``, and ``password`` for +the user you are creating. + +For example if you wanted to create a user with username ``testuser``, with email +``test@example.com`` and password ``p@ssword``, you would run the command as follows. + +.. code-block:: bash + + $ php bin/console fos:user:create testuser test@example.com p@ssword + +If any of the required arguments are not passed to the command, an interactive prompt +will ask you to enter them. For example, if you ran the command as follows, then +you would be prompted to enter the ``email`` and ``password`` for the user +you want to create. + +.. code-block:: bash + + $ php bin/console fos:user:create testuser + +There are two options that you can pass to the command as well. They are +``--super-admin`` and ``--inactive``. + +Specifying the ``--super-admin`` option will flag the user as a super admin when +the user is created. A super admin has access to any part of your application. +An example is provided below: + +.. code-block:: bash + + $ php bin/console fos:user:create adminuser --super-admin + +If you specify the ``--inactive`` option, then the user that you create will no be +able to log in until he is activated. + +.. code-block:: bash + + $ php bin/console fos:user:create testuser --inactive + +Activate a User +--------------- + +The ``fos:user:activate`` command activates an inactive user. The only argument +that the command requires is the ``username`` of the user who should be activated. +If no ``username`` is specified then an interactive prompt will ask you +to enter one. An example of using this command is listed below. + +.. code-block:: bash + + $ php bin/console fos:user:activate testuser + +Deactivate a User +----------------- + +The ``fos:user:deactivate`` command deactivates a user. Just like the activate +command, the only required argument is the ``username`` of the user who should be +activated. If no ``username`` is specified then an interactive prompt will ask you +to enter one. Below is an example of using this command. + +.. code-block:: bash + + $ php bin/console fos:user:deactivate testuser + +Promote a User +-------------- + +The ``fos:user:promote`` command enables you to add a role to a user or make the +user a super administrator. + +If you would like to add a role to a user you simply pass the ``username`` of the +user as the first argument to the command and the ``role`` to add to the user as +the second. + +.. code-block:: bash + + $ php bin/console fos:user:promote testuser ROLE_ADMIN + +You can promote a user to a super administrator by passing the ``--super`` option +after specifying the ``username``. + +.. code-block:: bash + + $ php bin/console fos:user:promote testuser --super + +If any of the arguments to the command are not specified then an interactive +prompt will ask you to enter them. + +.. note:: + + You may not specify the ``role`` argument and the ``--super`` option simultaneously. + +.. caution:: + + Changes will not be applied until the user logs out and back in again. + +Demote a User +------------- + +The ``fos:user:demote`` command is similar to the promote command except that +instead of adding a role to the user it removes it. You can also revoke a user's +super administrator status with this command. + +If you would like to remove a role from a user you simply pass the ``username`` of +the user as the first argument to the command and the ``role`` to remove as the +second. + +.. code-block:: bash + + $ php bin/console fos:user:demote testuser ROLE_ADMIN + +To revoke the super administrator status of a user, simply pass the ``username`` as +an argument to the command as well as the ``--super`` option. + +.. code-block:: bash + + $ php bin/console fos:user:demote testuser --super + +If any of the arguments to the command are not specified then an interactive +prompt will ask you to enter them. + +.. note:: + + You may not specify the ``role`` argument and the ``--super`` option simultaneously. + +.. caution:: + + Changes will not be applied until the user logs out and back in again. This has + implications for the way in which you configure sessions in your application since + you want to ensure that users are demoted as quickly as possible. + +Change a User's Password +------------------------ + +The ``fos:user:change-password`` command provides an easy way to change a user's +password. The command takes two arguments, the ``username`` of the user whose +password you would like to change and the new ``password``. + +.. code-block:: bash + + $ php bin/console fos:user:change-password testuser newp@ssword + +If you do not specify the ``password`` argument then an interactive prompt will +ask you to enter one. diff --git a/docs/configuration_reference.rst b/docs/configuration_reference.rst new file mode 100644 index 0000000000..1d246df6a9 --- /dev/null +++ b/docs/configuration_reference.rst @@ -0,0 +1,59 @@ +FOSUserBundle Configuration Reference +===================================== + +All available configuration options are listed below with their default values. + +.. code-block:: yaml + + fos_user: + db_driver: ~ # Required + firewall_name: ~ # Required + user_class: ~ # Required + use_listener: true + use_flash_notifications: true + use_authentication_listener: true + register_last_login: true + use_username_form_type: true + model_manager_name: null # change it to the name of your entity/document manager if you don't want to use the default one. + from_email: + address: webmaster@example.com + sender_name: webmaster + profile: + form: + type: FOS\UserBundle\Form\Type\ProfileFormType + name: fos_user_profile_form + validation_groups: [Profile, Default] + change_password: + form: + type: FOS\UserBundle\Form\Type\ChangePasswordFormType + name: fos_user_change_password_form + validation_groups: [ChangePassword, Default] + registration: + confirmation: + from_email: # Use this node only if you don't want the global email address for the confirmation email + address: ... + sender_name: ... + enabled: false # change to true for required email confirmation + template: '@FOSUser/Registration/email.txt.twig' + form: + type: FOS\UserBundle\Form\Type\RegistrationFormType + name: fos_user_registration_form + validation_groups: [Registration, Default] + resetting: + retry_ttl: 7200 # Value in seconds, logic will use as hours + token_ttl: 86400 + email: + from_email: # Use this node only if you don't want the global email address for the resetting email + address: ... + sender_name: ... + template: '@FOSUser/Resetting/email.txt.twig' + form: + type: FOS\UserBundle\Form\Type\ResettingFormType + name: fos_user_resetting_form + validation_groups: [ResetPassword, Default] + service: + mailer: fos_user.mailer.default + email_canonicalizer: fos_user.util.canonicalizer.default + username_canonicalizer: fos_user.util.canonicalizer.default + token_generator: fos_user.util.token_generator.default + user_manager: fos_user.user_manager.default diff --git a/docs/controller_events.rst b/docs/controller_events.rst new file mode 100644 index 0000000000..1bf1b2231b --- /dev/null +++ b/docs/controller_events.rst @@ -0,0 +1,108 @@ +Hooking into the controllers +============================ + +The controllers packaged with the FOSUserBundle provide a lot of +functionality that is sufficient for general use cases. But, you might find +that you need to extend that functionality and add some logic that suits the +specific needs of your application. + +For this purpose, the controllers are dispatching events in many places in +their logic. All events can be found in the constants of the +``FOS\UserBundle\FOSUserEvents`` class. + +All controllers follow the same convention: they dispatch a ``SUCCESS`` event +when the form is valid before saving the user, and a ``COMPLETED`` event when +it is done. Thus, all ``SUCCESS`` events allow you to set a response if you +don't want the default redirection. And all ``COMPLETED`` events give you access +to the response before it is returned. + +Controllers with a form also dispatch an ``INITIALIZE`` event after the entity is +fetched, but before the form is created. + +For instance, this listener will change the redirection after the password +resetting to go to the homepage instead of the profile: + +.. code-block:: php + + // src/Acme/UserBundle/EventListener/PasswordResettingListener.php + + namespace Acme\UserBundle\EventListener; + + use FOS\UserBundle\FOSUserEvents; + use FOS\UserBundle\Event\FormEvent; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + use Symfony\Component\HttpFoundation\RedirectResponse; + use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + + /** + * Listener responsible to change the redirection at the end of the password resetting + */ + class PasswordResettingListener implements EventSubscriberInterface + { + private $router; + + public function __construct(UrlGeneratorInterface $router) + { + $this->router = $router; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess', + ); + } + + public function onPasswordResettingSuccess(FormEvent $event) + { + $url = $this->router->generate('homepage'); + + $event->setResponse(new RedirectResponse($url)); + } + } + +You can then register this listener: + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/UserBundle/Resources/config/services.yml + services: + acme_user.password_resetting: + class: Acme\UserBundle\EventListener\PasswordResettingListener + arguments: ['@router'] + tags: + - { name: kernel.event_subscriber } + + .. code-block:: xml + + + + + + + +Registration success listener with enabled confirmation at the same time +------------------------------------------------------------------------ + +When you have registration confirmation and you want to hook up to +``FOSUserEvents::REGISTRATION_SUCCESS`` event you will have to prioritize this listener to be called +before ``FOS\UserBundle\EventListener\EmailConfirmationListener::onRegistrationSuccess``: + +.. code-block:: php + + public static function getSubscribedEvents() + { + return [ + FOSUserEvents::REGISTRATION_SUCCESS => [ + ['onRegistrationSuccess', -10], + ], + ]; + } + +If you don't do it, ``EmailConfirmationListener`` will be called earlier and you will be redirected to +``fos_user_registration_check_email`` route. diff --git a/docs/custom_storage_layer.rst b/docs/custom_storage_layer.rst new file mode 100644 index 0000000000..1f75e05295 --- /dev/null +++ b/docs/custom_storage_layer.rst @@ -0,0 +1,53 @@ +Using a custom storage layer +============================ + +FOSUserBundle has been designed to allow you to easily change the storage +layer used by your application and keep all of the functionality +provided by the bundle. + +Implementing a new storage layer requires providing two classes: the user +implementation and the corresponding user manager. + +The user implementation must implement ``FOS\UserBundle\Model\UserInterface`` +and the user manager must implement ``FOS\UserBundle\Model\UserManagerInterface``. +The ``FOS\UserBundle\Model`` namespace provides base classes to make it easier to +implement these interfaces. + +.. note:: + + You need to take care to always call ``updateCanonicalFields`` and ``updatePassword`` + before saving a user. This is done when calling ``updateUser`` so you will + be safe if you always use the user manager to save the users. + If your storage layer gives you a hook in its saving process, you can use + it to make your implementation more flexible (this is done for Doctrine + using listeners for instance) + +Configuring FOSUserBundle to use your implementation +---------------------------------------------------- + +To use your own implementation, create a service for your user manager. The +following example will assume that its id is ``app.custom_user_manager``. + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + db_driver: custom # custom means that none of the built-in implementation is used + user_class: AppBundle\Model\CustomUser + service: + user_manager: app.custom_user_manager + firewall_name: main + +.. note:: + + Your own service can be a private one. FOSUserBundle will create an alias + to make it available through ``fos_user.user_manager``. + +.. caution:: + + The validation of the uniqueness of the username and email fields is done + using the constraints provided by DoctrineBundle. You will + need to take care of this validation when using a custom storage layer, + using a `custom constraint`_ + +.. _custom constraint: https://symfony.com/doc/current/cookbook/validation/custom_constraint.html diff --git a/docs/doctrine.rst b/docs/doctrine.rst new file mode 100644 index 0000000000..e83e14053a --- /dev/null +++ b/docs/doctrine.rst @@ -0,0 +1,45 @@ +More about Doctrine implementations +=================================== + +FOSUserBundle was first written for Doctrine-based storage layers. This chapter +describes some things specific to these implementations. + +Using a different object manager than the default one +----------------------------------------------------- + +Using the default configuration , FOSUserBundle will use the default doctrine +object manager. If you are using multiple ones and want to handle your users +with a non-default one, you can change the object manager used in the configuration +by giving its name to FOSUserBundle. + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + db_driver: orm + model_manager_name: non_default # the name of your entity manager + +.. note:: + + Using the default object manager is done by setting the configuration + option to ``null`` which is the default value. + +Replacing the mapping of the bundle +----------------------------------- + +None of the Doctrine projects currently allow overwriting part of the mapping +of a mapped superclass in the child entity. + +If you need to change the mapping (for instance to adapt the field names +to a legacy database), one solution could be to write the whole mapping again +without inheriting the mapping from the mapped superclass. In such case, +your entity should extend directly from ``FOS\UserBundle\Model\User``. Another +solution can be through `doctrine attribute and relations overrides`_. + +.. caution:: + + It is highly recommended to map all fields used by the bundle (see the + mapping files of the bundle in ``Resources/config/doctrine/``). Omitting + them can lead to unexpected behaviors and should be done carefully. + +.. _doctrine attribute and relations overrides: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#overrides diff --git a/docs/emails.rst b/docs/emails.rst new file mode 100644 index 0000000000..0a50163296 --- /dev/null +++ b/docs/emails.rst @@ -0,0 +1,213 @@ +FOSUserBundle Emails +==================== + +The FOSUserBundle has built-in support for sending emails in two different +instances. + +Registration Confirmation +------------------------- + +The first is when a new user registers and the bundle is configured +to require email confirmation before the user registration is complete. +The email that is sent to the new user contains a link that, when visited, +will verify the registration and enable the user account. + +Requiring email confirmation for a new account is turned off by default. +To enable it, update your configuration as follows: + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + # ... + registration: + confirmation: + enabled: true + +Password Reset +-------------- + +An email is also sent when a user has requested a password reset. The +FOSUserBundle provides password reset functionality in a two-step process. +First the user must request a password reset. After the request has been +made, an email is sent containing a link to visit. Upon visiting the link, +the user will be identified by the token contained in the url. When the user +visits the link and the token is confirmed, the user will be presented with +a form to enter in a new password. + +Default Mailer Implementations +------------------------------ + +The bundle comes with 2 mailer implementations. They are listed below +by service id: + +- ``fos_user.mailer.twig_symfony`` uses symfony/mailer to send emails and Twig blocks to render the message. +- ``fos_user.mailer.noop`` is a mailer implementation which performs no operation, so no emails are sent. + +.. note:: + + The ``fos_user.mailer.noop`` mailer service should be used in the case + where you do not want the bundle to send emails and you do not want to + include an actual mailer in your app. + +Configuring the Sender Email Address +------------------------------------ + +The FOSUserBundle default mailer allows you to configure the sender email address +of the emails sent out by the bundle. You can configure the address globally or on +a per email basis. + +To configure the sender email address for all emails sent out by the bundle, simply +update your ``fos_user`` config as follows: + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + #... + from_email: + address: noreply@example.com + sender_name: Demo App + +The bundle also provides the flexibility of allowing you to configure the sender +email address for the emails individually. + +To configure the sender email address for the user registration confirmation +email update your ``fos_user`` config as follows: + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + #... + registration: + confirmation: + from_email: + address: registration@example.com + sender_name: Demo Registration + +You can similarly update the ``fos_user`` config to change the sender email address for +the password reset request email: + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + #... + resetting: + email: + from_email: + address: resetting@example.com + sender_name: Demo Resetting + +Sending HTML mails +------------------ + +The default mailers supports sending multipart messages. They expect your twig template +to define 3 blocks: + +- ``subject`` containing the email subject +- ``body_text`` rendering the plain text version of the message +- ``body_html`` rendering the html mail (this block is optional) + +Here is how you can use it, you can use either of the two methods +of referencing the email template below. + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + # ... + service: + mailer: fos_user.mailer.twig_symfony + resetting: + email: + template: email/password_resetting.email.twig + registration: + confirmation: + template: '@FOSUser/Registration/email.txt.twig' + +.. code-block:: html+jinja + + {# app/Resources/views/email/password_resetting.email.twig #} + + {% block subject %}Resetting your password{% endblock %} + + {% block body_text %} + {% autoescape false %} + Hello {{ user.username }} ! + + You can reset your password by accessing {{ confirmationUrl }} + + Greetings, + the App team + {% endautoescape %} + {% endblock %} + + {% block body_html %} + {# + You can of course render the html directly here. + Including a template as done here allows keeping things DRY by using + the template inheritance in it + #} + {% include 'email/password_resetting.html.twig' %} + {% endblock %} + +.. note:: + + The HTML part is set in the message only when the ``body_html`` block is + not empty. + +You can view the default email templates at +`@FOSUser/Registration/email.txt.twig` and +`@FOSUser/Resetting/email.txt.twig` + +Using A Custom Mailer +--------------------- + +If you would like to use a different library to send emails, want to send HTML emails +or simply change the content of the email you may do so by defining your own service. + +First you must create a new class which implements ``FOS\UserBundle\Mailer\MailerInterface`` +which is listed below. + +.. code-block:: php + + + */ + interface MailerInterface + { + /** + * Send an email to a user to confirm the account creation + * + * @param UserInterface $user + */ + function sendConfirmationEmailMessage(UserInterface $user); + + /** + * Send an email to a user to confirm the password reset + * + * @param UserInterface $user + */ + function sendResettingEmailMessage(UserInterface $user); + } + +After you have implemented your custom mailer class and defined it as a service, +you must update your bundle configuration so that FOSUserBundle will use it. +Simply set the ``mailer`` configuration parameter under the ``service`` section. +An example is listed below. + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + # ... + service: + mailer: app.custom_fos_user_mailer diff --git a/docs/form_type.rst b/docs/form_type.rst new file mode 100644 index 0000000000..4b821b4820 --- /dev/null +++ b/docs/form_type.rst @@ -0,0 +1,27 @@ +The username Form Type +====================== + +FOSUserBundle provides a convenient username form type, named ``fos_user_username``. +It appears as a text input, accepts usernames and convert them to a User +instance: + +.. code-block:: php + + class MessageFormType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('recipient', 'FOS\UserBundle\Form\Type\UsernameFormType'); + } + } + +.. note:: + + If you don't use this form type in your app, you can disable it to remove + the service from the container: + + .. code-block:: yaml + + # app/config/config.yml + fos_user: + use_username_form_type: false diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000..d317c44321 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,432 @@ +Getting Started With FOSUserBundle +================================== + +The Symfony Security component provides a flexible security framework that +allows you to load users from configuration, a database, or anywhere else +you can imagine. The FOSUserBundle builds on top of this to make it quick +and easy to store users in a database, as well as functionality for registration, +reset password and a profile page. + +So, if you need to persist and fetch the users in your system to and from +a database, then you're in the right place. + +For a video tutorial, check out `FOSUserBundle FTW`_ by KnpUniversity. + +Prerequisites +------------- + +This version of the bundle requires Symfony 2.8+. If you are using an older +Symfony version, please use the 1.3.x releases of the bundle. + +Translations +~~~~~~~~~~~~ + +If you wish to use default texts provided in this bundle, you have to make +sure you have translator enabled in your config. + +.. code-block:: yaml + + # app/config/config.yml + + framework: + translator: ~ + +For more information about translations, check `Symfony documentation`_. + +Installation +------------ + +Installation is a quick (I promise!) 7 step process: + +1. Download FOSUserBundle using composer +2. Enable the Bundle +3. Create your User class +4. Configure your application's security.yml +5. Configure the FOSUserBundle +6. Import FOSUserBundle routing +7. Update your database schema + +Step 1: Download FOSUserBundle using composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Require the bundle with composer: + +.. code-block:: bash + + $ composer require friendsofsymfony/user-bundle "^3.0" + +Composer will install the bundle to your project's ``vendor/friendsofsymfony/user-bundle`` directory. +If you encounter installation errors pointing at a lack of configuration parameters, such as ``The child node "db_driver" at path "fos_user" must be configured``, you should complete the configuration in Step 5 first and then re-run this step. + +Step 2: Enable the bundle +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Enable the bundle in the kernel: + +.. code-block:: php + + + + + + + + + + + + +.. caution:: + + ``user`` is a reserved keyword in the SQL standard. If you need to use reserved words, surround them with backticks, *e.g.* ``@ORM\Table(name="`user`")`` + +b) MongoDB User class +..................... + +If you're persisting your users via the Doctrine MongoDB ODM, then your ``User`` +class should live in the ``Document`` namespace of your bundle and look like +this to start: + +.. code-block:: php + + + + + + +Only four configuration's nodes are required to use the bundle: + +* The type of datastore you are using (``orm``, ``mongodb`` or ``custom```). +* The firewall name which you configured in Step 4. +* The fully qualified class name (FQCN) of the ``User`` class which you created in Step 3. +* The default email address to use when the bundle send a registration confirmation to the user. + +.. note:: + + FOSUserBundle uses a compiler pass to register mappings for the base + User model classes with the object manager that you configured + it to use. (Unless specified explicitly, this is the default manager + of your doctrine configuration.) + +Step 6: Import FOSUserBundle routing files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that you have activated and configured the bundle, all that is left to do is +import the FOSUserBundle routing files. + +By importing the routing files you will have ready made pages for things such as +logging in, creating users, etc. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing.yml + fos_user: + resource: "@FOSUserBundle/Resources/config/routing/all.xml" + + .. code-block:: xml + + + + +.. note:: + + In order to use the built-in email functionality (confirmation of the account, + resetting of the password), you must activate and configure the mailer. + +Step 7: Update your database schema +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that the bundle is configured, the last thing you need to do is update your +database schema because you have added a new entity, the ``User`` class which you +created in Step 4. + +For ORM run the following command. + +.. code-block:: bash + + $ php bin/console doctrine:schema:update --force + +For MongoDB users you can run the following command to create the indexes. + +.. code-block:: bash + + $ php bin/console doctrine:mongodb:schema:create --index + +.. note:: + + If you use the Symfony 2.x structure in your project, use ``app/console`` + instead of ``bin/console`` in the commands. + +You now can log in at ``http://app.com/app_dev.php/login``! + +Next Steps +~~~~~~~~~~ + +Now that you have completed the basic installation and configuration of the +FOSUserBundle, you are ready to learn about more advanced features and usages +of the bundle. + +The following documents are available: + +.. toctree:: + :maxdepth: 1 + + overriding_templates + controller_events + overriding_forms + user_manager + command_line_tools + logging_by_username_or_email + form_type + emails + doctrine + overriding_validation + canonicalizer + custom_storage_layer + routing + configuration_reference + adding_invitation_registration + +.. _security component documentation: https://symfony.com/doc/current/book/security.html +.. _Symfony documentation: https://symfony.com/doc/current/book/translation.html +.. _TypehintableBehavior: https://github.com/willdurand/TypehintableBehavior +.. _FOSUserBundle FTW: https://knpuniversity.com/screencast/fosuserbundle diff --git a/docs/logging_by_username_or_email.rst b/docs/logging_by_username_or_email.rst new file mode 100644 index 0000000000..f0d8b3f9eb --- /dev/null +++ b/docs/logging_by_username_or_email.rst @@ -0,0 +1,15 @@ +Logging in by Username or Email +=============================== + +As of the version 1.3.0, the bundle provides a built-in user provider implementation +using both the username and email fields. To use it, simply change the id +of your user provider to use this implementation instead of the base one +using only the username: + +.. code-block:: yaml + + # app/config/security.yml + security: + providers: + fos_userbundle: + id: fos_user.user_provider.username_email diff --git a/docs/overriding_forms.rst b/docs/overriding_forms.rst new file mode 100644 index 0000000000..2be2a087b6 --- /dev/null +++ b/docs/overriding_forms.rst @@ -0,0 +1,161 @@ +Overriding Default FOSUserBundle Forms +====================================== + +Overriding a Form Type +---------------------- + +The default forms packaged with the FOSUserBundle provide functionality for +registering new user, updating your profile, changing your password and +much more. These forms work well with the bundle's default classes and controllers. +But, as you start to add more properties to your ``User`` +class or you decide you want to add a few options to the registration form you +will find that you need to override the forms in the bundle. + +Suppose that you have created an ORM user class with the following class name, +``AppBundle\Entity\User``. In this class, you have added a ``name`` property +because you would like to save the user's name as well as their username and +email address. Now, when a user registers for your site they should enter in their +name as well as their username, email and password. Below is an example ``$name`` +property and its validators. + +.. code-block:: php + + // src/AppBundle/Entity/User.php + + use FOS\UserBundle\Model\User as BaseUser; + use Doctrine\ORM\Mapping as ORM; + use Symfony\Component\Validator\Constraints as Assert; + + class User extends BaseUser + { + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\Column(type="string", length=255) + * + * @Assert\NotBlank(message="Please enter your name.", groups={"Registration", "Profile"}) + * @Assert\Length( + * min=3, + * max=255, + * minMessage="The name is too short.", + * maxMessage="The name is too long.", + * groups={"Registration", "Profile"} + * ) + */ + protected $name; + + // ... + } + +.. note:: + + By default, the Registration validation group is used when validating a new + user registration. Unless you have overridden this value in the configuration, + make sure you add the validation group named Registration to your name property. + +If you try and register using the default registration form you will find that +your new ``name`` property is not part of the form. You need to create a custom +form type and configure the bundle to use it. + +The first step is to create a new form type in your own bundle. The following +class inherits from the base FOSUserBundle ``fos_user_registration`` type using +the form type hierarchy and then adds the custom ``name`` field. + +.. code-block:: php + + add('name'); + } + + public function getParent() + { + return 'FOS\UserBundle\Form\Type\RegistrationFormType'; + } + + public function getBlockPrefix() + { + return 'app_user_registration'; + } + } + + +.. note:: + + If you don't want to reuse the fields added in FOSUserBundle by default, + you can omit the ``getParent`` method and configure all fields yourself. + +Now that you have created your custom form type, you must declare it as a service +and add a tag to it. The tag must have a ``name`` value of ``form.type`` and an ``alias`` +value that is the equal to the string returned from the ``getName`` method of your +form type class. The ``alias`` that you specify is what you will use in the FOSUserBundle +configuration to let the bundle know that you want to use your custom form. + +Below is an example of configuring your form type as a service: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + app.form.registration: + class: AppBundle\Form\RegistrationType + tags: + - { name: form.type, alias: app_user_registration } + + .. code-block:: xml + + + + + + + + + + + + + + + + +Finally, you must update the configuration of the FOSUserBundle so that it will +use your form type instead of the default one. Below is the configuration for +changing the registration form type in YAML. + +.. code-block:: yaml + + # app/config/config.yml + fos_user: + # ... + registration: + form: + type: AppBundle\Form\RegistrationType + +Note how the ``alias`` value used in your form type's service configuration tag +is used in the bundle configuration to tell the FOSUserBundle to use your custom +form type. + +.. note:: + + If you need to add some logic to the processing of the form, you can + use a listener :doc:`hooking into the controller `. diff --git a/docs/overriding_templates.rst b/docs/overriding_templates.rst new file mode 100644 index 0000000000..8367757e2a --- /dev/null +++ b/docs/overriding_templates.rst @@ -0,0 +1,96 @@ +Overriding Default FOSUserBundle Templates +========================================== + +As you start to incorporate FOSUserBundle into your application, you will probably +find that you need to override the default templates that are provided by +the bundle. Although the template names are not configurable, Symfony +provides a built-in way to `override the templates themselves`_. + +Example: Overriding The Default layout.html.twig +------------------------------------------------ + +It is highly recommended that you override the ``Resources/views/layout.html.twig`` +template so that the pages provided by the FOSUserBundle have a similar look and +feel to the rest of your application. An example of overriding this layout template +is demonstrated below using both of the overriding options listed above. + +Here is the default ``layout.html.twig`` provided by the FOSUserBundle: + +.. code-block:: html+jinja + + + + + + + +
+ {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} + {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} | + + {{ 'layout.logout'|trans({}, 'FOSUserBundle') }} + + {% else %} + {{ 'layout.login'|trans({}, 'FOSUserBundle') }} + {% endif %} +
+ + {% if app.request.hasPreviousSession %} + {% for type, messages in app.session.flashBag.all %} + {% for message in messages %} +
+ {{ message|trans({}, 'FOSUserBundle') }} +
+ {% endfor %} + {% endfor %} + {% endif %} + +
+ {% block fos_user_content %} + {% endblock fos_user_content %} +
+ + + +As you can see its pretty basic and doesn't really have much structure, so you will +want to replace it with a layout file that is appropriate for your application. The +main thing to note in this template is the block named ``fos_user_content``. This is +the block where the content from each of the different bundle's actions will be +displayed, so you must make sure to include this block in the layout file you will +use to override the default one. + +The following Twig template file is an example of a layout file that might be used +to override the one provided by the bundle. + +.. code-block:: html+jinja + + {% block title %}Demo Application{% endblock %} + + {% block content %} + {% block fos_user_content %}{% endblock %} + {% endblock %} + +This example extends the layout template from the layout of your app. The +``content`` block is where the main content of each page is rendered. This +is why the ``fos_user_content`` block has been placed inside of it. This +will lead to the desired effect of having the output from the FOSUserBundle +actions integrated into our applications layout, preserving the look and +feel of the application. + +The easiest way to override a bundle's template is to simply place a new one in +your ``app/Resources`` folder. To override the layout template located at +``Resources/views/layout.html.twig`` in the ``FOSUserBundle`` directory, you would place +your new layout template at ``app/Resources/FOSUserBundle/views/layout.html.twig``. + +As you can see the pattern for overriding templates in this way is to +create a folder with the name of the bundle class in the ``app/Resources`` directory. +Then add your new template to this folder, preserving the directory structure from the +original bundle. + +After overriding a template, you must clear the cache for the override to +take effect, even in a development environment. + +Overriding all of the other templates provided by the FOSUserBundle can be done +in a similar fashion using either of the two methods shown in this document. + +.. _`override the templates themselves`: https://symfony.com/doc/current/templating/overriding.html diff --git a/docs/overriding_validation.rst b/docs/overriding_validation.rst new file mode 100644 index 0000000000..34edf8cf48 --- /dev/null +++ b/docs/overriding_validation.rst @@ -0,0 +1,6 @@ +Overriding Default FOSUserBundle Validation +=========================================== + +The ``Resources/config/validation.xml`` file contains definitions for custom +validator rules for various classes. The rules defined by FOSUserBundle are +all in validation groups so you can choose not to use them. diff --git a/docs/routing.rst b/docs/routing.rst new file mode 100644 index 0000000000..c80668c974 --- /dev/null +++ b/docs/routing.rst @@ -0,0 +1,40 @@ +Advanced routing configuration +============================== + +By default, the routing file ``@FOSUserBundle/Resources/config/routing/all.xml`` imports +all the routing files and enables all the routes. +In the case you want to enable or disable the different available routes, just use the +single routing configuration files. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing.yml + fos_user_security: + resource: "@FOSUserBundle/Resources/config/routing/security.xml" + + fos_user_profile: + resource: "@FOSUserBundle/Resources/config/routing/profile.xml" + prefix: /profile + + fos_user_register: + resource: "@FOSUserBundle/Resources/config/routing/registration.xml" + prefix: /register + + fos_user_resetting: + resource: "@FOSUserBundle/Resources/config/routing/resetting.xml" + prefix: /resetting + + fos_user_change_password: + resource: "@FOSUserBundle/Resources/config/routing/change_password.xml" + prefix: /profile + + .. code-block:: xml + + + + + + + diff --git a/docs/user_manager.rst b/docs/user_manager.rst new file mode 100644 index 0000000000..1bc6d3dc02 --- /dev/null +++ b/docs/user_manager.rst @@ -0,0 +1,147 @@ +About FOSUserBundle User Manager +================================ + +In order to be storage agnostic, all operations on the user instances are +handled by a user manager implementing ``FOS\UserBundle\Model\UserManagerInterface``. +Using it ensures that your code will continue to work if you change the storage. +The controllers provided by the bundle use the configured user manager instead +of interacting directly with the storage layer. + +If you configure the ``db_driver`` option to ``orm``, this service is an instance +of ``FOS\UserBundle\Doctrine\UserManager``. + +If you configure the ``db_driver`` option to ``mongodb``, this service is an +instance of ``FOS\UserBundle\Doctrine\UserManager``. + +Accessing the User Manager service +---------------------------------- + +The user manager is available in the container as the ``fos_user.user_manager`` +service. + +.. code-block:: php + + $userManager = $container->get('fos_user.user_manager'); + +Creating a new User +------------------- + +A new instance of your User class can be created by the user manager. + +.. code-block:: php + + $user = $userManager->createUser(); + +``$user`` is now an instance of your user class. + +.. note:: + + This method will not work if your user class has some mandatory constructor + arguments. + +Retrieving the users +-------------------- + +The user manager has a few methods to find users based on the unique fields +(username, email and confirmation token) and a method to retrieve all existing +users. + +- ``findUserByUsername($username)`` +- ``findUserByEmail($email)`` +- ``findUserByUsernameOrEmail($value)`` (check if the value looks like an email to choose) +- ``findUserByConfirmationToken($token)`` +- ``findUserBy(array('id'=>$id))`` +- ``findUsers()`` + +To save a user object, you can use the ``updateUser`` method of the user manager. +This method will update the encoded password and the canonical fields and +then persist the changes. + +Updating a User object +---------------------- + +.. code-block:: php + + $user = $userManager->createUser(); + $user->setUsername('John'); + $user->setEmail('john.doe@example.com'); + + $userManager->updateUser($user); + +.. note:: + + To make it easier, the bundle comes with a Doctrine listener handling + the update of the password and the canonical fields for you behind the + scenes. If you always save the user through the user manager, you may + want to disable it to improve performance. + + .. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + fos_user: + # ... + use_listener: false + + .. code-block:: xml + + + + +.. note:: + + For the Doctrine implementations, the default behavior is to flush the + unit of work when calling the ``updateUser`` method. You can disable the + flush by passing a second argument set to ``false``. + This will then be equivalent to calling ``updateCanonicalFields`` and + ``updatePassword``. + +An ORM example: + +.. code-block:: php + + class MainController extends Controller + { + public function updateAction($id) + { + $user = // get a user from the datastore + + $user->setEmail($newEmail); + + $this->get('fos_user.user_manager')->updateUser($user, false); + + // make more modifications to the database + + $this->getDoctrine()->getManager()->flush(); + } + } + +Overriding the User Manager +--------------------------- + +You can replace the default implementation of the user manager by defining +a service implementing ``FOS\UserBundle\Model\UserManagerInterface`` and +setting its id in the configuration. +The id of the default implementation is ``fos_user.user_manager.default`` + +.. code-block:: yaml + + fos_user: + # ... + service: + user_manager: custom_user_manager_id + +Your custom implementation can extend ``FOS\UserBundle\Model\UserManager`` +to reuse the common logic. + +SecurityBundle integration +-------------------------- + +The bundle provides several implementation of ``Symfony\Component\Security\Core\UserProviderInterface`` +on top of the ``UserManagerInterface``. diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ff9f16dd45..b341857dd8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,25 +1,23 @@ - - + + - - ./Tests + + ./tests - ./ + src/ - ./Resources - ./Tests + src/Resources + + + + diff --git a/src/Command/ActivateUserCommand.php b/src/Command/ActivateUserCommand.php new file mode 100644 index 0000000000..8455b64c69 --- /dev/null +++ b/src/Command/ActivateUserCommand.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * @author Antoine Hérault + * + * @internal + */ +#[AsCommand(name: 'fos:user:activate', description: 'Activate a user')] +final class ActivateUserCommand extends Command +{ + private $userManipulator; + + public function __construct(UserManipulator $userManipulator) + { + parent::__construct(); + + $this->userManipulator = $userManipulator; + } + + protected function configure(): void + { + $this + ->setDefinition([ + new InputArgument('username', InputArgument::REQUIRED, 'The username'), + ]) + ->setHelp(<<<'EOT' +The fos:user:activate command activates a user (so they will be able to log in): + + php %command.full_name% matthieu +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $username = $input->getArgument('username'); + + $this->userManipulator->activate($username); + + $output->writeln(sprintf('User "%s" has been activated.', $username)); + + return 0; + } + + protected function interact(InputInterface $input, OutputInterface $output): void + { + if (!$input->getArgument('username')) { + $question = new Question('Please choose a username:'); + $question->setValidator(function ($username) { + if (empty($username)) { + throw new \Exception('Username can not be empty'); + } + + return $username; + }); + + $helper = $this->getHelper('question'); + \assert($helper instanceof QuestionHelper); + $answer = $helper->ask($input, $output, $question); + + $input->setArgument('username', $answer); + } + } +} diff --git a/src/Command/ChangePasswordCommand.php b/src/Command/ChangePasswordCommand.php new file mode 100644 index 0000000000..653bef173d --- /dev/null +++ b/src/Command/ChangePasswordCommand.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * @internal + */ +#[AsCommand(name: 'fos:user:change-password', description: 'Change the password of a user.')] +final class ChangePasswordCommand extends Command +{ + private $userManipulator; + + public function __construct(UserManipulator $userManipulator) + { + parent::__construct(); + + $this->userManipulator = $userManipulator; + } + + protected function configure(): void + { + $this + ->setDefinition([ + new InputArgument('username', InputArgument::REQUIRED, 'The username'), + new InputArgument('password', InputArgument::REQUIRED, 'The password'), + ]) + ->setHelp(<<<'EOT' +The fos:user:change-password command changes the password of a user: + + php %command.full_name% matthieu + +This interactive shell will first ask you for a password. + +You can alternatively specify the password as a second argument: + + php %command.full_name% matthieu mypassword + +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $username = $input->getArgument('username'); + $password = $input->getArgument('password'); + + $this->userManipulator->changePassword($username, $password); + + $output->writeln(sprintf('Changed password for user %s', $username)); + + return 0; + } + + protected function interact(InputInterface $input, OutputInterface $output): void + { + $questions = []; + + if (!$input->getArgument('username')) { + $question = new Question('Please give the username:'); + $question->setValidator(function ($username) { + if (empty($username)) { + throw new \Exception('Username can not be empty'); + } + + return $username; + }); + $questions['username'] = $question; + } + + if (!$input->getArgument('password')) { + $question = new Question('Please enter the new password:'); + $question->setValidator(function ($password) { + if (empty($password)) { + throw new \Exception('Password can not be empty'); + } + + return $password; + }); + $question->setHidden(true); + $questions['password'] = $question; + } + + $helper = $this->getHelper('question'); + \assert($helper instanceof QuestionHelper); + + foreach ($questions as $name => $question) { + $answer = $helper->ask($input, $output, $question); + $input->setArgument($name, $answer); + } + } +} diff --git a/src/Command/CreateUserCommand.php b/src/Command/CreateUserCommand.php new file mode 100644 index 0000000000..3623c18dde --- /dev/null +++ b/src/Command/CreateUserCommand.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * @author Matthieu Bontemps + * @author Thibault Duplessis + * @author Luis Cordova + * + * @internal + */ +#[AsCommand(name: 'fos:user:create', description: 'Create a user.')] +final class CreateUserCommand extends Command +{ + private $userManipulator; + + public function __construct(UserManipulator $userManipulator) + { + parent::__construct(); + + $this->userManipulator = $userManipulator; + } + + protected function configure(): void + { + $this + ->setDefinition([ + new InputArgument('username', InputArgument::REQUIRED, 'The username'), + new InputArgument('email', InputArgument::REQUIRED, 'The email'), + new InputArgument('password', InputArgument::REQUIRED, 'The password'), + new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Set the user as super admin'), + new InputOption('inactive', null, InputOption::VALUE_NONE, 'Set the user as inactive'), + ]) + ->setHelp(<<<'EOT' +The fos:user:create command creates a user: + + php %command.full_name% matthieu + +This interactive shell will ask you for an email and then a password. + +You can alternatively specify the email and password as the second and third arguments: + + php %command.full_name% matthieu matthieu@example.com mypassword + +You can create a super admin via the super-admin flag: + + php %command.full_name% admin --super-admin + +You can create an inactive user (will not be able to log in): + + php %command.full_name% thibault --inactive + +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $username = $input->getArgument('username'); + $email = $input->getArgument('email'); + $password = $input->getArgument('password'); + $inactive = $input->getOption('inactive'); + $superadmin = $input->getOption('super-admin'); + + $this->userManipulator->create($username, $password, $email, !$inactive, $superadmin); + + $output->writeln(sprintf('Created user %s', $username)); + + return 0; + } + + protected function interact(InputInterface $input, OutputInterface $output): void + { + $questions = []; + + if (!$input->getArgument('username')) { + $question = new Question('Please choose a username:'); + $question->setValidator(function ($username) { + if (empty($username)) { + throw new \Exception('Username can not be empty'); + } + + return $username; + }); + $questions['username'] = $question; + } + + if (!$input->getArgument('email')) { + $question = new Question('Please choose an email:'); + $question->setValidator(function ($email) { + if (empty($email)) { + throw new \Exception('Email can not be empty'); + } + + return $email; + }); + $questions['email'] = $question; + } + + if (!$input->getArgument('password')) { + $question = new Question('Please choose a password:'); + $question->setValidator(function ($password) { + if (empty($password)) { + throw new \Exception('Password can not be empty'); + } + + return $password; + }); + $question->setHidden(true); + $questions['password'] = $question; + } + + $helper = $this->getHelper('question'); + \assert($helper instanceof QuestionHelper); + + foreach ($questions as $name => $question) { + $answer = $helper->ask($input, $output, $question); + $input->setArgument($name, $answer); + } + } +} diff --git a/src/Command/DeactivateUserCommand.php b/src/Command/DeactivateUserCommand.php new file mode 100644 index 0000000000..1f5849a7bb --- /dev/null +++ b/src/Command/DeactivateUserCommand.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * @author Antoine Hérault + * + * @internal + */ +#[AsCommand(name: 'fos:user:deactivate', description: 'Deactivate a user')] +final class DeactivateUserCommand extends Command +{ + private $userManipulator; + + public function __construct(UserManipulator $userManipulator) + { + parent::__construct(); + + $this->userManipulator = $userManipulator; + } + + protected function configure(): void + { + $this + ->setDefinition([ + new InputArgument('username', InputArgument::REQUIRED, 'The username'), + ]) + ->setHelp(<<<'EOT' +The fos:user:deactivate command deactivates a user (will not be able to log in) + + php %command.full_name% matthieu +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $username = $input->getArgument('username'); + + $this->userManipulator->deactivate($username); + + $output->writeln(sprintf('User "%s" has been deactivated.', $username)); + + return 0; + } + + protected function interact(InputInterface $input, OutputInterface $output): void + { + if (!$input->getArgument('username')) { + $question = new Question('Please choose a username:'); + $question->setValidator(function ($username) { + if (empty($username)) { + throw new \Exception('Username can not be empty'); + } + + return $username; + }); + + $helper = $this->getHelper('question'); + \assert($helper instanceof QuestionHelper); + $answer = $helper->ask($input, $output, $question); + + $input->setArgument('username', $answer); + } + } +} diff --git a/src/Command/DemoteUserCommand.php b/src/Command/DemoteUserCommand.php new file mode 100644 index 0000000000..99e0a9c629 --- /dev/null +++ b/src/Command/DemoteUserCommand.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Antoine Hérault + * @author Lenar Lõhmus + * + * @internal + */ +#[AsCommand(name: 'fos:user:demote', description: 'Demote a user by removing a role')] +final class DemoteUserCommand extends RoleCommand +{ + protected function configure(): void + { + parent::configure(); + + $this + ->setHelp(<<<'EOT' +The fos:user:demote command demotes a user by removing a role + + php %command.full_name% matthieu ROLE_CUSTOM + php %command.full_name% --super matthieu +EOT + ); + } + + protected function executeRoleCommand(UserManipulator $manipulator, OutputInterface $output, $username, $super, $role): void + { + if ($super) { + $manipulator->demote($username); + $output->writeln(sprintf('User "%s" has been demoted as a simple user. This change will not apply until the user logs out and back in again.', $username)); + } else { + if ($manipulator->removeRole($username, $role)) { + $output->writeln(sprintf('Role "%s" has been removed from user "%s". This change will not apply until the user logs out and back in again.', $role, $username)); + } else { + $output->writeln(sprintf('User "%s" didn\'t have "%s" role.', $username, $role)); + } + } + } +} diff --git a/src/Command/PromoteUserCommand.php b/src/Command/PromoteUserCommand.php new file mode 100644 index 0000000000..fb88ae7b1a --- /dev/null +++ b/src/Command/PromoteUserCommand.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Matthieu Bontemps + * @author Thibault Duplessis + * @author Luis Cordova + * @author Lenar Lõhmus + * + * @internal + */ +#[AsCommand(name: 'fos:user:promote', description: 'Promotes a user by adding a role')] +final class PromoteUserCommand extends RoleCommand +{ + protected function configure(): void + { + parent::configure(); + + $this + ->setHelp(<<<'EOT' +The fos:user:promote command promotes a user by adding a role + + php %command.full_name% matthieu ROLE_CUSTOM + php %command.full_name% --super matthieu +EOT + ); + } + + protected function executeRoleCommand(UserManipulator $manipulator, OutputInterface $output, $username, $super, $role): void + { + if ($super) { + $manipulator->promote($username); + $output->writeln(sprintf('User "%s" has been promoted as a super administrator. This change will not apply until the user logs out and back in again.', $username)); + } else { + if ($manipulator->addRole($username, $role)) { + $output->writeln(sprintf('Role "%s" has been added to user "%s". This change will not apply until the user logs out and back in again.', $role, $username)); + } else { + $output->writeln(sprintf('User "%s" did already have "%s" role.', $username, $role)); + } + } + } +} diff --git a/src/Command/RoleCommand.php b/src/Command/RoleCommand.php new file mode 100644 index 0000000000..793d15a0ee --- /dev/null +++ b/src/Command/RoleCommand.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Command; + +use FOS\UserBundle\Util\UserManipulator; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * @author Lenar Lõhmus + * + * @internal + */ +abstract class RoleCommand extends Command +{ + private $userManipulator; + + public function __construct(UserManipulator $userManipulator) + { + parent::__construct(); + + $this->userManipulator = $userManipulator; + } + + protected function configure(): void + { + $this + ->setDefinition([ + new InputArgument('username', InputArgument::REQUIRED, 'The username'), + new InputArgument('role', InputArgument::OPTIONAL, 'The role'), + new InputOption('super', null, InputOption::VALUE_NONE, 'Instead specifying role, use this to quickly add the super administrator role'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $username = $input->getArgument('username'); + $role = $input->getArgument('role'); + $super = (true === $input->getOption('super')); + + if (null !== $role && $super) { + throw new \InvalidArgumentException('You can pass either the role or the --super option (but not both simultaneously).'); + } + + if (null === $role && !$super) { + throw new \RuntimeException('Not enough arguments.'); + } + + $manipulator = $this->userManipulator; + $this->executeRoleCommand($manipulator, $output, $username, $super, $role); + + return 0; + } + + /** + * @see Command + * + * @param string $username + * @param bool $super + * @param string $role + */ + abstract protected function executeRoleCommand(UserManipulator $manipulator, OutputInterface $output, $username, $super, $role): void; + + protected function interact(InputInterface $input, OutputInterface $output): void + { + $questions = []; + + if (!$input->getArgument('username')) { + $question = new Question('Please choose a username:'); + $question->setValidator(function ($username) { + if (empty($username)) { + throw new \Exception('Username can not be empty'); + } + + return $username; + }); + $questions['username'] = $question; + } + + if ((true !== $input->getOption('super')) && !$input->getArgument('role')) { + $question = new Question('Please choose a role:'); + $question->setValidator(function ($role) { + if (empty($role)) { + throw new \Exception('Role can not be empty'); + } + + return $role; + }); + $questions['role'] = $question; + } + + $helper = $this->getHelper('question'); + \assert($helper instanceof QuestionHelper); + + foreach ($questions as $name => $question) { + $answer = $helper->ask($input, $output, $question); + $input->setArgument($name, $answer); + } + } +} diff --git a/src/Controller/ChangePasswordController.php b/src/Controller/ChangePasswordController.php new file mode 100644 index 0000000000..84525c3600 --- /dev/null +++ b/src/Controller/ChangePasswordController.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Controller; + +use FOS\UserBundle\Event\FilterUserResponseEvent; +use FOS\UserBundle\Event\FormEvent; +use FOS\UserBundle\Event\GetResponseUserEvent; +use FOS\UserBundle\Form\Factory\FactoryInterface; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * Controller managing the password change. + * + * @author Thibault Duplessis + * @author Christophe Coevoet + */ +final class ChangePasswordController extends AbstractController +{ + private $eventDispatcher; + private $formFactory; + private $userManager; + + public function __construct(EventDispatcherInterface $eventDispatcher, FactoryInterface $formFactory, UserManagerInterface $userManager) + { + $this->eventDispatcher = $eventDispatcher; + $this->formFactory = $formFactory; + $this->userManager = $userManager; + } + + /** + * Change user password. + */ + public function changePasswordAction(Request $request): Response + { + $user = $this->getUser(); + if (!is_object($user) || !$user instanceof UserInterface) { + throw new AccessDeniedException('This user does not have access to this section.'); + } + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::CHANGE_PASSWORD_INITIALIZE); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + $form = $this->formFactory->createForm(); + $form->setData($user); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $event = new FormEvent($form, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::CHANGE_PASSWORD_SUCCESS); + + $this->userManager->updateUser($user); + + if (null === $response = $event->getResponse()) { + $url = $this->generateUrl('fos_user_profile_show'); + $response = new RedirectResponse($url); + } + + $this->eventDispatcher->dispatch(new FilterUserResponseEvent($user, $request, $response), FOSUserEvents::CHANGE_PASSWORD_COMPLETED); + + return $response; + } + + return $this->render('@FOSUser/ChangePassword/change_password.html.twig', [ + 'form' => $form->createView(), + ]); + } +} diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php new file mode 100644 index 0000000000..d45f651679 --- /dev/null +++ b/src/Controller/ProfileController.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Controller; + +use FOS\UserBundle\Event\FilterUserResponseEvent; +use FOS\UserBundle\Event\FormEvent; +use FOS\UserBundle\Event\GetResponseUserEvent; +use FOS\UserBundle\Form\Factory\FactoryInterface; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * Controller managing the user profile. + * + * @author Christophe Coevoet + */ +final class ProfileController extends AbstractController +{ + private $eventDispatcher; + private $formFactory; + private $userManager; + + public function __construct(EventDispatcherInterface $eventDispatcher, FactoryInterface $formFactory, UserManagerInterface $userManager) + { + $this->eventDispatcher = $eventDispatcher; + $this->formFactory = $formFactory; + $this->userManager = $userManager; + } + + /** + * Show the user. + */ + public function showAction(): Response + { + $user = $this->getUser(); + if (!is_object($user) || !$user instanceof UserInterface) { + throw new AccessDeniedException('This user does not have access to this section.'); + } + + return $this->render('@FOSUser/Profile/show.html.twig', [ + 'user' => $user, + ]); + } + + /** + * Edit the user. + */ + public function editAction(Request $request): Response + { + $user = $this->getUser(); + if (!is_object($user) || !$user instanceof UserInterface) { + throw new AccessDeniedException('This user does not have access to this section.'); + } + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::PROFILE_EDIT_INITIALIZE); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + $form = $this->formFactory->createForm(); + $form->setData($user); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $event = new FormEvent($form, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::PROFILE_EDIT_SUCCESS); + + $this->userManager->updateUser($user); + + if (null === $response = $event->getResponse()) { + $url = $this->generateUrl('fos_user_profile_show'); + $response = new RedirectResponse($url); + } + + $this->eventDispatcher->dispatch(new FilterUserResponseEvent($user, $request, $response), FOSUserEvents::PROFILE_EDIT_COMPLETED); + + return $response; + } + + return $this->render('@FOSUser/Profile/edit.html.twig', [ + 'form' => $form->createView(), + ]); + } +} diff --git a/src/Controller/RegistrationController.php b/src/Controller/RegistrationController.php new file mode 100644 index 0000000000..4ed4b1fbf7 --- /dev/null +++ b/src/Controller/RegistrationController.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Controller; + +use FOS\UserBundle\Event\FilterUserResponseEvent; +use FOS\UserBundle\Event\FormEvent; +use FOS\UserBundle\Event\GetResponseUserEvent; +use FOS\UserBundle\Form\Factory\FactoryInterface; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * Controller managing the registration. + * + * @author Thibault Duplessis + * @author Christophe Coevoet + */ +final class RegistrationController extends AbstractController +{ + private $eventDispatcher; + private $formFactory; + private $userManager; + private $tokenStorage; + + public function __construct(EventDispatcherInterface $eventDispatcher, FactoryInterface $formFactory, UserManagerInterface $userManager, TokenStorageInterface $tokenStorage) + { + $this->eventDispatcher = $eventDispatcher; + $this->formFactory = $formFactory; + $this->userManager = $userManager; + $this->tokenStorage = $tokenStorage; + } + + public function registerAction(Request $request): Response + { + $user = $this->userManager->createUser(); + $user->setEnabled(true); + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::REGISTRATION_INITIALIZE); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + $form = $this->formFactory->createForm(); + $form->setData($user); + + $form->handleRequest($request); + + if ($form->isSubmitted()) { + if ($form->isValid()) { + $event = new FormEvent($form, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::REGISTRATION_SUCCESS); + + $this->userManager->updateUser($user); + + if (null === $response = $event->getResponse()) { + $url = $this->generateUrl('fos_user_registration_confirmed'); + $response = new RedirectResponse($url); + } + + $this->eventDispatcher->dispatch(new FilterUserResponseEvent($user, $request, $response), FOSUserEvents::REGISTRATION_COMPLETED); + + return $response; + } + + $event = new FormEvent($form, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::REGISTRATION_FAILURE); + + if (null !== $response = $event->getResponse()) { + return $response; + } + } + + return $this->render('@FOSUser/Registration/register.html.twig', [ + 'form' => $form->createView(), + ]); + } + + /** + * Tell the user to check their email provider. + */ + public function checkEmailAction(Request $request): Response + { + $email = $request->getSession()->get('fos_user_send_confirmation_email/email'); + + if (empty($email)) { + return new RedirectResponse($this->generateUrl('fos_user_registration_register')); + } + + $request->getSession()->remove('fos_user_send_confirmation_email/email'); + $user = $this->userManager->findUserByEmail($email); + + if (null === $user) { + return new RedirectResponse($this->container->get('router')->generate('fos_user_security_login')); + } + + return $this->render('@FOSUser/Registration/check_email.html.twig', [ + 'user' => $user, + ]); + } + + /** + * Receive the confirmation token from user email provider, login the user. + * + * @param string $token + */ + public function confirmAction(Request $request, $token): Response + { + $userManager = $this->userManager; + + $user = $userManager->findUserByConfirmationToken($token); + + if (null === $user) { + return new RedirectResponse($this->container->get('router')->generate('fos_user_security_login')); + } + + $user->setConfirmationToken(null); + $user->setEnabled(true); + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::REGISTRATION_CONFIRM); + + $userManager->updateUser($user); + + if (null === $response = $event->getResponse()) { + $url = $this->generateUrl('fos_user_registration_confirmed'); + $response = new RedirectResponse($url); + } + + $this->eventDispatcher->dispatch(new FilterUserResponseEvent($user, $request, $response), FOSUserEvents::REGISTRATION_CONFIRMED); + + return $response; + } + + /** + * Tell the user his account is now confirmed. + */ + public function confirmedAction(Request $request): Response + { + $user = $this->getUser(); + if (!is_object($user) || !$user instanceof UserInterface) { + throw new AccessDeniedException('This user does not have access to this section.'); + } + + return $this->render('@FOSUser/Registration/confirmed.html.twig', [ + 'user' => $user, + 'targetUrl' => $this->getTargetUrlFromSession($request->getSession()), + ]); + } + + private function getTargetUrlFromSession(SessionInterface $session): ?string + { + $token = $this->tokenStorage->getToken(); + + if (null === $token) { + return null; + } + + if (method_exists($token, 'getFirewallName')) { + $firewallName = $token->getFirewallName(); + } else { + return null; + } + + $key = sprintf('_security.%s.target_path', $firewallName); + + if ($session->has($key)) { + return $session->get($key); + } + + return null; + } +} diff --git a/src/Controller/ResettingController.php b/src/Controller/ResettingController.php new file mode 100644 index 0000000000..fd36771a8c --- /dev/null +++ b/src/Controller/ResettingController.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Controller; + +use FOS\UserBundle\Event\FilterUserResponseEvent; +use FOS\UserBundle\Event\FormEvent; +use FOS\UserBundle\Event\GetResponseNullableUserEvent; +use FOS\UserBundle\Event\GetResponseUserEvent; +use FOS\UserBundle\Form\Factory\FactoryInterface; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Mailer\MailerInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use FOS\UserBundle\Util\TokenGeneratorInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * Controller managing the resetting of the password. + * + * @author Thibault Duplessis + * @author Christophe Coevoet + */ +final class ResettingController extends AbstractController +{ + private $eventDispatcher; + private $formFactory; + private $userManager; + private $tokenGenerator; + private $mailer; + + /** + * @var int + */ + private $retryTtl; + + /** + * @param int $retryTtl + */ + public function __construct(EventDispatcherInterface $eventDispatcher, FactoryInterface $formFactory, UserManagerInterface $userManager, TokenGeneratorInterface $tokenGenerator, MailerInterface $mailer, $retryTtl) + { + $this->eventDispatcher = $eventDispatcher; + $this->formFactory = $formFactory; + $this->userManager = $userManager; + $this->tokenGenerator = $tokenGenerator; + $this->mailer = $mailer; + $this->retryTtl = $retryTtl; + } + + /** + * Request reset user password: show form. + */ + public function requestAction(): Response + { + return $this->render('@FOSUser/Resetting/request.html.twig'); + } + + /** + * Request reset user password: submit form and send email. + */ + public function sendEmailAction(Request $request): Response + { + $username = $request->request->get('username'); + + $user = $this->userManager->findUserByUsernameOrEmail($username); + + $event = new GetResponseNullableUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::RESETTING_SEND_EMAIL_INITIALIZE); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + if (null !== $user && !$user->isPasswordRequestNonExpired($this->retryTtl)) { + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::RESETTING_RESET_REQUEST); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + if (null === $user->getConfirmationToken()) { + $user->setConfirmationToken($this->tokenGenerator->generateToken()); + } + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::RESETTING_SEND_EMAIL_CONFIRM); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + $this->mailer->sendResettingEmailMessage($user); + $user->setPasswordRequestedAt(new \DateTime()); + $this->userManager->updateUser($user); + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::RESETTING_SEND_EMAIL_COMPLETED); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + } + + return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email', ['username' => $username])); + } + + /** + * Tell the user to check his email provider. + */ + public function checkEmailAction(Request $request): Response + { + $username = $request->query->get('username'); + + if (empty($username)) { + // the user does not come from the sendEmail action + return new RedirectResponse($this->generateUrl('fos_user_resetting_request')); + } + + return $this->render('@FOSUser/Resetting/check_email.html.twig', [ + 'tokenLifetime' => ceil($this->retryTtl / 3600), + ]); + } + + /** + * Reset user password. + * + * @param string $token + */ + public function resetAction(Request $request, $token): Response + { + $user = $this->userManager->findUserByConfirmationToken($token); + + if (null === $user) { + return new RedirectResponse($this->container->get('router')->generate('fos_user_security_login')); + } + + $event = new GetResponseUserEvent($user, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::RESETTING_RESET_INITIALIZE); + + if (null !== $event->getResponse()) { + return $event->getResponse(); + } + + $form = $this->formFactory->createForm(); + $form->setData($user); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $event = new FormEvent($form, $request); + $this->eventDispatcher->dispatch($event, FOSUserEvents::RESETTING_RESET_SUCCESS); + + $this->userManager->updateUser($user); + + if (null === $response = $event->getResponse()) { + $url = $this->generateUrl('fos_user_profile_show'); + $response = new RedirectResponse($url); + } + + $this->eventDispatcher->dispatch( + new FilterUserResponseEvent($user, $request, $response), + FOSUserEvents::RESETTING_RESET_COMPLETED + ); + + return $response; + } + + return $this->render('@FOSUser/Resetting/reset.html.twig', [ + 'token' => $token, + 'form' => $form->createView(), + ]); + } +} diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000000..afb7d7d9b0 --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; + +/** + * Controller managing security. + * + * @author Thibault Duplessis + * @author Christophe Coevoet + */ +final class SecurityController extends AbstractController +{ + private $authenticationUtils; + private $tokenManager; + + public function __construct(AuthenticationUtils $authenticationUtils, ?CsrfTokenManagerInterface $tokenManager = null) + { + $this->authenticationUtils = $authenticationUtils; + $this->tokenManager = $tokenManager; + } + + public function loginAction(): Response + { + $error = $this->authenticationUtils->getLastAuthenticationError(); + $lastUsername = $this->authenticationUtils->getLastUsername(); + + $csrfToken = $this->tokenManager + ? $this->tokenManager->getToken('authenticate')->getValue() + : null; + + return $this->renderLogin([ + 'last_username' => $lastUsername, + 'error' => $error, + 'csrf_token' => $csrfToken, + ]); + } + + public function checkAction(): never + { + throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.'); + } + + public function logoutAction(): never + { + throw new \RuntimeException('You must activate the logout in your security firewall configuration.'); + } + + /** + * Renders the login template with the given parameters. Overwrite this function in + * an extended controller to provide additional data for the login template. + * + * @param array $data + */ + protected function renderLogin(array $data): Response + { + return $this->render('@FOSUser/Security/login.html.twig', $data); + } +} diff --git a/src/DependencyInjection/Compiler/CheckForSessionPass.php b/src/DependencyInjection/Compiler/CheckForSessionPass.php new file mode 100644 index 0000000000..4a47917dfd --- /dev/null +++ b/src/DependencyInjection/Compiler/CheckForSessionPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Flex\Recipe; + +/** + * Checks to see if the session service exists. + * + * @author Ryan Weaver + * + * @internal + */ +final class CheckForSessionPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if ($container->hasParameter('fos_user.session_needed') && !$container->has('session.storage.factory') && !$container->has('session')) { + $message = 'FOSUserBundle requires the "session" to be available for the enabled features.'; + + if (class_exists(Recipe::class)) { + $message .= ' Uncomment the "session" section in "config/packages/framework.yaml" to activate it.'; + } + + throw new \LogicException($message); + } + + $container->getParameterBag()->remove('fos_user.session_needed'); + } +} diff --git a/src/DependencyInjection/Compiler/InjectRememberMeServicesPass.php b/src/DependencyInjection/Compiler/InjectRememberMeServicesPass.php new file mode 100644 index 0000000000..ac5d382d6c --- /dev/null +++ b/src/DependencyInjection/Compiler/InjectRememberMeServicesPass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Inject RememberMeServices into LoginManager. + * + * @author Vasily Khayrulin + * + * @internal + */ +final class InjectRememberMeServicesPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + $firewallName = $container->getParameter('fos_user.firewall_name'); + \assert(\is_string($firewallName)); + $loginManager = $container->getDefinition('fos_user.security.login_manager'); + + if ($container->has('security.authenticator.remember_me_handler.'.$firewallName)) { + $loginManager->replaceArgument(4, new Reference('security.authenticator.remember_me_handler.'.$firewallName)); + } + } +} diff --git a/src/DependencyInjection/Compiler/InjectUserCheckerPass.php b/src/DependencyInjection/Compiler/InjectUserCheckerPass.php new file mode 100644 index 0000000000..91e32c7456 --- /dev/null +++ b/src/DependencyInjection/Compiler/InjectUserCheckerPass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Injects firewall's UserChecker into LoginManager. + * + * @author Gocha Ossinkine + * + * @internal + */ +final class InjectUserCheckerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + $firewallName = $container->getParameter('fos_user.firewall_name'); + \assert(\is_string($firewallName)); + $loginManager = $container->findDefinition('fos_user.security.login_manager'); + + if ($container->has('security.user_checker.'.$firewallName)) { + $loginManager->replaceArgument(1, new Reference('security.user_checker.'.$firewallName)); + } + } +} diff --git a/src/DependencyInjection/Compiler/ValidationPass.php b/src/DependencyInjection/Compiler/ValidationPass.php new file mode 100644 index 0000000000..17a27fd072 --- /dev/null +++ b/src/DependencyInjection/Compiler/ValidationPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Registers the additional validators according to the storage. + * + * @author Christophe Coevoet + * + * @internal + */ +final class ValidationPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasParameter('fos_user.storage')) { + return; + } + + $storage = $container->getParameter('fos_user.storage'); + \assert(\is_string($storage)); + + if ('custom' === $storage) { + return; + } + + $validationFile = __DIR__.'/../../Resources/config/storage-validation/'.$storage.'.xml'; + + $container->getDefinition('validator.builder') + ->addMethodCall('addXmlMapping', [$validationFile]); + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 0000000000..888f7c51b6 --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\DependencyInjection; + +use FOS\UserBundle\Form\Type; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This class contains the configuration information for the bundle. + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * @author Christophe Coevoet + * + * @internal + */ +final class Configuration implements ConfigurationInterface +{ + public function getConfigTreeBuilder(): TreeBuilder + { + $treeBuilder = new TreeBuilder('fos_user'); + $rootNode = $treeBuilder->getRootNode(); + + $supportedDrivers = ['orm', 'mongodb', 'custom']; + + $rootNode + ->children() + ->scalarNode('db_driver') + ->validate() + ->ifNotInArray($supportedDrivers) + ->thenInvalid('The driver %s is not supported. Please choose one of '.json_encode($supportedDrivers)) + ->end() + ->cannotBeOverwritten() + ->isRequired() + ->cannotBeEmpty() + ->end() + ->scalarNode('user_class')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('firewall_name')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('model_manager_name')->defaultNull()->end() + ->booleanNode('use_authentication_listener')->defaultTrue()->end() + ->booleanNode('register_last_login')->defaultTrue()->end() + ->booleanNode('use_listener')->defaultTrue()->end() + ->booleanNode('use_flash_notifications')->defaultTrue()->end() + ->booleanNode('use_username_form_type')->defaultTrue()->end() + ->arrayNode('from_email') + ->isRequired() + ->children() + ->scalarNode('address')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('sender_name')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + // Using the custom driver requires changing the manager services + ->validate() + ->ifTrue(function ($v) { + return 'custom' === $v['db_driver'] && 'fos_user.user_manager.default' === $v['service']['user_manager']; + }) + ->thenInvalid('You need to specify your own user manager service when using the "custom" driver.') + ->end(); + + $this->addProfileSection($rootNode); + $this->addChangePasswordSection($rootNode); + $this->addRegistrationSection($rootNode); + $this->addResettingSection($rootNode); + $this->addServiceSection($rootNode); + + return $treeBuilder; + } + + private function addProfileSection(ArrayNodeDefinition $node): void + { + $node + ->children() + ->arrayNode('profile') + ->addDefaultsIfNotSet() + ->canBeUnset() + ->children() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->fixXmlConfig('validation_group') + ->children() + ->scalarNode('type')->defaultValue(Type\ProfileFormType::class)->end() + ->scalarNode('name')->defaultValue('fos_user_profile_form')->end() + ->arrayNode('validation_groups') + ->prototype('scalar')->end() + ->defaultValue(['Profile', 'Default']) + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + private function addRegistrationSection(ArrayNodeDefinition $node): void + { + $node + ->children() + ->arrayNode('registration') + ->addDefaultsIfNotSet() + ->canBeUnset() + ->children() + ->arrayNode('confirmation') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled')->defaultFalse()->end() + ->scalarNode('template')->defaultValue('@FOSUser/Registration/email.txt.twig')->end() + ->arrayNode('from_email') + ->canBeUnset() + ->children() + ->scalarNode('address')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('sender_name')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('type')->defaultValue(Type\RegistrationFormType::class)->end() + ->scalarNode('name')->defaultValue('fos_user_registration_form')->end() + ->arrayNode('validation_groups') + ->prototype('scalar')->end() + ->defaultValue(['Registration', 'Default']) + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + private function addResettingSection(ArrayNodeDefinition $node): void + { + $node + ->children() + ->arrayNode('resetting') + ->addDefaultsIfNotSet() + ->canBeUnset() + ->children() + ->scalarNode('retry_ttl')->defaultValue(7200)->end() + ->scalarNode('token_ttl')->defaultValue(86400)->end() + ->arrayNode('email') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('template')->defaultValue('@FOSUser/Resetting/email.txt.twig')->end() + ->arrayNode('from_email') + ->canBeUnset() + ->children() + ->scalarNode('address')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('sender_name')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('type')->defaultValue(Type\ResettingFormType::class)->end() + ->scalarNode('name')->defaultValue('fos_user_resetting_form')->end() + ->arrayNode('validation_groups') + ->prototype('scalar')->end() + ->defaultValue(['ResetPassword', 'Default']) + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + private function addChangePasswordSection(ArrayNodeDefinition $node): void + { + $node + ->children() + ->arrayNode('change_password') + ->addDefaultsIfNotSet() + ->canBeUnset() + ->children() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('type')->defaultValue(Type\ChangePasswordFormType::class)->end() + ->scalarNode('name')->defaultValue('fos_user_change_password_form')->end() + ->arrayNode('validation_groups') + ->prototype('scalar')->end() + ->defaultValue(['ChangePassword', 'Default']) + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + private function addServiceSection(ArrayNodeDefinition $node): void + { + $node + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('service') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('mailer')->defaultNull()->end() + ->scalarNode('email_canonicalizer')->defaultValue('fos_user.util.canonicalizer.default')->end() + ->scalarNode('token_generator')->defaultValue('fos_user.util.token_generator.default')->end() + ->scalarNode('username_canonicalizer')->defaultValue('fos_user.util.canonicalizer.default')->end() + ->scalarNode('user_manager')->defaultValue('fos_user.user_manager.default')->end() + ->end() + ->end() + ->end() + ->end(); + } +} diff --git a/src/DependencyInjection/FOSUserExtension.php b/src/DependencyInjection/FOSUserExtension.php new file mode 100644 index 0000000000..657e3cc021 --- /dev/null +++ b/src/DependencyInjection/FOSUserExtension.php @@ -0,0 +1,260 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\DependencyInjection; + +use FOS\UserBundle\Mailer\MailerInterface; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @internal + */ +final class FOSUserExtension extends Extension +{ + /** + * @var array + */ + private static $doctrineDrivers = [ + 'orm' => [ + 'registry' => 'doctrine', + 'tag' => 'doctrine.event_listener', + ], + 'mongodb' => [ + 'registry' => 'doctrine_mongodb', + 'tag' => 'doctrine_mongodb.odm.event_listener', + ], + ]; + + private bool $mailerNeeded = false; + private bool $sessionNeeded = false; + + public function load(array $configs, ContainerBuilder $container): void + { + $processor = new Processor(); + $configuration = new Configuration(); + + $config = $processor->processConfiguration($configuration, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + if ('custom' !== $config['db_driver']) { + if (isset(self::$doctrineDrivers[$config['db_driver']])) { + $loader->load('doctrine.xml'); + $container->setAlias('fos_user.doctrine_registry', new Alias(self::$doctrineDrivers[$config['db_driver']]['registry'], false)); + } else { + $loader->load(sprintf('%s.xml', $config['db_driver'])); + } + $container->setParameter($this->getAlias().'.backend_type_'.$config['db_driver'], true); + } + + if (isset(self::$doctrineDrivers[$config['db_driver']])) { + $definition = $container->getDefinition('fos_user.object_manager'); + $definition->setFactory([new Reference('fos_user.doctrine_registry'), 'getManager']); + } + + foreach (['validator', 'security', 'util', 'mailer', 'listeners', 'commands'] as $basename) { + $loader->load(sprintf('%s.xml', $basename)); + } + + if (!$config['use_authentication_listener']) { + $container->removeDefinition('fos_user.listener.authentication'); + } + + if (!$config['register_last_login']) { + $container->removeDefinition('fos_user.security.interactive_login_listener'); + } + + if ($config['use_flash_notifications']) { + $this->sessionNeeded = true; + $loader->load('flash_notifications.xml'); + } + + $container->setAlias('fos_user.util.email_canonicalizer', $config['service']['email_canonicalizer']); + $container->setAlias('fos_user.util.username_canonicalizer', $config['service']['username_canonicalizer']); + $container->setAlias('fos_user.util.token_generator', $config['service']['token_generator']); + $container->setAlias('fos_user.user_manager', new Alias($config['service']['user_manager'], true)); + + if ($config['use_listener'] && isset(self::$doctrineDrivers[$config['db_driver']])) { + $listenerDefinition = $container->getDefinition('fos_user.user_listener'); + $listenerDefinition->addTag(self::$doctrineDrivers[$config['db_driver']]['tag'], ['event' => 'prePersist']); + $listenerDefinition->addTag(self::$doctrineDrivers[$config['db_driver']]['tag'], ['event' => 'preUpdate']); + } + + if ($config['use_username_form_type']) { + $loader->load('username_form_type.xml'); + } + + $this->remapParametersNamespaces($config, $container, [ + '' => [ + 'db_driver' => 'fos_user.storage', + 'firewall_name' => 'fos_user.firewall_name', + 'model_manager_name' => 'fos_user.model_manager_name', + 'user_class' => 'fos_user.model.user.class', + ], + ]); + + if (!empty($config['profile'])) { + $this->loadProfile($config['profile'], $container, $loader); + } + + if (!empty($config['registration'])) { + $this->loadRegistration($config['registration'], $container, $loader, $config['from_email']); + } + + if (!empty($config['change_password'])) { + $this->loadChangePassword($config['change_password'], $container, $loader); + } + + if (!empty($config['resetting'])) { + $this->loadResetting($config['resetting'], $container, $loader, $config['from_email']); + } + + if ($this->mailerNeeded && null === $config['service']['mailer']) { + throw new \LogicException('Configuring the mailer service for FOSUserBundle is mandatory when enabling features that need the mailer.'); + } + + if (null !== $config['service']['mailer']) { + $container->setAlias('fos_user.mailer', $config['service']['mailer']); + $container->setAlias(MailerInterface::class, new Alias('fos_user.mailer', false)); + } + + if ($this->sessionNeeded) { + $container->setParameter('fos_user.session_needed', true); + } + } + + public function getNamespace(): string + { + return 'http://friendsofsymfony.github.io/schema/dic/user'; + } + + /** + * /** + * @param array $config + * @param array $map + */ + protected function remapParameters(array $config, ContainerBuilder $container, array $map): void + { + foreach ($map as $name => $paramName) { + if (array_key_exists($name, $config)) { + $container->setParameter($paramName, $config[$name]); + } + } + } + + /** + * @param array $config + * @param array> $namespaces + */ + protected function remapParametersNamespaces(array $config, ContainerBuilder $container, array $namespaces): void + { + foreach ($namespaces as $ns => $map) { + if ($ns) { + if (!array_key_exists($ns, $config)) { + continue; + } + $namespaceConfig = $config[$ns]; + } else { + $namespaceConfig = $config; + } + if (is_array($map)) { + $this->remapParameters($namespaceConfig, $container, $map); + } else { + foreach ($namespaceConfig as $name => $value) { + $container->setParameter(sprintf($map, $name), $value); + } + } + } + } + + /** + * @param array $config + */ + private function loadProfile(array $config, ContainerBuilder $container, XmlFileLoader $loader): void + { + $loader->load('profile.xml'); + + $this->remapParametersNamespaces($config, $container, [ + 'form' => 'fos_user.profile.form.%s', + ]); + } + + /** + * @param array $config + * @param array{address: string, sender_name: string} $fromEmail + */ + private function loadRegistration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $fromEmail): void + { + $loader->load('registration.xml'); + $this->sessionNeeded = true; + + if ($config['confirmation']['enabled']) { + $this->mailerNeeded = true; + $loader->load('email_confirmation.xml'); + } + + if (isset($config['confirmation']['from_email'])) { + // overwrite the global one + $fromEmail = $config['confirmation']['from_email']; + unset($config['confirmation']['from_email']); + } + $container->setParameter('fos_user.registration.confirmation.from_address', $fromEmail); + + $this->remapParametersNamespaces($config, $container, [ + 'confirmation' => 'fos_user.registration.confirmation.%s', + 'form' => 'fos_user.registration.form.%s', + ]); + } + + /** + * @param array $config + */ + private function loadChangePassword(array $config, ContainerBuilder $container, XmlFileLoader $loader): void + { + $loader->load('change_password.xml'); + + $this->remapParametersNamespaces($config, $container, [ + 'form' => 'fos_user.change_password.form.%s', + ]); + } + + /** + * @param array $config + * @param array{address: string, sender_name: string} $fromEmail + */ + private function loadResetting(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $fromEmail): void + { + $this->mailerNeeded = true; + $loader->load('resetting.xml'); + + if (isset($config['email']['from_email'])) { + // overwrite the global one + $fromEmail = $config['email']['from_email']; + unset($config['email']['from_email']); + } + $container->setParameter('fos_user.resetting.email.from_address', $fromEmail); + + $this->remapParametersNamespaces($config, $container, [ + '' => [ + 'retry_ttl' => 'fos_user.resetting.retry_ttl', + 'token_ttl' => 'fos_user.resetting.token_ttl', + ], + 'email' => 'fos_user.resetting.email.%s', + 'form' => 'fos_user.resetting.form.%s', + ]); + } +} diff --git a/src/Doctrine/UserListener.php b/src/Doctrine/UserListener.php new file mode 100644 index 0000000000..900f2a4973 --- /dev/null +++ b/src/Doctrine/UserListener.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Doctrine; + +use Doctrine\Common\EventSubscriber; +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ORM\EntityManager; +use Doctrine\Persistence\Event\LifecycleEventArgs; +use Doctrine\Persistence\ObjectManager; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Util\CanonicalFieldsUpdater; +use FOS\UserBundle\Util\PasswordUpdaterInterface; + +/** + * Doctrine listener updating the canonical username and password fields. + * + * @author Christophe Coevoet + * @author David Buchmann + * + * @internal + */ +final class UserListener implements EventSubscriber +{ + private $passwordUpdater; + private $canonicalFieldsUpdater; + + public function __construct(PasswordUpdaterInterface $passwordUpdater, CanonicalFieldsUpdater $canonicalFieldsUpdater) + { + $this->passwordUpdater = $passwordUpdater; + $this->canonicalFieldsUpdater = $canonicalFieldsUpdater; + } + + public function getSubscribedEvents(): array + { + return [ + 'prePersist', + 'preUpdate', + ]; + } + + /** + * Pre persist listener based on doctrine common. + * + * @param LifecycleEventArgs $args + */ + public function prePersist(LifecycleEventArgs $args): void + { + $object = $args->getObject(); + if ($object instanceof UserInterface) { + $this->updateUserFields($object); + } + } + + /** + * Pre update listener based on doctrine common. + * + * @param LifecycleEventArgs $args + */ + public function preUpdate(LifecycleEventArgs $args): void + { + $object = $args->getObject(); + if ($object instanceof UserInterface) { + $this->updateUserFields($object); + $this->recomputeChangeSet($args->getObjectManager(), $object); + } + } + + /** + * Updates the user properties. + */ + private function updateUserFields(UserInterface $user): void + { + $this->canonicalFieldsUpdater->updateCanonicalFields($user); + $this->passwordUpdater->hashPassword($user); + } + + /** + * Recomputes change set for Doctrine implementations not doing it automatically after the event. + */ + private function recomputeChangeSet(ObjectManager $om, UserInterface $user): void + { + if ($om instanceof EntityManager) { + $meta = $om->getClassMetadata(get_class($user)); + $om->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $user); + + return; + } + + if ($om instanceof DocumentManager) { + $meta = $om->getClassMetadata(get_class($user)); + $om->getUnitOfWork()->recomputeSingleDocumentChangeSet($meta, $user); + } + } +} diff --git a/src/Doctrine/UserManager.php b/src/Doctrine/UserManager.php new file mode 100644 index 0000000000..eda9cb9583 --- /dev/null +++ b/src/Doctrine/UserManager.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Doctrine; + +use Doctrine\Persistence\ObjectManager; +use Doctrine\Persistence\ObjectRepository; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManager as BaseUserManager; +use FOS\UserBundle\Util\CanonicalFieldsUpdater; +use FOS\UserBundle\Util\PasswordUpdaterInterface; + +class UserManager extends BaseUserManager +{ + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * @var string + */ + private $class; + + /** + * Constructor. + * + * @param string $class + */ + public function __construct(PasswordUpdaterInterface $passwordUpdater, CanonicalFieldsUpdater $canonicalFieldsUpdater, ObjectManager $om, $class) + { + parent::__construct($passwordUpdater, $canonicalFieldsUpdater); + + $this->objectManager = $om; + $this->class = $class; + } + + public function deleteUser(UserInterface $user): void + { + $this->objectManager->remove($user); + $this->objectManager->flush(); + } + + /** + * @phpstan-return class-string + */ + public function getClass(): string + { + if (false !== strpos($this->class, ':')) { + $metadata = $this->objectManager->getClassMetadata($this->class); + $this->class = $metadata->getName(); + } + + return $this->class; + } + + public function findUserBy(array $criteria): ?UserInterface + { + return $this->getRepository()->findOneBy($criteria); + } + + /** + * @return iterable + */ + public function findUsers(): iterable + { + return $this->getRepository()->findAll(); + } + + public function reloadUser(UserInterface $user): void + { + $this->objectManager->refresh($user); + } + + public function updateUser(UserInterface $user, $andFlush = true): void + { + $this->updateCanonicalFields($user); + $this->updatePassword($user); + + $this->objectManager->persist($user); + if ($andFlush) { + $this->objectManager->flush(); + } + } + + /** + * @return ObjectRepository + */ + protected function getRepository(): ObjectRepository + { + return $this->objectManager->getRepository($this->getClass()); + } +} diff --git a/src/Event/FilterUserResponseEvent.php b/src/Event/FilterUserResponseEvent.php new file mode 100644 index 0000000000..5cf7e0af68 --- /dev/null +++ b/src/Event/FilterUserResponseEvent.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Event; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +final class FilterUserResponseEvent extends UserEvent +{ + private $response; + + /** + * FilterUserResponseEvent constructor. + */ + public function __construct(UserInterface $user, Request $request, Response $response) + { + parent::__construct($user, $request); + $this->response = $response; + } + + public function getResponse(): Response + { + return $this->response; + } + + /** + * Sets a new response object. + */ + public function setResponse(Response $response): void + { + $this->response = $response; + } +} diff --git a/src/Event/FormEvent.php b/src/Event/FormEvent.php new file mode 100644 index 0000000000..fd70535e1a --- /dev/null +++ b/src/Event/FormEvent.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Event; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\EventDispatcher\Event; + +final class FormEvent extends Event +{ + /** + * @var FormInterface + */ + private $form; + + /** + * @var Request + */ + private $request; + + /** + * @var Response + */ + private $response; + + /** + * FormEvent constructor. + */ + public function __construct(FormInterface $form, Request $request) + { + $this->form = $form; + $this->request = $request; + } + + public function getForm(): FormInterface + { + return $this->form; + } + + public function getRequest(): Request + { + return $this->request; + } + + public function setResponse(Response $response): void + { + $this->response = $response; + } + + public function getResponse(): ?Response + { + return $this->response; + } +} diff --git a/src/Event/GetResponseNullableUserEvent.php b/src/Event/GetResponseNullableUserEvent.php new file mode 100644 index 0000000000..8308b97c7e --- /dev/null +++ b/src/Event/GetResponseNullableUserEvent.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Event; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Contracts\EventDispatcher\Event; + +/** + * Response user event that allows null user. + * + * @author Konstantinos Christofilos + */ +final class GetResponseNullableUserEvent extends Event +{ + /** + * @var Request + */ + private $request; + + /** + * @var UserInterface|null + */ + private $user; + + /** + * @var Response|null + */ + private $response; + + public function __construct(?UserInterface $user, Request $request) + { + $this->user = $user; + $this->request = $request; + } + + public function getUser(): ?UserInterface + { + return $this->user; + } + + public function getRequest(): Request + { + return $this->request; + } + + public function setResponse(Response $response): void + { + $this->response = $response; + } + + public function getResponse(): ?Response + { + return $this->response; + } +} diff --git a/src/Event/GetResponseUserEvent.php b/src/Event/GetResponseUserEvent.php new file mode 100644 index 0000000000..48bca6460e --- /dev/null +++ b/src/Event/GetResponseUserEvent.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Event; + +use Symfony\Component\HttpFoundation\Response; + +final class GetResponseUserEvent extends UserEvent +{ + /** + * @var Response|null + */ + private $response; + + public function setResponse(Response $response): void + { + $this->response = $response; + } + + public function getResponse(): ?Response + { + return $this->response; + } +} diff --git a/src/Event/UserEvent.php b/src/Event/UserEvent.php new file mode 100644 index 0000000000..fc57bfb61f --- /dev/null +++ b/src/Event/UserEvent.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Event; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Contracts\EventDispatcher\Event; + +class UserEvent extends Event +{ + /** + * @var Request|null + */ + protected $request; + + /** + * @var UserInterface + */ + protected $user; + + public function __construct(UserInterface $user, ?Request $request = null) + { + $this->user = $user; + $this->request = $request; + } + + public function getUser(): UserInterface + { + return $this->user; + } + + public function getRequest(): ?Request + { + return $this->request; + } +} diff --git a/src/EventListener/AuthenticationListener.php b/src/EventListener/AuthenticationListener.php new file mode 100644 index 0000000000..b817942fd0 --- /dev/null +++ b/src/EventListener/AuthenticationListener.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\EventListener; + +use FOS\UserBundle\Event\FilterUserResponseEvent; +use FOS\UserBundle\Event\UserEvent; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Security\LoginManagerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Exception\AccountStatusException; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * @internal + */ +final class AuthenticationListener implements EventSubscriberInterface +{ + /** + * @var LoginManagerInterface + */ + private $loginManager; + + /** + * @var string + */ + private $firewallName; + + /** + * AuthenticationListener constructor. + * + * @param string $firewallName + */ + public function __construct(LoginManagerInterface $loginManager, $firewallName) + { + $this->loginManager = $loginManager; + $this->firewallName = $firewallName; + } + + public static function getSubscribedEvents(): array + { + return [ + FOSUserEvents::REGISTRATION_COMPLETED => 'authenticate', + FOSUserEvents::REGISTRATION_CONFIRMED => 'authenticate', + FOSUserEvents::RESETTING_RESET_COMPLETED => 'authenticate', + ]; + } + + /** + * @param string $eventName + */ + public function authenticate(FilterUserResponseEvent $event, $eventName, EventDispatcherInterface $eventDispatcher): void + { + try { + $this->loginManager->logInUser($this->firewallName, $event->getUser(), $event->getResponse()); + + $eventDispatcher->dispatch(new UserEvent($event->getUser(), $event->getRequest()), FOSUserEvents::SECURITY_IMPLICIT_LOGIN); + } catch (AccountStatusException $ex) { + // We simply do not authenticate users which do not pass the user + // checker (not enabled, expired, etc.). + } + } +} diff --git a/src/EventListener/EmailConfirmationListener.php b/src/EventListener/EmailConfirmationListener.php new file mode 100644 index 0000000000..450c8207a4 --- /dev/null +++ b/src/EventListener/EmailConfirmationListener.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\EventListener; + +use FOS\UserBundle\Event\FormEvent; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Mailer\MailerInterface; +use FOS\UserBundle\Util\TokenGeneratorInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * @internal + */ +final class EmailConfirmationListener implements EventSubscriberInterface +{ + private $mailer; + private $tokenGenerator; + private $router; + + /** + * EmailConfirmationListener constructor. + */ + public function __construct(MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator, UrlGeneratorInterface $router) + { + $this->mailer = $mailer; + $this->tokenGenerator = $tokenGenerator; + $this->router = $router; + } + + public static function getSubscribedEvents(): array + { + return [ + FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess', + ]; + } + + public function onRegistrationSuccess(FormEvent $event): void + { + /** @var \FOS\UserBundle\Model\UserInterface $user */ + $user = $event->getForm()->getData(); + + $user->setEnabled(false); + if (null === $user->getConfirmationToken()) { + $user->setConfirmationToken($this->tokenGenerator->generateToken()); + } + + $this->mailer->sendConfirmationEmailMessage($user); + + $event->getRequest()->getSession()->set('fos_user_send_confirmation_email/email', $user->getEmail()); + + $url = $this->router->generate('fos_user_registration_check_email'); + $event->setResponse(new RedirectResponse($url)); + } +} diff --git a/src/EventListener/FlashListener.php b/src/EventListener/FlashListener.php new file mode 100644 index 0000000000..ff8a5b5c65 --- /dev/null +++ b/src/EventListener/FlashListener.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\EventListener; + +use FOS\UserBundle\FOSUserEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Contracts\EventDispatcher\Event; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * @internal + */ +final class FlashListener implements EventSubscriberInterface +{ + /** + * @var string[] + */ + private static $successMessages = [ + FOSUserEvents::CHANGE_PASSWORD_COMPLETED => 'change_password.flash.success', + FOSUserEvents::PROFILE_EDIT_COMPLETED => 'profile.flash.updated', + FOSUserEvents::REGISTRATION_COMPLETED => 'registration.flash.user_created', + FOSUserEvents::RESETTING_RESET_COMPLETED => 'resetting.flash.success', + ]; + + /** + * @var RequestStack + */ + private $requestStack; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * FlashListener constructor. + */ + public function __construct(RequestStack $requestStack, TranslatorInterface $translator) + { + $this->translator = $translator; + $this->requestStack = $requestStack; + } + + public static function getSubscribedEvents(): array + { + return [ + FOSUserEvents::CHANGE_PASSWORD_COMPLETED => 'addSuccessFlash', + FOSUserEvents::PROFILE_EDIT_COMPLETED => 'addSuccessFlash', + FOSUserEvents::REGISTRATION_COMPLETED => 'addSuccessFlash', + FOSUserEvents::RESETTING_RESET_COMPLETED => 'addSuccessFlash', + ]; + } + + /** + * @param string $eventName + */ + public function addSuccessFlash(Event $event, $eventName): void + { + if (!isset(self::$successMessages[$eventName])) { + throw new \InvalidArgumentException('This event does not correspond to a known flash message'); + } + + $this->getSession()->getFlashBag()->add('success', $this->trans(self::$successMessages[$eventName])); + } + + private function getSession(): Session + { + $request = $this->requestStack->getCurrentRequest(); + + if (null === $request) { + throw new \LogicException('Cannot get the session without an active request.'); + } + + return $request->getSession(); + } + + /** + * @param string $message + */ + private function trans($message): string + { + return $this->translator->trans($message, [], 'FOSUserBundle'); + } +} diff --git a/src/EventListener/LastLoginListener.php b/src/EventListener/LastLoginListener.php new file mode 100644 index 0000000000..e1c8d3cbf3 --- /dev/null +++ b/src/EventListener/LastLoginListener.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\EventListener; + +use FOS\UserBundle\Event\UserEvent; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\SecurityEvents; + +/** + * @internal + */ +final class LastLoginListener implements EventSubscriberInterface +{ + /** + * @var UserManagerInterface + */ + protected $userManager; + + /** + * LastLoginListener constructor. + */ + public function __construct(UserManagerInterface $userManager) + { + $this->userManager = $userManager; + } + + public static function getSubscribedEvents(): array + { + return [ + FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'onImplicitLogin', + SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin', + ]; + } + + public function onImplicitLogin(UserEvent $event): void + { + $user = $event->getUser(); + + $user->setLastLogin(new \DateTime()); + $this->userManager->updateUser($user); + } + + public function onSecurityInteractiveLogin(InteractiveLoginEvent $event): void + { + $user = $event->getAuthenticationToken()->getUser(); + + if ($user instanceof UserInterface) { + $user->setLastLogin(new \DateTime()); + $this->userManager->updateUser($user); + } + } +} diff --git a/src/EventListener/ResettingListener.php b/src/EventListener/ResettingListener.php new file mode 100644 index 0000000000..758553eeb5 --- /dev/null +++ b/src/EventListener/ResettingListener.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\EventListener; + +use FOS\UserBundle\Event\FormEvent; +use FOS\UserBundle\Event\GetResponseUserEvent; +use FOS\UserBundle\FOSUserEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * @internal + */ +final class ResettingListener implements EventSubscriberInterface +{ + /** + * @var UrlGeneratorInterface + */ + private $router; + + /** + * @var int + */ + private $tokenTtl; + + /** + * ResettingListener constructor. + * + * @param int $tokenTtl + */ + public function __construct(UrlGeneratorInterface $router, $tokenTtl) + { + $this->router = $router; + $this->tokenTtl = $tokenTtl; + } + + public static function getSubscribedEvents(): array + { + return [ + FOSUserEvents::RESETTING_RESET_INITIALIZE => 'onResettingResetInitialize', + FOSUserEvents::RESETTING_RESET_SUCCESS => 'onResettingResetSuccess', + ]; + } + + public function onResettingResetInitialize(GetResponseUserEvent $event): void + { + if (!$event->getUser()->isPasswordRequestNonExpired($this->tokenTtl)) { + $event->setResponse(new RedirectResponse($this->router->generate('fos_user_resetting_request'))); + } + } + + public function onResettingResetSuccess(FormEvent $event): void + { + /** @var \FOS\UserBundle\Model\UserInterface $user */ + $user = $event->getForm()->getData(); + + $user->setConfirmationToken(null); + $user->setPasswordRequestedAt(null); + $user->setEnabled(true); + } +} diff --git a/src/FOSUserBundle.php b/src/FOSUserBundle.php new file mode 100644 index 0000000000..1a46b0d54d --- /dev/null +++ b/src/FOSUserBundle.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle; + +use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass; +use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass; +use FOS\UserBundle\DependencyInjection\Compiler\CheckForSessionPass; +use FOS\UserBundle\DependencyInjection\Compiler\InjectRememberMeServicesPass; +use FOS\UserBundle\DependencyInjection\Compiler\InjectUserCheckerPass; +use FOS\UserBundle\DependencyInjection\Compiler\ValidationPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * @author Matthieu Bontemps + * @author Thibault Duplessis + */ +final class FOSUserBundle extends Bundle +{ + public function build(ContainerBuilder $container): void + { + parent::build($container); + $container->addCompilerPass(new ValidationPass()); + $container->addCompilerPass(new InjectUserCheckerPass()); + $container->addCompilerPass(new InjectRememberMeServicesPass()); + $container->addCompilerPass(new CheckForSessionPass()); + + $this->addRegisterMappingsPass($container); + } + + private function addRegisterMappingsPass(ContainerBuilder $container): void + { + $mappings = [ + realpath(__DIR__.'/Resources/config/doctrine-mapping') => 'FOS\UserBundle\Model', + ]; + + if (class_exists('Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass')) { + $container->addCompilerPass(DoctrineOrmMappingsPass::createXmlMappingDriver($mappings, ['fos_user.model_manager_name'], 'fos_user.backend_type_orm', [], true)); + } + + if (class_exists('Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass')) { + $container->addCompilerPass(DoctrineMongoDBMappingsPass::createXmlMappingDriver($mappings, ['fos_user.model_manager_name'], 'fos_user.backend_type_mongodb')); + } + } +} diff --git a/src/FOSUserEvents.php b/src/FOSUserEvents.php new file mode 100644 index 0000000000..04924cf5af --- /dev/null +++ b/src/FOSUserEvents.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle; + +/** + * Contains all events thrown in the FOSUserBundle. + */ +final class FOSUserEvents +{ + /** + * The CHANGE_PASSWORD_INITIALIZE event occurs when the change password process is initialized. + * + * This event allows you to modify the default values of the user before binding the form. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const CHANGE_PASSWORD_INITIALIZE = 'fos_user.change_password.edit.initialize'; + + /** + * The CHANGE_PASSWORD_SUCCESS event occurs when the change password form is submitted successfully. + * + * This event allows you to set the response instead of using the default one. + * + * @Event("FOS\UserBundle\Event\FormEvent") + */ + public const CHANGE_PASSWORD_SUCCESS = 'fos_user.change_password.edit.success'; + + /** + * The CHANGE_PASSWORD_COMPLETED event occurs after saving the user in the change password process. + * + * This event allows you to access the response which will be sent. + * + * @Event("FOS\UserBundle\Event\FilterUserResponseEvent") + */ + public const CHANGE_PASSWORD_COMPLETED = 'fos_user.change_password.edit.completed'; + + /** + * The PROFILE_EDIT_INITIALIZE event occurs when the profile editing process is initialized. + * + * This event allows you to modify the default values of the user before binding the form. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const PROFILE_EDIT_INITIALIZE = 'fos_user.profile.edit.initialize'; + + /** + * The PROFILE_EDIT_SUCCESS event occurs when the profile edit form is submitted successfully. + * + * This event allows you to set the response instead of using the default one. + * + * @Event("FOS\UserBundle\Event\FormEvent") + */ + public const PROFILE_EDIT_SUCCESS = 'fos_user.profile.edit.success'; + + /** + * The PROFILE_EDIT_COMPLETED event occurs after saving the user in the profile edit process. + * + * This event allows you to access the response which will be sent. + * + * @Event("FOS\UserBundle\Event\FilterUserResponseEvent") + */ + public const PROFILE_EDIT_COMPLETED = 'fos_user.profile.edit.completed'; + + /** + * The REGISTRATION_INITIALIZE event occurs when the registration process is initialized. + * + * This event allows you to modify the default values of the user before binding the form. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const REGISTRATION_INITIALIZE = 'fos_user.registration.initialize'; + + /** + * The REGISTRATION_SUCCESS event occurs when the registration form is submitted successfully. + * + * This event allows you to set the response instead of using the default one. + * + * @Event("FOS\UserBundle\Event\FormEvent") + */ + public const REGISTRATION_SUCCESS = 'fos_user.registration.success'; + + /** + * The REGISTRATION_FAILURE event occurs when the registration form is not valid. + * + * This event allows you to set the response instead of using the default one. + * The event listener method receives a FOS\UserBundle\Event\FormEvent instance. + * + * @Event("FOS\UserBundle\Event\FormEvent") + */ + public const REGISTRATION_FAILURE = 'fos_user.registration.failure'; + + /** + * The REGISTRATION_COMPLETED event occurs after saving the user in the registration process. + * + * This event allows you to access the response which will be sent. + * + * @Event("FOS\UserBundle\Event\FilterUserResponseEvent") + */ + public const REGISTRATION_COMPLETED = 'fos_user.registration.completed'; + + /** + * The REGISTRATION_CONFIRM event occurs just before confirming the account. + * + * This event allows you to access the user which will be confirmed. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const REGISTRATION_CONFIRM = 'fos_user.registration.confirm'; + + /** + * The REGISTRATION_CONFIRMED event occurs after confirming the account. + * + * This event allows you to access the response which will be sent. + * + * @Event("FOS\UserBundle\Event\FilterUserResponseEvent") + */ + public const REGISTRATION_CONFIRMED = 'fos_user.registration.confirmed'; + + /** + * The RESETTING_RESET_REQUEST event occurs when a user requests a password reset of the account. + * + * This event allows you to check if a user is locked out before requesting a password. + * The event listener method receives a FOS\UserBundle\Event\GetResponseUserEvent instance. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const RESETTING_RESET_REQUEST = 'fos_user.resetting.reset.request'; + + /** + * The RESETTING_RESET_INITIALIZE event occurs when the resetting process is initialized. + * + * This event allows you to set the response to bypass the processing. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const RESETTING_RESET_INITIALIZE = 'fos_user.resetting.reset.initialize'; + + /** + * The RESETTING_RESET_SUCCESS event occurs when the resetting form is submitted successfully. + * + * This event allows you to set the response instead of using the default one. + * + * @Event("FOS\UserBundle\Event\FormEvent ") + */ + public const RESETTING_RESET_SUCCESS = 'fos_user.resetting.reset.success'; + + /** + * The RESETTING_RESET_COMPLETED event occurs after saving the user in the resetting process. + * + * This event allows you to access the response which will be sent. + * + * @Event("FOS\UserBundle\Event\FilterUserResponseEvent") + */ + public const RESETTING_RESET_COMPLETED = 'fos_user.resetting.reset.completed'; + + /** + * The SECURITY_IMPLICIT_LOGIN event occurs when the user is logged in programmatically. + * + * This event allows you to access the response which will be sent. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const SECURITY_IMPLICIT_LOGIN = 'fos_user.security.implicit_login'; + + /** + * The RESETTING_SEND_EMAIL_INITIALIZE event occurs when the send email process is initialized. + * + * This event allows you to set the response to bypass the email confirmation processing. + * The event listener method receives a FOS\UserBundle\Event\GetResponseNullableUserEvent instance. + * + * @Event("FOS\UserBundle\Event\GetResponseNullableUserEvent") + */ + public const RESETTING_SEND_EMAIL_INITIALIZE = 'fos_user.resetting.send_email.initialize'; + + /** + * The RESETTING_SEND_EMAIL_CONFIRM event occurs when all prerequisites to send email are + * confirmed and before the mail is sent. + * + * This event allows you to set the response to bypass the email sending. + * The event listener method receives a FOS\UserBundle\Event\GetResponseUserEvent instance. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const RESETTING_SEND_EMAIL_CONFIRM = 'fos_user.resetting.send_email.confirm'; + + /** + * The RESETTING_SEND_EMAIL_COMPLETED event occurs after the email is sent. + * + * This event allows you to set the response to bypass the the redirection after the email is sent. + * The event listener method receives a FOS\UserBundle\Event\GetResponseUserEvent instance. + * + * @Event("FOS\UserBundle\Event\GetResponseUserEvent") + */ + public const RESETTING_SEND_EMAIL_COMPLETED = 'fos_user.resetting.send_email.completed'; + + /** + * The USER_CREATED event occurs when the user is created with UserManipulator. + * + * This event allows you to access the created user and to add some behaviour after the creation. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const USER_CREATED = 'fos_user.user.created'; + + /** + * The USER_PASSWORD_CHANGED event occurs when the user is created with UserManipulator. + * + * This event allows you to access the created user and to add some behaviour after the password change. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const USER_PASSWORD_CHANGED = 'fos_user.user.password_changed'; + + /** + * The USER_ACTIVATED event occurs when the user is created with UserManipulator. + * + * This event allows you to access the activated user and to add some behaviour after the activation. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const USER_ACTIVATED = 'fos_user.user.activated'; + + /** + * The USER_DEACTIVATED event occurs when the user is created with UserManipulator. + * + * This event allows you to access the deactivated user and to add some behaviour after the deactivation. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const USER_DEACTIVATED = 'fos_user.user.deactivated'; + + /** + * The USER_PROMOTED event occurs when the user is created with UserManipulator. + * + * This event allows you to access the promoted user and to add some behaviour after the promotion. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const USER_PROMOTED = 'fos_user.user.promoted'; + + /** + * The USER_DEMOTED event occurs when the user is created with UserManipulator. + * + * This event allows you to access the demoted user and to add some behaviour after the demotion. + * + * @Event("FOS\UserBundle\Event\UserEvent") + */ + public const USER_DEMOTED = 'fos_user.user.demoted'; +} diff --git a/src/Form/DataTransformer/UserToUsernameTransformer.php b/src/Form/DataTransformer/UserToUsernameTransformer.php new file mode 100644 index 0000000000..72ef3a1df6 --- /dev/null +++ b/src/Form/DataTransformer/UserToUsernameTransformer.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\DataTransformer; + +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a UserInterface instance and a username string. + * + * @author Thibault Duplessis + * + * @template-implements DataTransformerInterface + */ +final class UserToUsernameTransformer implements DataTransformerInterface +{ + /** + * @var UserManagerInterface + */ + protected $userManager; + + /** + * UserToUsernameTransformer constructor. + */ + public function __construct(UserManagerInterface $userManager) + { + $this->userManager = $userManager; + } + + /** + * Transforms a UserInterface instance into a username string. + * + * @param UserInterface|null $value UserInterface instance + * + * @return string|null Username + * + * @throws UnexpectedTypeException if the given value is not a UserInterface instance + */ + public function transform($value): ?string + { + if (null === $value) { + return null; + } + + if (!$value instanceof UserInterface) { + throw new UnexpectedTypeException($value, 'FOS\UserBundle\Model\UserInterface'); + } + + return $value->getUsername(); + } + + /** + * Transforms a username string into a UserInterface instance. + * + * @param string $value Username + * + * @return UserInterface|null the corresponding UserInterface instance + * + * @throws UnexpectedTypeException if the given value is not a string + */ + public function reverseTransform($value): ?UserInterface + { + if (null === $value || '' === $value) { + return null; + } + + if (!is_string($value)) { + throw new UnexpectedTypeException($value, 'string'); + } + + return $this->userManager->findUserByUsername($value); + } +} diff --git a/src/Form/Factory/FactoryInterface.php b/src/Form/Factory/FactoryInterface.php new file mode 100644 index 0000000000..20df60508b --- /dev/null +++ b/src/Form/Factory/FactoryInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Factory; + +use Symfony\Component\Form\FormInterface; + +interface FactoryInterface +{ + public function createForm(): FormInterface; +} diff --git a/src/Form/Factory/FormFactory.php b/src/Form/Factory/FormFactory.php new file mode 100644 index 0000000000..38a0be62c5 --- /dev/null +++ b/src/Form/Factory/FormFactory.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Factory; + +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\Form\FormInterface; + +class FormFactory implements FactoryInterface +{ + /** + * @var FormFactoryInterface + */ + private $formFactory; + + /** + * @var string + */ + private $name; + + /** + * @var string + */ + private $type; + + /** + * @var string[]|null + */ + private $validationGroups; + + /** + * FormFactory constructor. + * + * @param string $name + * @param string $type + * @param string[]|null $validationGroups + */ + public function __construct(FormFactoryInterface $formFactory, $name, $type, ?array $validationGroups = null) + { + $this->formFactory = $formFactory; + $this->name = $name; + $this->type = $type; + $this->validationGroups = $validationGroups; + } + + /** + * @param array $options + */ + public function createForm(array $options = []): FormInterface + { + $options = array_merge(['validation_groups' => $this->validationGroups], $options); + + return $this->formFactory->createNamed($this->name, $this->type, null, $options); + } +} diff --git a/src/Form/Type/ChangePasswordFormType.php b/src/Form/Type/ChangePasswordFormType.php new file mode 100644 index 0000000000..77ec5ed043 --- /dev/null +++ b/src/Form/Type/ChangePasswordFormType.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; +use Symfony\Component\Validator\Constraints\NotBlank; + +final class ChangePasswordFormType extends AbstractType +{ + /** + * @var string + */ + private $class; + + /** + * @param string $class The User class name + */ + public function __construct($class) + { + $this->class = $class; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $constraintsOptions = [ + 'message' => 'fos_user.current_password.invalid', + ]; + + if (!empty($options['validation_groups'])) { + $constraintsOptions['groups'] = [reset($options['validation_groups'])]; + } + + $builder->add('current_password', PasswordType::class, [ + 'label' => 'form.current_password', + 'translation_domain' => 'FOSUserBundle', + 'mapped' => false, + 'constraints' => [ + new NotBlank(), + new UserPassword($constraintsOptions), + ], + 'attr' => [ + 'autocomplete' => 'current-password', + ], + ]); + + $builder->add('plainPassword', RepeatedType::class, [ + 'type' => PasswordType::class, + 'options' => [ + 'translation_domain' => 'FOSUserBundle', + 'attr' => [ + 'autocomplete' => 'new-password', + ], + ], + 'first_options' => ['label' => 'form.new_password'], + 'second_options' => ['label' => 'form.new_password_confirmation'], + 'invalid_message' => 'fos_user.password.mismatch', + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => $this->class, + 'csrf_token_id' => 'change_password', + ]); + } + + public function getBlockPrefix(): string + { + return 'fos_user_change_password'; + } +} diff --git a/src/Form/Type/ProfileFormType.php b/src/Form/Type/ProfileFormType.php new file mode 100644 index 0000000000..f672e4b09c --- /dev/null +++ b/src/Form/Type/ProfileFormType.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\EmailType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; +use Symfony\Component\Validator\Constraints\NotBlank; + +final class ProfileFormType extends AbstractType +{ + /** + * @var string + */ + private $class; + + /** + * @param string $class The User class name + */ + public function __construct($class) + { + $this->class = $class; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $this->buildUserForm($builder, $options); + + $constraintsOptions = [ + 'message' => 'fos_user.current_password.invalid', + ]; + + if (!empty($options['validation_groups'])) { + $constraintsOptions['groups'] = [reset($options['validation_groups'])]; + } + + $builder->add('current_password', PasswordType::class, [ + 'label' => 'form.current_password', + 'translation_domain' => 'FOSUserBundle', + 'mapped' => false, + 'constraints' => [ + new NotBlank(), + new UserPassword($constraintsOptions), + ], + 'attr' => [ + 'autocomplete' => 'current-password', + ], + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => $this->class, + 'csrf_token_id' => 'profile', + ]); + } + + public function getBlockPrefix(): string + { + return 'fos_user_profile'; + } + + /** + * Builds the embedded form representing the user. + * + * @param array $options + */ + protected function buildUserForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('username', null, ['label' => 'form.username', 'translation_domain' => 'FOSUserBundle']) + ->add('email', EmailType::class, ['label' => 'form.email', 'translation_domain' => 'FOSUserBundle']) + ; + } +} diff --git a/src/Form/Type/RegistrationFormType.php b/src/Form/Type/RegistrationFormType.php new file mode 100644 index 0000000000..ee0ce02754 --- /dev/null +++ b/src/Form/Type/RegistrationFormType.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\EmailType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +final class RegistrationFormType extends AbstractType +{ + /** + * @var string + */ + private $class; + + /** + * @param string $class The User class name + */ + public function __construct($class) + { + $this->class = $class; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('email', EmailType::class, ['label' => 'form.email', 'translation_domain' => 'FOSUserBundle']) + ->add('username', null, ['label' => 'form.username', 'translation_domain' => 'FOSUserBundle']) + ->add('plainPassword', RepeatedType::class, [ + 'type' => PasswordType::class, + 'options' => [ + 'translation_domain' => 'FOSUserBundle', + 'attr' => [ + 'autocomplete' => 'new-password', + ], + ], + 'first_options' => ['label' => 'form.password'], + 'second_options' => ['label' => 'form.password_confirmation'], + 'invalid_message' => 'fos_user.password.mismatch', + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => $this->class, + 'csrf_token_id' => 'registration', + ]); + } + + public function getBlockPrefix(): string + { + return 'fos_user_registration'; + } +} diff --git a/src/Form/Type/ResettingFormType.php b/src/Form/Type/ResettingFormType.php new file mode 100644 index 0000000000..01c037bab8 --- /dev/null +++ b/src/Form/Type/ResettingFormType.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +final class ResettingFormType extends AbstractType +{ + /** + * @var string + */ + private $class; + + /** + * @param string $class The User class name + */ + public function __construct($class) + { + $this->class = $class; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->add('plainPassword', RepeatedType::class, [ + 'type' => PasswordType::class, + 'options' => [ + 'translation_domain' => 'FOSUserBundle', + 'attr' => [ + 'autocomplete' => 'new-password', + ], + ], + 'first_options' => ['label' => 'form.new_password'], + 'second_options' => ['label' => 'form.new_password_confirmation'], + 'invalid_message' => 'fos_user.password.mismatch', + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => $this->class, + 'csrf_token_id' => 'resetting', + ]); + } + + public function getBlockPrefix(): string + { + return 'fos_user_resetting'; + } +} diff --git a/src/Form/Type/UsernameFormType.php b/src/Form/Type/UsernameFormType.php new file mode 100644 index 0000000000..22505c6c86 --- /dev/null +++ b/src/Form/Type/UsernameFormType.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Form\Type; + +use FOS\UserBundle\Form\DataTransformer\UserToUsernameTransformer; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Form type for representing a UserInterface instance by its username string. + * + * @author Thibault Duplessis + */ +final class UsernameFormType extends AbstractType +{ + /** + * @var UserToUsernameTransformer + */ + protected $usernameTransformer; + + /** + * Constructor. + */ + public function __construct(UserToUsernameTransformer $usernameTransformer) + { + $this->usernameTransformer = $usernameTransformer; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->addModelTransformer($this->usernameTransformer); + } + + public function getParent(): ?string + { + return TextType::class; + } + + public function getBlockPrefix(): string + { + return 'fos_user_username'; + } +} diff --git a/src/Mailer/MailerInterface.php b/src/Mailer/MailerInterface.php new file mode 100644 index 0000000000..399edaf269 --- /dev/null +++ b/src/Mailer/MailerInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Mailer; + +use FOS\UserBundle\Model\UserInterface; + +/** + * @author Thibault Duplessis + */ +interface MailerInterface +{ + /** + * Send an email to a user to confirm the account creation. + */ + public function sendConfirmationEmailMessage(UserInterface $user): void; + + /** + * Send an email to a user to confirm the password reset. + */ + public function sendResettingEmailMessage(UserInterface $user): void; +} diff --git a/src/Mailer/NoopMailer.php b/src/Mailer/NoopMailer.php new file mode 100644 index 0000000000..290687ddf4 --- /dev/null +++ b/src/Mailer/NoopMailer.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Mailer; + +use FOS\UserBundle\Model\UserInterface; + +/** + * This mailer does nothing. + * It is used when the 'email' configuration is not set, + * and allows to use this bundle without a mailer component. + * + * @author Thibault Duplessis + */ +final class NoopMailer implements MailerInterface +{ + public function sendConfirmationEmailMessage(UserInterface $user): void + { + // nothing happens. + } + + public function sendResettingEmailMessage(UserInterface $user): void + { + // nothing happens. + } +} diff --git a/src/Mailer/TwigSymfonyMailer.php b/src/Mailer/TwigSymfonyMailer.php new file mode 100644 index 0000000000..a233284e72 --- /dev/null +++ b/src/Mailer/TwigSymfonyMailer.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Mailer; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\Mailer\MailerInterface as SymfonyMailerInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Twig\Environment; + +/** + * @author Christophe Coevoet + */ +final class TwigSymfonyMailer implements MailerInterface +{ + private SymfonyMailerInterface $mailer; + private UrlGeneratorInterface $router; + private Environment $twig; + /** + * @var array{template: array{confirmation: string, resetting: string}, from_email: array{confirmation: array{address: string, sender_name: string}, resetting: array{address: string, sender_name: string}}} + */ + private array $parameters; + + /** + * @param array{template: array{confirmation: string, resetting: string}, from_email: array{confirmation: array{address: string, sender_name: string}, resetting: array{address: string, sender_name: string}}} $parameters + */ + public function __construct(SymfonyMailerInterface $mailer, UrlGeneratorInterface $router, Environment $twig, array $parameters) + { + $this->mailer = $mailer; + $this->router = $router; + $this->twig = $twig; + $this->parameters = $parameters; + } + + public function sendConfirmationEmailMessage(UserInterface $user): void + { + $template = $this->parameters['template']['confirmation']; + $url = $this->router->generate('fos_user_registration_confirm', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL); + + $context = [ + 'user' => $user, + 'confirmationUrl' => $url, + ]; + + $this->sendMessage($template, $context, $this->parameters['from_email']['confirmation'], $user->getEmail()); + } + + public function sendResettingEmailMessage(UserInterface $user): void + { + $template = $this->parameters['template']['resetting']; + $url = $this->router->generate('fos_user_resetting_reset', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL); + + $context = [ + 'user' => $user, + 'confirmationUrl' => $url, + ]; + + $this->sendMessage($template, $context, $this->parameters['from_email']['resetting'], $user->getEmail()); + } + + /** + * @param array $context + * @param array{address: string, sender_name: string} $fromEmail + */ + private function sendMessage(string $templateName, array $context, array $fromEmail, string $toEmail): void + { + $template = $this->twig->load($templateName); + $subject = $template->renderBlock('subject', $context); + $textBody = $template->renderBlock('body_text', $context); + + $htmlBody = ''; + + if ($template->hasBlock('body_html', $context)) { + $htmlBody = $template->renderBlock('body_html', $context); + } + + $message = (new Email()) + ->subject($subject) + ->from(new Address($fromEmail['address'], $fromEmail['sender_name'])) + ->to($toEmail) + ->text($textBody) + ; + + if (!empty($htmlBody)) { + $message->html($htmlBody); + } + + $this->mailer->send($message); + } +} diff --git a/src/Model/User.php b/src/Model/User.php new file mode 100644 index 0000000000..854ea82cc0 --- /dev/null +++ b/src/Model/User.php @@ -0,0 +1,424 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Model; + +use Symfony\Component\Security\Core\User\EquatableInterface; +use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface; + +/** + * Storage agnostic user object. + * + * @author Thibault Duplessis + * @author Johannes M. Schmitt + */ +abstract class User implements UserInterface, EquatableInterface, \Serializable +{ + /** + * @var mixed + */ + protected $id; + + /** + * @var string + */ + protected $username; + + /** + * @var string + */ + protected $usernameCanonical; + + /** + * @var string + */ + protected $email; + + /** + * @var string + */ + protected $emailCanonical; + + /** + * @var bool + */ + protected $enabled; + + /** + * The salt to use for hashing. + * + * @var string|null + */ + protected $salt; + + /** + * Encrypted password. Must be persisted. + * + * @var string + */ + protected $password; + + /** + * Plain password. Used for model validation. Must not be persisted. + * + * @var string|null + */ + protected $plainPassword; + + /** + * @var \DateTime|null + */ + protected $lastLogin; + + /** + * Random string sent to the user email address in order to verify it. + * + * @var string|null + */ + protected $confirmationToken; + + /** + * @var \DateTime|null + */ + protected $passwordRequestedAt; + + /** + * @var string[] + */ + protected $roles; + + /** + * User constructor. + */ + public function __construct() + { + $this->enabled = false; + $this->roles = []; + } + + /** + * @return string + */ + public function __toString() + { + return (string) $this->getUsername(); + } + + public function __serialize(): array + { + return [ + $this->password, + $this->salt, + $this->usernameCanonical, + $this->username, + $this->enabled, + $this->id, + $this->email, + $this->emailCanonical, + ]; + } + + /** + * @param list $data + */ + public function __unserialize(array $data): void + { + if (13 === count($data)) { + // Unserializing a User object from 1.3.x + unset($data[4], $data[5], $data[6], $data[9], $data[10]); + $data = array_values($data); + } elseif (11 === count($data)) { + // Unserializing a User from a dev version somewhere between 2.0-alpha3 and 2.0-beta1 + unset($data[4], $data[7], $data[8]); + $data = array_values($data); + } + + [ + $this->password, + $this->salt, + $this->usernameCanonical, + $this->username, + $this->enabled, + $this->id, + $this->email, + $this->emailCanonical + ] = $data; + } + + /** + * @internal + */ + final public function serialize() + { + return serialize($this->__serialize()); + } + + /** + * @internal + */ + final public function unserialize($serialized) + { + $this->__unserialize(unserialize($serialized)); + } + + public function addRole($role) + { + $role = strtoupper($role); + if ($role === static::ROLE_DEFAULT) { + return $this; + } + + if (!in_array($role, $this->roles, true)) { + $this->roles[] = $role; + } + + return $this; + } + + public function eraseCredentials(): void + { + $this->plainPassword = null; + } + + public function getId() + { + return $this->id; + } + + public function getUserIdentifier(): string + { + return $this->username; + } + + /** + * @return string + */ + public function getUsername() + { + return $this->username; + } + + public function getUsernameCanonical() + { + return $this->usernameCanonical; + } + + public function getSalt(): ?string + { + return $this->salt; + } + + public function getEmail() + { + return $this->email; + } + + public function getEmailCanonical() + { + return $this->emailCanonical; + } + + public function getPassword(): ?string + { + return $this->password; + } + + public function getPlainPassword() + { + return $this->plainPassword; + } + + /** + * Gets the last login time. + * + * @return \DateTime|null + */ + public function getLastLogin() + { + return $this->lastLogin; + } + + public function getConfirmationToken() + { + return $this->confirmationToken; + } + + public function getRoles(): array + { + $roles = $this->roles; + + // we need to make sure to have at least one role + $roles[] = static::ROLE_DEFAULT; + + return array_values(array_unique($roles)); + } + + public function hasRole($role) + { + return in_array(strtoupper($role), $this->getRoles(), true); + } + + public function isEnabled() + { + return $this->enabled; + } + + public function isSuperAdmin() + { + return $this->hasRole(static::ROLE_SUPER_ADMIN); + } + + public function removeRole($role) + { + if (false !== $key = array_search(strtoupper($role), $this->roles, true)) { + unset($this->roles[$key]); + $this->roles = array_values($this->roles); + } + + return $this; + } + + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + public function setUsernameCanonical($usernameCanonical) + { + $this->usernameCanonical = $usernameCanonical; + + return $this; + } + + public function setSalt($salt) + { + $this->salt = $salt; + + return $this; + } + + public function setEmail($email) + { + $this->email = $email; + + return $this; + } + + public function setEmailCanonical($emailCanonical) + { + $this->emailCanonical = $emailCanonical; + + return $this; + } + + public function setEnabled($boolean) + { + $this->enabled = (bool) $boolean; + + return $this; + } + + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + public function setSuperAdmin($boolean) + { + if (true === $boolean) { + $this->addRole(static::ROLE_SUPER_ADMIN); + } else { + $this->removeRole(static::ROLE_SUPER_ADMIN); + } + + return $this; + } + + public function setPlainPassword($password) + { + $this->plainPassword = $password; + + return $this; + } + + public function setLastLogin(?\DateTime $time = null) + { + $this->lastLogin = $time; + + return $this; + } + + public function setConfirmationToken($confirmationToken) + { + $this->confirmationToken = $confirmationToken; + + return $this; + } + + public function setPasswordRequestedAt(?\DateTime $date = null) + { + $this->passwordRequestedAt = $date; + + return $this; + } + + /** + * Gets the timestamp that the user requested a password reset. + * + * @return \DateTime|null + */ + public function getPasswordRequestedAt() + { + return $this->passwordRequestedAt; + } + + public function isPasswordRequestNonExpired($ttl) + { + return $this->getPasswordRequestedAt() instanceof \DateTime + && $this->getPasswordRequestedAt()->getTimestamp() + $ttl > time(); + } + + public function setRoles(array $roles) + { + $this->roles = []; + + foreach ($roles as $role) { + $this->addRole($role); + } + + return $this; + } + + public function isEqualTo(BaseUserInterface $user): bool + { + if (!$user instanceof self) { + return false; + } + + if ($this->password !== $user->getPassword()) { + return false; + } + + if ($this->salt !== $user->getSalt()) { + return false; + } + + if ($this->username !== $user->getUsername()) { + return false; + } + + return true; + } +} diff --git a/src/Model/UserInterface.php b/src/Model/UserInterface.php new file mode 100644 index 0000000000..9f3b521f5a --- /dev/null +++ b/src/Model/UserInterface.php @@ -0,0 +1,245 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Model; + +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface; + +/** + * Implementations of that interface must be serializable. The mechanism + * being used to support serialization is up for the implementation. + * The serialized data must contain all the fields involved in comparing + * the user in the security component. + * + * @author Thibault Duplessis + * @author Johannes M. Schmitt + * @author Julian Finkler + */ +interface UserInterface extends PasswordAuthenticatedUserInterface, BaseUserInterface +{ + public const ROLE_DEFAULT = 'ROLE_USER'; + + public const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN'; + + /** + * Returns the user unique id. + * + * @return mixed + */ + public function getId(); + + /** + * @return string + */ + public function getUsername(); + + /** + * Sets the username. + * + * @param string $username + * + * @return static + */ + public function setUsername($username); + + /** + * Gets the canonical username in search and sort queries. + * + * @return string + */ + public function getUsernameCanonical(); + + /** + * Sets the canonical username. + * + * @param string $usernameCanonical + * + * @return static + */ + public function setUsernameCanonical($usernameCanonical); + + /** + * @param string|null $salt + * + * @return static + */ + public function setSalt($salt); + + /** + * Gets email. + * + * @return string + */ + public function getEmail(); + + /** + * Sets the email. + * + * @param string $email + * + * @return static + */ + public function setEmail($email); + + /** + * Gets the canonical email in search and sort queries. + * + * @return string + */ + public function getEmailCanonical(); + + /** + * Sets the canonical email. + * + * @param string $emailCanonical + * + * @return static + */ + public function setEmailCanonical($emailCanonical); + + /** + * Gets the plain password. + * + * @return string|null + */ + public function getPlainPassword(); + + /** + * Sets the plain password. + * + * @param string|null $password + * + * @return static + */ + public function setPlainPassword($password); + + /** + * Sets the hashed password. + * + * @param string $password + * + * @return static + */ + public function setPassword($password); + + /** + * Tells if the the given user has the super admin role. + * + * @return bool + */ + public function isSuperAdmin(); + + /** + * @param bool $boolean + * + * @return static + */ + public function setEnabled($boolean); + + /** + * Sets the super admin status. + * + * @param bool $boolean + * + * @return static + */ + public function setSuperAdmin($boolean); + + /** + * Gets the confirmation token. + * + * @return string|null + */ + public function getConfirmationToken(); + + /** + * Sets the confirmation token. + * + * @param string|null $confirmationToken + * + * @return static + */ + public function setConfirmationToken($confirmationToken); + + /** + * Sets the timestamp that the user requested a password reset. + * + * @return static + */ + public function setPasswordRequestedAt(?\DateTime $date = null); + + /** + * Checks whether the password reset request has expired. + * + * @param int $ttl Requests older than this many seconds will be considered expired + * + * @return bool + */ + public function isPasswordRequestNonExpired($ttl); + + /** + * Sets the last login time. + * + * @return static + */ + public function setLastLogin(?\DateTime $time = null); + + /** + * Never use this to check if this user has access to anything! + * + * Use the AuthorizationChecker, or an implementation of AccessDecisionManager + * instead, e.g. + * + * $authorizationChecker->isGranted('ROLE_USER'); + * + * @param string $role + * + * @return bool + */ + public function hasRole($role); + + /** + * Sets the roles of the user. + * + * This overwrites any previous roles. + * + * @param string[] $roles + * + * @return static + */ + public function setRoles(array $roles); + + /** + * Adds a role to the user. + * + * @param string $role + * + * @return static + */ + public function addRole($role); + + /** + * Removes a role to the user. + * + * @param string $role + * + * @return static + */ + public function removeRole($role); + + /** + * Checks whether the user is enabled. + * + * @return bool true if the user is enabled, false otherwise + */ + public function isEnabled(); +} diff --git a/src/Model/UserManager.php b/src/Model/UserManager.php new file mode 100644 index 0000000000..3a14cdcd43 --- /dev/null +++ b/src/Model/UserManager.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Model; + +use FOS\UserBundle\Util\CanonicalFieldsUpdater; +use FOS\UserBundle\Util\PasswordUpdaterInterface; + +/** + * Abstract User Manager implementation which can be used as base class for your + * concrete manager. + * + * @author Johannes M. Schmitt + */ +abstract class UserManager implements UserManagerInterface +{ + private $passwordUpdater; + private $canonicalFieldsUpdater; + + public function __construct(PasswordUpdaterInterface $passwordUpdater, CanonicalFieldsUpdater $canonicalFieldsUpdater) + { + $this->passwordUpdater = $passwordUpdater; + $this->canonicalFieldsUpdater = $canonicalFieldsUpdater; + } + + public function createUser(): UserInterface + { + $class = $this->getClass(); + $user = new $class(); + + return $user; + } + + public function findUserByEmail($email): ?UserInterface + { + return $this->findUserBy(['emailCanonical' => $this->canonicalFieldsUpdater->canonicalizeEmail($email)]); + } + + public function findUserByUsername($username): ?UserInterface + { + return $this->findUserBy(['usernameCanonical' => $this->canonicalFieldsUpdater->canonicalizeUsername($username)]); + } + + public function findUserByUsernameOrEmail($usernameOrEmail): ?UserInterface + { + if (preg_match('/^.+\@\S+\.\S+$/', $usernameOrEmail)) { + $user = $this->findUserByEmail($usernameOrEmail); + if (null !== $user) { + return $user; + } + } + + return $this->findUserByUsername($usernameOrEmail); + } + + public function findUserByConfirmationToken($token): ?UserInterface + { + return $this->findUserBy(['confirmationToken' => $token]); + } + + public function updateCanonicalFields(UserInterface $user): void + { + $this->canonicalFieldsUpdater->updateCanonicalFields($user); + } + + public function updatePassword(UserInterface $user): void + { + $this->passwordUpdater->hashPassword($user); + } + + protected function getPasswordUpdater(): PasswordUpdaterInterface + { + return $this->passwordUpdater; + } + + protected function getCanonicalFieldsUpdater(): CanonicalFieldsUpdater + { + return $this->canonicalFieldsUpdater; + } +} diff --git a/src/Model/UserManagerInterface.php b/src/Model/UserManagerInterface.php new file mode 100644 index 0000000000..6c2d1cc501 --- /dev/null +++ b/src/Model/UserManagerInterface.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Model; + +/** + * Interface to be implemented by user managers. This adds an additional level + * of abstraction between your application, and the actual repository. + * + * All changes to users should happen through this interface. + * + * The class also contains ACL annotations which will only work if you have the + * SecurityExtraBundle installed, otherwise they will simply be ignored. + * + * @author Gordon Franke + * @author Thibault Duplessis + * @author Johannes M. Schmitt + */ +interface UserManagerInterface +{ + /** + * Creates an empty user instance. + */ + public function createUser(): UserInterface; + + /** + * Deletes a user. + */ + public function deleteUser(UserInterface $user): void; + + /** + * Finds one user by the given criteria. + * + * @param array $criteria + */ + public function findUserBy(array $criteria): ?UserInterface; + + /** + * Find a user by its username. + * + * @param string $username + */ + public function findUserByUsername($username): ?UserInterface; + + /** + * Finds a user by its email. + * + * @param string $email + */ + public function findUserByEmail($email): ?UserInterface; + + /** + * Finds a user by its username or email. + * + * @param string $usernameOrEmail + */ + public function findUserByUsernameOrEmail($usernameOrEmail): ?UserInterface; + + /** + * Finds a user by its confirmationToken. + * + * @param string $token + */ + public function findUserByConfirmationToken($token): ?UserInterface; + + /** + * Returns a collection with all user instances. + * + * @return iterable + */ + public function findUsers(): iterable; + + /** + * Returns the user's fully qualified class name. + * + * @phpstan-return class-string + */ + public function getClass(): string; + + /** + * Reloads a user. + */ + public function reloadUser(UserInterface $user): void; + + /** + * Updates a user. + */ + public function updateUser(UserInterface $user): void; + + /** + * Updates the canonical username and email fields for a user. + */ + public function updateCanonicalFields(UserInterface $user): void; + + /** + * Updates a user password if a plain password is set. + */ + public function updatePassword(UserInterface $user): void; +} diff --git a/src/Resources/config/change_password.xml b/src/Resources/config/change_password.xml new file mode 100644 index 0000000000..db54feb97e --- /dev/null +++ b/src/Resources/config/change_password.xml @@ -0,0 +1,33 @@ + + + + + + + + %fos_user.change_password.form.name% + %fos_user.change_password.form.type% + %fos_user.change_password.form.validation_groups% + + + + + %fos_user.model.user.class% + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/commands.xml b/src/Resources/config/commands.xml new file mode 100644 index 0000000000..569e7ac343 --- /dev/null +++ b/src/Resources/config/commands.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/doctrine-mapping/User.mongodb.xml b/src/Resources/config/doctrine-mapping/User.mongodb.xml new file mode 100644 index 0000000000..2429442ed9 --- /dev/null +++ b/src/Resources/config/doctrine-mapping/User.mongodb.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/doctrine-mapping/User.orm.xml b/src/Resources/config/doctrine-mapping/User.orm.xml new file mode 100644 index 0000000000..2cd57471b7 --- /dev/null +++ b/src/Resources/config/doctrine-mapping/User.orm.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/doctrine.xml b/src/Resources/config/doctrine.xml new file mode 100644 index 0000000000..73825bf94d --- /dev/null +++ b/src/Resources/config/doctrine.xml @@ -0,0 +1,26 @@ + + + + + + + + + + %fos_user.model.user.class% + + + + + %fos_user.model_manager_name% + + + + + + + + + diff --git a/src/Resources/config/email_confirmation.xml b/src/Resources/config/email_confirmation.xml new file mode 100644 index 0000000000..2d9c334a6e --- /dev/null +++ b/src/Resources/config/email_confirmation.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/Resources/config/flash_notifications.xml b/src/Resources/config/flash_notifications.xml new file mode 100644 index 0000000000..e9fa34193b --- /dev/null +++ b/src/Resources/config/flash_notifications.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/Resources/config/listeners.xml b/src/Resources/config/listeners.xml new file mode 100644 index 0000000000..feb9addffd --- /dev/null +++ b/src/Resources/config/listeners.xml @@ -0,0 +1,15 @@ + + + + + + + + + %fos_user.firewall_name% + + + + diff --git a/src/Resources/config/mailer.xml b/src/Resources/config/mailer.xml new file mode 100644 index 0000000000..0993d9c4d2 --- /dev/null +++ b/src/Resources/config/mailer.xml @@ -0,0 +1,40 @@ + + + + + + @FOSUser/Resetting/email.txt.twig + @FOSUser/Registration/email.txt.twig + + no-registration@acme.com + Acme Ltd + + + no-resetting@acme.com + Acme Ltd + + + + + + + + + + + %fos_user.registration.confirmation.template% + %fos_user.resetting.email.template% + + + %fos_user.registration.confirmation.from_address% + %fos_user.resetting.email.from_address% + + + + + + + + diff --git a/src/Resources/config/profile.xml b/src/Resources/config/profile.xml new file mode 100644 index 0000000000..5039e1cc75 --- /dev/null +++ b/src/Resources/config/profile.xml @@ -0,0 +1,34 @@ + + + + + + + + + %fos_user.profile.form.name% + %fos_user.profile.form.type% + %fos_user.profile.form.validation_groups% + + + + %fos_user.model.user.class% + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/registration.xml b/src/Resources/config/registration.xml new file mode 100644 index 0000000000..6b15cdfd8e --- /dev/null +++ b/src/Resources/config/registration.xml @@ -0,0 +1,35 @@ + + + + + + + + + %fos_user.registration.form.name% + %fos_user.registration.form.type% + %fos_user.registration.form.validation_groups% + + + + + %fos_user.model.user.class% + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/resetting.xml b/src/Resources/config/resetting.xml new file mode 100644 index 0000000000..59425a3d5d --- /dev/null +++ b/src/Resources/config/resetting.xml @@ -0,0 +1,43 @@ + + + + + + + + + %fos_user.resetting.form.name% + %fos_user.resetting.form.type% + %fos_user.resetting.form.validation_groups% + + + + + %fos_user.model.user.class% + + + + + + %fos_user.resetting.token_ttl% + + + + + + + + + %fos_user.resetting.retry_ttl% + + + + + + + + + + diff --git a/src/Resources/config/routing/all.xml b/src/Resources/config/routing/all.xml new file mode 100644 index 0000000000..4000b42db9 --- /dev/null +++ b/src/Resources/config/routing/all.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/src/Resources/config/routing/change_password.xml b/src/Resources/config/routing/change_password.xml new file mode 100644 index 0000000000..456e1fa32b --- /dev/null +++ b/src/Resources/config/routing/change_password.xml @@ -0,0 +1,11 @@ + + + + + + fos_user.change_password.controller::changePasswordAction + + + diff --git a/src/Resources/config/routing/profile.xml b/src/Resources/config/routing/profile.xml new file mode 100644 index 0000000000..390c392f14 --- /dev/null +++ b/src/Resources/config/routing/profile.xml @@ -0,0 +1,15 @@ + + + + + + fos_user.profile.controller::showAction + + + + fos_user.profile.controller::editAction + + + diff --git a/src/Resources/config/routing/registration.xml b/src/Resources/config/routing/registration.xml new file mode 100644 index 0000000000..411760d641 --- /dev/null +++ b/src/Resources/config/routing/registration.xml @@ -0,0 +1,23 @@ + + + + + + fos_user.registration.controller::registerAction + + + + fos_user.registration.controller::checkEmailAction + + + + fos_user.registration.controller::confirmAction + + + + fos_user.registration.controller::confirmedAction + + + diff --git a/src/Resources/config/routing/resetting.xml b/src/Resources/config/routing/resetting.xml new file mode 100644 index 0000000000..b11a2360e8 --- /dev/null +++ b/src/Resources/config/routing/resetting.xml @@ -0,0 +1,23 @@ + + + + + + fos_user.resetting.controller::requestAction + + + + fos_user.resetting.controller::sendEmailAction + + + + fos_user.resetting.controller::checkEmailAction + + + + fos_user.resetting.controller::resetAction + + + diff --git a/src/Resources/config/routing/security.xml b/src/Resources/config/routing/security.xml new file mode 100644 index 0000000000..beeb036491 --- /dev/null +++ b/src/Resources/config/routing/security.xml @@ -0,0 +1,19 @@ + + + + + + fos_user.security.controller::loginAction + + + + fos_user.security.controller::checkAction + + + + fos_user.security.controller::logoutAction + + + diff --git a/src/Resources/config/security.xml b/src/Resources/config/security.xml new file mode 100644 index 0000000000..59427426e6 --- /dev/null +++ b/src/Resources/config/security.xml @@ -0,0 +1,54 @@ + + + + + + FOS\UserBundle\EventListener\LastLoginListener + FOS\UserBundle\Security\LoginManager + + + + + + + + + + + null + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/storage-validation/mongodb.xml b/src/Resources/config/storage-validation/mongodb.xml new file mode 100644 index 0000000000..0dc276497e --- /dev/null +++ b/src/Resources/config/storage-validation/mongodb.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/storage-validation/orm.xml b/src/Resources/config/storage-validation/orm.xml new file mode 100644 index 0000000000..fdea9a8181 --- /dev/null +++ b/src/Resources/config/storage-validation/orm.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/username_form_type.xml b/src/Resources/config/username_form_type.xml new file mode 100644 index 0000000000..447784a63e --- /dev/null +++ b/src/Resources/config/username_form_type.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/util.xml b/src/Resources/config/util.xml new file mode 100644 index 0000000000..585a28b43e --- /dev/null +++ b/src/Resources/config/util.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/validation.xml b/src/Resources/config/validation.xml new file mode 100644 index 0000000000..992e0d1253 --- /dev/null +++ b/src/Resources/config/validation.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/validator.xml b/src/Resources/config/validator.xml new file mode 100644 index 0000000000..b7b0d21f63 --- /dev/null +++ b/src/Resources/config/validator.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/Resources/translations/FOSUserBundle.af.yml b/src/Resources/translations/FOSUserBundle.af.yml new file mode 100644 index 0000000000..bc0d3a7f5f --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.af.yml @@ -0,0 +1,78 @@ +security: + login: + username: 'Gebruiker Naam' + password: Wagwoord + remember_me: 'Onthou my' + submit: 'Teken in' + +profile: + show: + username: 'Gebruiker Naam' + email: E-pos + edit: + submit: Opdateer + flash: + updated: 'Die profiel was opgedateer' + +change_password: + submit: 'Verander wagwoord' + flash: + success: 'Die wagwoord was verander' + +registration: + check_email: "'n E-pos was gestuur na %email%. Dit behels aktiveerings skakel, waarop jy moet klik om jou rekening te aktiveer" + confirmed: 'Geluk %username%, jou rekening is nou geaktiveer' + back: 'Terug na die oorsprongblad' + submit: Registreer + flash: + user_created: 'Die verbruiker was suksesvol gemaak' + email: + subject: 'Welkom %username%!' + message: | + Hello %username%! + + Om die registrasie proses te finaliseer, besoek asseblief %confirmationUrl% + + Hierdie skakel kan slegs een keer gebruik word. + + Groete, + die Span. + +resetting: + check_email: | + 'n E-pos was gestuur na %email%. Dit bevat 'n skakel waarop jy moet klik om jou wagwoord te herstel. + Nota: Jy kan eers weer 'n nuwe skakel versoek na %tokenLifetime% ure. + + As jy nie 'n e-pos kry nie, kyk in jou "spam" leer en probeer weer. + request: + username: 'Verbruikernaam of e-pos adres' + submit: 'Herstel wagwoord' + reset: + submit: 'Verander wagwoord' + flash: + success: 'Die wagwoord was suksesvol herstel' + email: + subjet: 'Herstel Wagwoord' + message: | + Hello %username%! + + Om jou wagwoord te herstel, besoek asseblief %confirmationUrl% + + Groete, + die Span. + +# Global strings +layout: + logout: 'Sluit af' + login: 'Teken in' + register: Registreer + logged_in_as: 'Ingeteken as %username%' + +form: + username: 'Verbruiker Naam' + email: E-pos + current_password: 'Huidige wagwoord' + password: Wagwoord + password_confirmation: 'Herhaal wagwoord' + new_password: 'Nuwe wagwoord' + new_password_confirmation: 'Herhaal nuwe wagwoord' diff --git a/src/Resources/translations/FOSUserBundle.ar.yml b/src/Resources/translations/FOSUserBundle.ar.yml new file mode 100644 index 0000000000..c169ba67a8 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.ar.yml @@ -0,0 +1,78 @@ +security: + login: + username: 'إسم المستخدم' + password: 'كلمة المرور' + remember_me: 'حفظ البيانات' + submit: 'تسجيل دخول' + +profile: + show: + username: 'إسم المستخدم' + email: 'بريد إلكتروني' + edit: + submit: تحديث + flash: + updated: 'تم تحديث الملف الشخصي' + +change_password: + submit: 'تغيير كلمة المرور' + flash: + success: 'تم تغيير كلمة المرور' + +registration: + check_email: 'لقد تم إرسال رسالة إلكترونية إلى %email% تحتوي على رابط يجب إتباعه لتفعيل حسابك الشخصي.' + confirmed: 'تهانينا %username%، لقد تم تفعيل حسابك.' + back: 'عودة إلى الصفحة الرئيسية' + submit: تسجيل + flash: + user_created: 'تم إنشاء حسابك بنجاح' + email: + subject: '%username%! مرحبا' + message: | + مرحبا %username%! + + + لتفعيل حسابك يرجى إتباع الرابط التالي %confirmationUrl% + + الرابط يمكن إستخدامه مرة واحدة فقط لتفعيل حسابك + + شكرا، + فريق العمل. + +resetting: + check_email: | + لقد تم إرسال رسالة إلكترونية تحتوي على رابط يجب الضغط عليه لإعادة ضبط كلمة المرور الخاصة بك. + ملحوظة: يمكنك إعادة ضبط كلمة المرور الخاصة بك خلال %tokenLifetime% ساعة + + يرجى التحقق من ملف السبام إذا لم تستقبل الرسالة الإلكترونية قريبا أو أعد المحاولة + request: + username: 'إسم المستخدم او البريد الالكتروني' + submit: 'إعادة ضبط كلمة المرور' + reset: + submit: 'تغيير كلمة المرور' + flash: + success: 'تم إعادة ضبط كلمة المرور بنجاح' + email: + subject: 'إعادة ضبط كلمة المرور' + message: | + مرحبا %username%! + + لإعادة ضبط كلمة المرور يرجى إتباع الرابط التالي %confirmationUrl% + + شكرا، + فريق العمل. + +layout: + logout: خروج + login: دخول + register: تسجيل + logged_in_as: 'تم الدخول بإسم %username%' + +form: + username: 'إسم المستخدم' + email: 'البريد الإلكتروني' + current_password: 'كلمة المرور الحالية' + password: 'كلمة المرور' + password_confirmation: تأكيد + new_password: 'كلمة المرور الجديدة' + new_password_confirmation: 'تأكيد كلمة المرور' diff --git a/src/Resources/translations/FOSUserBundle.bg.yml b/src/Resources/translations/FOSUserBundle.bg.yml new file mode 100644 index 0000000000..17fdb93263 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.bg.yml @@ -0,0 +1,79 @@ +security: + login: + username: 'Потребителско име' + password: 'Парола' + remember_me: 'Запомни ме' + submit: 'Вход' + +profile: + show: + username: 'Потребителско име' + email: Имейл + edit: + submit: 'Промени' + flash: + updated: 'Профилът беше успешно променен.' + +change_password: + submit: 'Промени паролата' + flash: + success: 'Паролата беше успешно променена.' + +registration: + check_email: | + Изпратихме писмо до %email%. То съдържа линк за активиране, който трябва да отворите, за да се активира профилът Ви. + confirmed: 'Честито, %username%, акаунтът е успешно активиран.' + back: 'Обратно към предишната страница.' + submit: 'Регистрация' + flash: + user_created: 'Потребителят беше успешно създаден.' + email: + subject: 'Добре дошли, %username%!' + message: | + Здравейте, %username%! + + За да активирате своя профил, моля отворете %confirmationUrl% + + Това е еднократен линк за валидиране на акаунта Ви. + + Поздрави, + от екипа. + +resetting: + check_email: | + Изпратихме писмо с линк за активация на посочения имейл адрес: %email%. + Забележка: Заявка за нова парола може да направите в рамките на %tokenLifetime% час(а). + + Ако не откривате писмото проверете в папка СПАМ или опитайте отново. + request: + username: 'Потребителско име или имейл' + submit: 'Промени паролата' + reset: + submit: 'Промени паролата' + flash: + success: 'Паролата беше успешно променена.' + email: + subject: 'Забравена парола' + message: | + Здравейте, %username%! + + За да промените своята парола, моля отворете %confirmationUrl% + + Поздрави, + от екипа. + +# Global strings +layout: + logout: 'Изход' + login: 'Вход' + register: 'Регистрация' + logged_in_as: 'Влязъл като %username%' + +form: + username: 'Потребителско име' + email: 'Имейл' + current_password: 'Текуща парола' + password: 'Парола' + password_confirmation: 'Парола (отново)' + new_password: 'Нова парола' + new_password_confirmation: 'Нова парола (отново)' diff --git a/src/Resources/translations/FOSUserBundle.bn.yml b/src/Resources/translations/FOSUserBundle.bn.yml new file mode 100644 index 0000000000..0e56bd08e1 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.bn.yml @@ -0,0 +1,79 @@ +security: + login: + username: 'ব্যবহারকারীর নাম' + password: 'পাসওয়ার্ড' + remember_me: 'আমাকে মনে রেখো' + submit: 'লগ ইন' + +profile: + show: + username: 'ব্যবহারকারীর নাম' + email: 'ই-মেইল' + edit: + submit: 'আপডেট' + flash: + updated: 'প্রোফাইল আপডেট করা হয়েছে।' + +change_password: + submit: 'পাসওয়ার্ড পরিবর্তন' + flash: + success: 'পাসওয়ার্ড পরিবর্তন সফল হয়েছো' + +registration: + check_email: | + %email% এড্রেসে একটি ই-মেইল পাঠানো হয়েছে. অ্যাকাউন্ট সক্রিয় করার জন্য ই-মেইলে পাঠানো লিংকটি ক্লিক করুন + confirmed: '%username% অভিনন্দন, আপনার অ্যাকাউন্ট এখন সক্রিয়।' + back: 'আগের পাতা' + submit: 'নিবন্ধন' + flash: + user_created: 'ব্যবহারকারী সফলভাবে তৈরি করা হয়েছে' + email: + subject: 'স্বাগতম %username%!' + message: | + হ্যালো %username%! + + আপনার অ্যাকাউন্ট সক্রিয় করার জন্য - দয়া করে %confirmationUrl% লিংকটি ভিসিট করুর + + এই লিঙ্কটি শুধুমাত্র একবার আপনার অ্যাকাউন্ট যাচাই করতে ব্যবহার করা যেতে পারে। + + শুভেচ্চান্তে, + এডমিন। + +resetting: + check_email: | + একটি ই-মেইল পাঠানো হয়েছে। পাসওয়ার্ড রিসেট করার জন্য ই-মেইলে পাঠানো লিংকটি ক্লিক করুন। + বিঃদ্রঃ %tokenLifetime% ঘন্টার মধ্যে শুধুমাত্র একবার রিসেট পাসওয়ার্ড করতে পারবেন। + + যদি ই-মেইল টি না পেয়ে থাকেন, তাহলে আপসার স্পাম ফোল্ডারে দেখুন অথবা আবার চেষ্টা করুন। + request: + username: 'ব্যবহারকারীর নাম অথবা ই-মেইল' + submit: 'রিসেট পাসওয়ার্ড' + reset: + submit: 'পাসওয়ার্ড পরিবর্তন' + flash: + success: 'পাসওয়ার্ডটি সফলভাবো রিসেট করা হয়েছে' + email: + subject: 'রিসেট পাসওয়ার্ড' + message: | + হ্যালো %username%! + + আপনার পাসওয়ার্ড রিসেট করতে - দয়া করে %confirmationUrl% লিংকটি ভিসিট করুর + + শুভেচ্চান্তে, + এডমিন। + +# Global strings +layout: + logout: 'লগ আউট' + login: 'লগ ইন' + register: 'নিবন্ধন' + logged_in_as: '%username% হিসাবে লগ ইন করেছেন' + +form: + username: 'ব্যবহারকারীর নাম' + email: 'ই-মেইল' + current_password: 'বর্তমান পাসওয়ার্ড' + password: 'পাসওয়ার্ড' + password_confirmation: 'পাসওয়ার্ড আবার লিখুন' + new_password: 'নতুন পাসওয়ার্ড' + new_password_confirmation: 'নতুন পাসওয়ার্ড আবার লিখুন' diff --git a/src/Resources/translations/FOSUserBundle.bn_BD.yml b/src/Resources/translations/FOSUserBundle.bn_BD.yml new file mode 100644 index 0000000000..0e56bd08e1 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.bn_BD.yml @@ -0,0 +1,79 @@ +security: + login: + username: 'ব্যবহারকারীর নাম' + password: 'পাসওয়ার্ড' + remember_me: 'আমাকে মনে রেখো' + submit: 'লগ ইন' + +profile: + show: + username: 'ব্যবহারকারীর নাম' + email: 'ই-মেইল' + edit: + submit: 'আপডেট' + flash: + updated: 'প্রোফাইল আপডেট করা হয়েছে।' + +change_password: + submit: 'পাসওয়ার্ড পরিবর্তন' + flash: + success: 'পাসওয়ার্ড পরিবর্তন সফল হয়েছো' + +registration: + check_email: | + %email% এড্রেসে একটি ই-মেইল পাঠানো হয়েছে. অ্যাকাউন্ট সক্রিয় করার জন্য ই-মেইলে পাঠানো লিংকটি ক্লিক করুন + confirmed: '%username% অভিনন্দন, আপনার অ্যাকাউন্ট এখন সক্রিয়।' + back: 'আগের পাতা' + submit: 'নিবন্ধন' + flash: + user_created: 'ব্যবহারকারী সফলভাবে তৈরি করা হয়েছে' + email: + subject: 'স্বাগতম %username%!' + message: | + হ্যালো %username%! + + আপনার অ্যাকাউন্ট সক্রিয় করার জন্য - দয়া করে %confirmationUrl% লিংকটি ভিসিট করুর + + এই লিঙ্কটি শুধুমাত্র একবার আপনার অ্যাকাউন্ট যাচাই করতে ব্যবহার করা যেতে পারে। + + শুভেচ্চান্তে, + এডমিন। + +resetting: + check_email: | + একটি ই-মেইল পাঠানো হয়েছে। পাসওয়ার্ড রিসেট করার জন্য ই-মেইলে পাঠানো লিংকটি ক্লিক করুন। + বিঃদ্রঃ %tokenLifetime% ঘন্টার মধ্যে শুধুমাত্র একবার রিসেট পাসওয়ার্ড করতে পারবেন। + + যদি ই-মেইল টি না পেয়ে থাকেন, তাহলে আপসার স্পাম ফোল্ডারে দেখুন অথবা আবার চেষ্টা করুন। + request: + username: 'ব্যবহারকারীর নাম অথবা ই-মেইল' + submit: 'রিসেট পাসওয়ার্ড' + reset: + submit: 'পাসওয়ার্ড পরিবর্তন' + flash: + success: 'পাসওয়ার্ডটি সফলভাবো রিসেট করা হয়েছে' + email: + subject: 'রিসেট পাসওয়ার্ড' + message: | + হ্যালো %username%! + + আপনার পাসওয়ার্ড রিসেট করতে - দয়া করে %confirmationUrl% লিংকটি ভিসিট করুর + + শুভেচ্চান্তে, + এডমিন। + +# Global strings +layout: + logout: 'লগ আউট' + login: 'লগ ইন' + register: 'নিবন্ধন' + logged_in_as: '%username% হিসাবে লগ ইন করেছেন' + +form: + username: 'ব্যবহারকারীর নাম' + email: 'ই-মেইল' + current_password: 'বর্তমান পাসওয়ার্ড' + password: 'পাসওয়ার্ড' + password_confirmation: 'পাসওয়ার্ড আবার লিখুন' + new_password: 'নতুন পাসওয়ার্ড' + new_password_confirmation: 'নতুন পাসওয়ার্ড আবার লিখুন' diff --git a/src/Resources/translations/FOSUserBundle.ca.yml b/src/Resources/translations/FOSUserBundle.ca.yml new file mode 100644 index 0000000000..164e464b09 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.ca.yml @@ -0,0 +1,67 @@ +security: + login: + username: 'Nom d''usuari' + password: Contrasenya + remember_me: 'Recorda''m' + submit: Entra +profile: + show: + username: 'Nom d''usuari' + email: 'Correu electrònic' + edit: + submit: Actualitza + flash: + updated: 'S''ha actualitzat el perfil.' +change_password: + submit: 'Canvia la contrasenya' + flash: + success: 'S''ha canviat la contrasenya.' +registration: + check_email: 'S''ha enviat un correu electrònic a %email%. Conté un enllaç d''activació que heu de clicar per activar el compte.' + confirmed: 'Enhorabona %username%, el vostre compte s''ha activat.' + back: 'Torna a la pàgina original.' + submit: Registra + flash: + user_created: 'S''ha creat l''usuari correctament.' + email: + subject: 'Benvingut %username%!' + message: | + Hola %username%! + + Per finalitzar l'activació del seu compte - si us plau visiteu %confirmationUrl% + + Atentament, + L'equip. + +resetting: +# check_email: 'S''ha enviat un correu electrònic a %email%. Conté un enllaç que heu de clicar per restablir la contrasenya.' + request: + username: 'Nom d''usuari o correu electrònic' + submit: 'Restableix la contrasenya' + reset: + submit: 'Canvia la contrasenya' + flash: + success: 'S''ha restablert la contrasenya correctament.' + email: + subject: 'Restablir la contrasenya' + message: | + Hola %username%! + + Per restablir la contrasenya - si us plau visiteu %confirmationUrl% + + Atentament, + L'equip. + +layout: + logout: 'Tanca la sessió' + login: Entra + register: Registrar-se + logged_in_as: 'Heu iniciat sessió com a %username%' +form: + username: 'Nom d''usuari' + email: 'Correu electrònic' + current_password: 'Contrasenya actual' + password: Contrasenya + password_confirmation: Verificació + new_password: 'Nova contrasenya' + new_password_confirmation: Verificació diff --git a/src/Resources/translations/FOSUserBundle.cs.yml b/src/Resources/translations/FOSUserBundle.cs.yml new file mode 100644 index 0000000000..61b57edbff --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.cs.yml @@ -0,0 +1,72 @@ +security: + login: + username: 'Uživatelské jméno' + password: Heslo + remember_me: 'Zapamatovat si' + submit: 'Přihlásit se' +profile: + show: + username: 'Uživatelské jméno' + email: E-mail + edit: + submit: Upravit + flash: + updated: 'Profil byl úspěšně aktualizován.' +change_password: + submit: 'Nastavit nové heslo' + flash: + success: 'Nové heslo bylo úspěšně nastaveno.' +registration: + check_email: 'Na adresu %email% byl zaslán aktivační e-mail. Obsahuje odkaz, na který je nutné kliknout pro aktivaci Vašeho účtu.' + confirmed: 'Gratulujeme, %username%, Váš účet je nyní aktivní.' + back: 'Návrat na předchozí stránku.' + submit: 'Registrovat se' + flash: + user_created: 'Váš účet byl úspěšně vytvořen.' + email: + subject: 'Vítejte, %username%!' + message: | + Dobrý den, %username%! + + Pro dokončení Vaší registrace prosím klikněte na tento odkaz: %confirmationUrl% + + S pozdravem, + realizační tým. + +resetting: + check_email: | + Byl Vám zaslán e-mail s návodem na nastavení nového hesla. E-mail obsahuje odkaz, na který je nutné kliknout pro nastavení nového hesla. + O nastavení nového hesla můžete žádat pouze každých %tokenLifetime% hodin. + + Pokud jste e-mail neobdrželi, zkontrolujte prosím složku se spamem nebo zkuste obnovování hesla zopakovat. + + request: + username: 'Uživatelské jméno nebo e-mail' + submit: 'Nastavit nové heslo' + reset: + submit: 'Změnit heslo' + flash: + success: 'Heslo bylo úspěšně změněno.' + email: + subject: 'Nastavení nového hesla' + message: | + Dobrý den, %username%! + + Pro nastavení nového hesla prosím klikněte na tento odkaz: %confirmationUrl% + + S pozdravem, + realizační tým. + +layout: + logout: 'Odhlásit se' + login: 'Přihlásit se' + register: 'Registrovat se' + logged_in_as: 'Přihlášený uživatel: %username%' +form: + username: 'Uživatelské jméno' + email: E-mail + current_password: 'Současné heslo' + password: Heslo + password_confirmation: 'Potvrzení hesla' + new_password: 'Nové heslo' + new_password_confirmation: 'Potvrzení nového hesla' diff --git a/src/Resources/translations/FOSUserBundle.da.yml b/src/Resources/translations/FOSUserBundle.da.yml new file mode 100644 index 0000000000..d1f71c4a84 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.da.yml @@ -0,0 +1,78 @@ +security: + login: + username: Brugernavn + password: Adgangskode + remember_me: 'Husk mig' + submit: 'Log ind' + +profile: + show: + username: Brugernavn + email: E-mail + edit: + submit: Opdater + flash: + updated: 'Profilen er blevet opdateret.' + +change_password: + submit: 'Skift adgangskode' + flash: + success: 'Adgangskoden er blevet opdateret.' + +registration: + check_email: | + En e-mail er blevet sendt til %email%. Den indeholder et aktiveringslink, du skal følge for at aktivere din konto. + confirmed: 'Tillykke %username%, din konto er nu aktiveret' + back: 'Tilbage til foregående side.' + submit: Registrer + flash: + user_created: 'Bruger er blevet oprettet.' + email: + subject: 'Velkommen %username%!' + message: | + Hej %username%! + + For at færdiggøre aktiveringen af din konto, besøg venligst %confirmationUrl% + + Dette link kan kun bruges én gang til at validere din konto. + + Venlig hilsen, + Holdet bag siden. + +resetting: + check_email: | + En e-mail er blevet sendt til %email%. Den indeholder et link, du skal følge for at nulstille din adgangskode. + Note: Du kan først bede om en ny adgangskode om %tokenLifetime% timer. + + Hvis ikke du modtager en enmail skal du tjekke dit spamfilter eller prøve igen. + request: + username: 'Brugernavn eller e-mailadresse' + submit: 'Nulstil adgangskode' + reset: + submit: 'Skift adgangskode' + flash: + success: 'Adgangskoden er blevet nulstillet.' + email: + subject: 'Velkommen %username%!' + message: | + Hej %username%! + + For at nulstille din adgangskode, besøg venligst %confirmationUrl% + + Venlig hilsen, + Holdet bag siden. + +# Global strings +layout: + logout: 'Log ud' + login: 'Log ind' + register: Registrer + logged_in_as: 'Logget ind som %username%' +form: + username: Brugernavn + email: E-mail + current_password: 'Nuværende adgangskode' + password: Adgangskode + password_confirmation: 'Gentag adgangskode' + new_password: 'Ny adgangskode' + new_password_confirmation: 'Gentag adgangskode' diff --git a/src/Resources/translations/FOSUserBundle.de.yml b/src/Resources/translations/FOSUserBundle.de.yml new file mode 100644 index 0000000000..4762310593 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.de.yml @@ -0,0 +1,72 @@ +security: + login: + username: Benutzername + password: Passwort + remember_me: 'An mich erinnern' + submit: Anmelden +profile: + show: + username: Benutzername + email: E-Mail + edit: + submit: 'Benutzer aktualisieren' + flash: + updated: 'Das Benutzerprofil wurde aktualisiert.' +change_password: + submit: 'Passwort ändern' + flash: + success: 'Das Passwort wurde geändert.' +registration: + check_email: 'Eine E-Mail wurde an %email% gesendet. Sie enthält einen Link, den Sie anklicken müssen, um Ihr Benutzerkonto zu bestätigen.' + confirmed: 'Glückwunsch %username%, Ihr Benutzerkonto ist jetzt bestätigt.' + back: 'Zurück zur ursprünglichen Seite.' + submit: Registrieren + flash: + user_created: 'Der Benutzer wurde erfolgreich erstellt.' + email: + subject: 'Willkommen %username%!' + message: | + Hallo %username%! + + Besuchen Sie bitte folgende Seite, um Ihr Benutzerkonto zu bestätigen: %confirmationUrl% + + Mit besten Grüßen, + das Team. + +resetting: + check_email: | + Eine E-Mail wurde verschickt. Sie beinhaltet einen Link zum Zurücksetzen des Passwortes. + Hinweis: Ein neues Passwort kann nur alle %tokenLifetime% Stunden beantragt werden. + + Eventuell wurde diese E-Mail als Spam markiert, wenn sie nicht angekommen ist. + + request: + username: 'Benutzername oder E-Mail-Adresse' + submit: 'Passwort zurücksetzen' + reset: + submit: 'Passwort ändern' + flash: + success: 'Das Passwort wurde erfolgreich zurückgesetzt.' + email: + subject: 'Passwort zurücksetzen' + message: | + Hallo %username%! + + Besuchen Sie bitte folgende Seite, um Ihr Passwort zurückzusetzen: %confirmationUrl% + + Mit besten Grüßen, + das Team. + +layout: + logout: Abmelden + login: Anmelden + register: Registrieren + logged_in_as: 'Angemeldet als %username%' +form: + username: Benutzername + email: E-Mail-Adresse + current_password: 'Derzeitiges Passwort' + password: Passwort + password_confirmation: 'Passwort bestätigen' + new_password: 'Neues Passwort' + new_password_confirmation: 'Neues Passwort bestätigen' diff --git a/src/Resources/translations/FOSUserBundle.el.yml b/src/Resources/translations/FOSUserBundle.el.yml new file mode 100644 index 0000000000..75d7db3c97 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.el.yml @@ -0,0 +1,70 @@ +security: + login: + username: 'Όνομα Χρήστη' + password: 'Κωδικός Πρόσβασης' + remember_me: 'Να με θυμάσαι' + submit: Είσοδος +profile: + show: + username: 'Όνομα Χρήστη' + email: Email + edit: + submit: Ενημέρωσε + flash: + updated: 'Το προφίλ ενημερώθηκε επιτυχώς' +change_password: + submit: 'Αλλαγή κωδικού' + flash: + success: 'Ο κωδικός άλλαξε επιτυχώς' +registration: + check_email: 'Ένα email στάλθηκε στο %email%. Περιέχει μία διεύθυνση ενεργοποίησης του λογαριασμού σας που πρέπει να κάνετε κλικ για να ενεργοποιηθεί.' + confirmed: 'Συγχαρητήρια %username%, ο λογαριασμός μόλις ενεργοποιήθηκε.' + back: 'Επιστροφή στην αρχική σελίδα.' + submit: Εγγραφή + flash: + user_created: 'Ο χρήστης δημιουργήθηκε επιτυχώς.' + email: + subject: 'Καλώς ήρθες %username%!' + message: | + Αγαπητέ %username%! + + Για να ολοκληρώσετε την ενεργοποίηση του λογαριασμού σας - επισκεφθείτε %confirmationUrl% + + Ευχαριστούμε πολύ. + +resetting: + check_email: | + Ένα email έχει αποσταλεί. Περιέχει ένα σύνδεσμο ώστε να μπορέσετε να επαναφέρετε τον κωδικό σας. + Σημείωση: Μπορείτε να ζητήσε επαναφορά το πολύ μία φορά μέσα σε %tokenLifetime% hours. + + Εάν δεν έχετε λάβει κάποιο email παρακαλώ ελένξτε τον φάκελο με την ανεπιθύμητη αλληλογραφία (spam) ή δοκιμάστε ξανά. + + request: + username: 'Όνομα χρήστη ή διεύθυνση e-mail' + submit: 'Επαναφορά του κωδικού πρόσβασης' + reset: + submit: 'Αλλαγή κωδικού πρόσβασης' + flash: + success: 'Η επαναφορά του κωδικού πρόσβασης έγινε με επιτυχία' + email: + subject: 'Επαναφορά του κωδικού πρόσβασης' + message: | + Αγαπητέ %username%! + + Για να επαναφέρετε τον κωδικό πρόσβασής σας - επισκεφθείτε %confirmationUrl% + + Ευχαριστούμε πολυ. + +layout: + logout: Αποσύνδεση + login: Σύνδεση + register: Εγγραφή + logged_in_as: 'Έχετε συνδεθεί ως %username%' +form: + username: 'Όνομα Χρήστη' + email: Email + current_password: 'Τρέχον κωδικός πρόσβασης' + password: 'Κωδικός πρόσβασης' + password_confirmation: Επαλήθευση + new_password: 'Νέος Κωδικός πρόσβασης' + new_password_confirmation: Επαλήθευση diff --git a/src/Resources/translations/FOSUserBundle.en.yml b/src/Resources/translations/FOSUserBundle.en.yml new file mode 100644 index 0000000000..b706e38926 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.en.yml @@ -0,0 +1,74 @@ +security: + login: + username: Username + password: Password + remember_me: 'Remember me' + submit: 'Log in' +profile: + show: + username: Username + email: Email + edit: + submit: Update + flash: + updated: 'The profile has been updated.' +change_password: + submit: 'Change password' + flash: + success: 'The password has been changed.' +registration: + check_email: | + An email has been sent to %email%. It contains an activation link you must click to activate your account. + confirmed: 'Congrats %username%, your account is now activated.' + back: 'Back to the originating page.' + submit: Register + flash: + user_created: 'The user has been created successfully.' + email: + subject: 'Welcome %username%!' + message: | + Hello %username%! + + To finish activating your account - please visit %confirmationUrl% + + This link can only be used once to validate your account. + + Regards, + the Team. +resetting: + check_email: | + An email has been sent. It contains a link you must click to reset your password. + Note: You can only request a new password once within %tokenLifetime% hours. + + If you don't get an email check your spam folder or try again. + request: + username: 'Username or email address' + submit: 'Reset password' + reset: + submit: 'Change password' + flash: + success: 'The password has been reset successfully.' + email: + subject: 'Reset Password' + message: | + Hello %username%! + + To reset your password - please visit %confirmationUrl% + + Regards, + the Team. + +# Global strings +layout: + logout: 'Log out' + login: 'Log in' + register: Register + logged_in_as: 'Logged in as %username%' +form: + username: Username + email: Email + current_password: 'Current password' + password: Password + password_confirmation: 'Repeat password' + new_password: 'New password' + new_password_confirmation: 'Repeat new password' diff --git a/src/Resources/translations/FOSUserBundle.eo.yml b/src/Resources/translations/FOSUserBundle.eo.yml new file mode 100644 index 0000000000..6d6c4085f9 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.eo.yml @@ -0,0 +1,79 @@ +security: + login: + username: Uzantnomo + password: Pasvorto + remember_me: 'Memoru min' + submit: 'Ensaluti' + +profile: + show: + username: Uzantnomo + email: Retpoŝtadreso + edit: + submit: Aktualigi + flash: + updated: 'La profilo estas aktualigita.' + +change_password: + submit: 'Ŝanĝi la pasvorton' + flash: + success: 'La pasvorto estas ŝanĝita' + +registration: + check_email: | + Por fini la registriĝon bonvolu alklaki la ligilon en la retpoŝtmesaĝo, kiun ni ĵus sendis al vi. Se vi ne ricevis la mesaĝon, kontrolu vian spam-dosierujon. + confirmed: 'Gratulon %username%, via konto estas aktiva.' + back: 'Reiri al la origina paĝo.' + submit: Registriĝi + flash: + user_created: 'La uzanto estis kreita.' + email: + subject: 'Bonvenon %username%!' + message: | + Saluton %username%! + + Por kompletigi la aktivigadon de via konto - Bonvolu viziti %confirmationUrl% + + Ĉi tiu ligilo povas validigi nur vian konton. + + Amike, + la Teamo. + +resetting: + check_email: | + Ni sendis retpoŝtmesaĝon al vi. Ĝi enhavas ligilon, kiun vi devas alklaki por ŝanĝi vian pasvorton. + Rimarku: Vi nur povas uzi la ligilon dum %tokenLifetime% horoj. + + Se vi ne ricevis la mesaĝon, kontrolu vian spam-dosierujon. + request: + username: 'Uzantnomo aŭ retpoŝtadreso' + submit: 'Ŝanĝi la Pasvorton' + reset: + submit: 'Ŝanĝi la Pasvorton' + flash: + success: 'La pasvorto estas sukcese ŝanĝita.' + email: + subject: 'Ŝanĝi la pasvorton' + message: | + Saluton %username%! + + Por ŝanĝi la pasvorton - Bonvolu viziti %confirmationUrl% + + Amike, + la Teamo. + +# Global strings +layout: + logout: 'Elsaluti' + login: 'Ensaluti' + register: Registriĝi + logged_in_as: 'Ensalutita kiel %username%' + +form: + username: Uzantnomo + email: Retpoŝtadreso + current_password: 'Nuna pasvorto' + password: Pasvorto + password_confirmation: 'Retajpu pasvorton' + new_password: 'Nova pasvorto' + new_password_confirmation: 'Retajpu novan pasvorton' diff --git a/src/Resources/translations/FOSUserBundle.es.yml b/src/Resources/translations/FOSUserBundle.es.yml new file mode 100644 index 0000000000..ae0242615a --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.es.yml @@ -0,0 +1,74 @@ +security: + login: + username: 'Nombre de usuario' + password: Contraseña + remember_me: Recordar + submit: Entrar +profile: + show: + username: 'Nombre de usuario' + email: Email + edit: + submit: 'Actualizar usuario' + flash: + updated: 'El perfil ha sido actualizado.' +change_password: + submit: 'Cambiar contraseña' + flash: + success: 'La contraseña se ha cambiado con éxito.' +registration: + check_email: 'Se ha enviado un correo electrónico a %email%. Contiene un enlace de activación al que debes acceder para activar tu cuenta.' + confirmed: 'Felicidades %username%, tu cuenta está ahora confirmada.' + back: 'Volver a la página original.' + submit: Registrar + flash: + user_created: 'El usuario se ha creado satisfactoriamente.' + email: + subject: '¡Bienvenido %username%!' + message: | + ¡Hola %username%! + + Para completar la validación de tu cuenta, por favor visita %confirmationUrl% + + Este enlace sólo puede utilizarse una vez para validar tu cuenta. + + Atentamente, + el Equipo. + +resetting: + check_email: | + Se ha enviado un correo electrónico a tu cuenta de correo. Contiene un enlace de activación al que debes acceder para restablecer tu contraseña. + Nota: Solo se puede solicitar una nueva contraseña cada %tokenLifetime% horas. + + Si no recibes un correo electrónico, comprueba tu carpeta de correo no deseado o inténtalo de nuevo. + + request: + username: 'Nombre de usuario o correo electrónico' + submit: 'Restablecer contraseña' + reset: + submit: 'Cambiar contraseña' + flash: + success: 'La contraseña se ha cambiado con éxito.' + email: + subject: 'Restablecer Contraseña' + message: | + ¡Hola %username%! + + Para restablecer tu contraseña, por favor visita %confirmationUrl% + + Atentamente, + el Equipo. + +layout: + logout: Salir + login: Entrar + register: Registrar + logged_in_as: 'Identificado como %username%' +form: + username: 'Nombre de usuario' + email: Email + current_password: 'Contraseña actual' + password: Contraseña + password_confirmation: 'Repita la contraseña' + new_password: 'Nueva contraseña' + new_password_confirmation: 'Repita la contraseña' diff --git a/src/Resources/translations/FOSUserBundle.et.yml b/src/Resources/translations/FOSUserBundle.et.yml new file mode 100644 index 0000000000..78cbf67366 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.et.yml @@ -0,0 +1,74 @@ +security: + login: + username: Kasutajanimi + password: Salasõna + remember_me: 'Jäta mind meelde' + submit: Sisene +profile: + show: + username: Kasutajanimi + email: E-mail + edit: + submit: Uuenda + flash: + updated: 'Profiil uuendatud.' + +change_password: + submit: 'Muuda salasõna' + flash: + success: 'Parool muudetud.' +registration: + check_email: | + E-mail on saadetud aadressile %email%. Selles sisaldub viide, mis tuleb konto aktiveerimiseks avada. + confirmed: 'Palju õnne %username%, Sinu konto on aktiveeritud.' + back: 'Tagasi esialgsele lehele.' + submit: Registreeri + flash: + user_created: 'Kasutaja edukalt loodud.' + email: + subject: 'Tere %username%!' + message: | + Tere %username%! + + Konto aktiveerimiseks palun avage viide %confirmationUrl% + + Seda viidet saate kasutada vaid ühe korra konto valideerimiseks. + + Parimatega, + Tiim. + +resetting: + check_email: | + E-mail on saadetud. Selles sisaldub viide, mis tuleb salasõna muutmiseks avada. + Märkus: Saate uue parooli tellida vaid korra %tokenLifetime% tunni jooksul. + + Kui te ei e-maili ei saanud, kontrollige oma spämmikausta või proovige uuesti. + request: + username: Kasutajanimi või e-mail + submit: 'Saada salasõna' + reset: + submit: 'Muuda salasõna' + flash: + success: 'Parooli saatmine õnnestus.' + email: + message: | + Tere %username%! + + Salasõna muutmiseks palun avage viide %confirmationUrl% + + Parimatega, + Tiim. + +layout: + logout: Välju + login: Sisene + register: Registreeri + logged_in_as: 'Sisse logitud: %username%' +form: + username: Kasutajanimi + email: E-mail + current_password: 'Vana salasõna' + password: Salasõna + password_confirmation: 'Salasõna uuesti' + new_password: 'Uus salasõna' + new_password_confirmation: 'Salasõna uuesti' diff --git a/src/Resources/translations/FOSUserBundle.eu.yml b/src/Resources/translations/FOSUserBundle.eu.yml new file mode 100644 index 0000000000..56a5f6c176 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.eu.yml @@ -0,0 +1,74 @@ +security: + login: + username: 'Erabiltzaile izena' + password: Pasahitza + remember_me: Oroitu + submit: Sartu +profile: + show: + username: 'Erabiltzaile izena' + email: E-posta + edit: + submit: 'Erabiltzailea eguneratu' + flash: + updated: 'Perfila eguneratua izan da.' +change_password: + submit: 'Pasahitza aldatu' + flash: + success: 'Pasahitz ongi eguneratu da.' +registration: + check_email: 'E-posta bat bidali da %email% helbidera. Kontua aktibatzeko klik egin beharreko esteka zehazten da.' + confirmed: 'Zorionak %username%, zure kontua aktibatua dago.' + back: Itzuli. + submit: 'Izena eman' + flash: + user_created: 'Erabiltzailea ongi sortu da.' + email: + subject: 'Ongi etorri %username%!' + message: | + Kaixo %username%! + + Kontuaren balidazioazioarekin jarraitzeko - ireki ondoko orria: %confirmationUrl% + + Esteka hau behin baino ez erabili daiteke zure kontua balidatzeko. + + Adeitasunez, + Taldea. + +resetting: + check_email: | + E-posta bat bidali da zure helbidera. Pasahitza berreskuratzeko estekan klik egin behar duzu bertan. + Oharra: Behin %tokenLifetime% horduro bakarrik ezka dezakezu pasahitz berria. + + E-posta ez baduzu jasotzen, begira ezazu zure zabor-postan edo saiatu berriro. + + request: + username: 'Erabiltzaile izena' + submit: 'Pasahitza berreskuratu' + reset: + submit: 'Pasahitza aldatu' + flash: + success: 'Pasahitza ongi aldatu da.' + email: + subject: 'Pasahitza berrezarri' + message: | + Kaixo %username%! + + Pasahitza berrezartzeko egin klik esteka honetan: %confirmationUrl% + + Adeitasunez, + Taldea. + +layout: + logout: Irten + login: Sartu + register: 'Izena eman' + logged_in_as: '%username% bezala konektatua' +form: + username: 'Erabiltzaile izena' + email: E-posta + current_password: 'Oraingo pasahitza' + password: Pasahitza + password_confirmation: 'Errepikatu pasahitza' + new_password: 'Pasahitz berria' + new_password_confirmation: 'Errepikatu pasahitza' diff --git a/src/Resources/translations/FOSUserBundle.fa.yml b/src/Resources/translations/FOSUserBundle.fa.yml new file mode 100644 index 0000000000..37014414c1 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.fa.yml @@ -0,0 +1,79 @@ +security: + login: + username: 'نام کاربری' + password: 'کلمه عبور' + remember_me: 'مشخصات ورود من را به خاطر بسپار' + submit: 'ورود' + +profile: + show: + username: 'نام کاربری' + email: 'ایمیل' + edit: + submit: 'به‌روزرسانی' + flash: + updated: 'پروفایل کاربری با موفقیت به‌روز شد' + +change_password: + submit: 'تغییر کلمه عبور' + flash: + success: 'کلمه عبور با موفقیت تغییر کرد' + +registration: + check_email: | + ایمیلی به %email% فرستاده شده است. این ایمیل حاوی لینکی است که برای فعال سازی باید روی آن کلیک کنید. + confirmed: '%username% عزیز، نام کاربری شما با موفقیت فعال شد.' + back: 'بازگشت به صفحه قبلی' + submit: 'ثبت نام' + flash: + user_created: 'کاربر با موفقیت ساخته شد' + email: + subject: '%username% خوش آمدید!' + message: | + %username% سلام! + + برای تکمیل فعال سازی نام کاربری خود لطفا %confirmationUrl% را مشاهده کنید + + از این لینک تنها یک بار می توان برای فعال سازی نام کاربری استفاده کرد. + + با احترام، + گروه پشتیبانی + +resetting: + check_email: | + ایمیلی به نشانی شما فرستاده شده است. این ایمیل حاوی لینکی است که برای فعال سازی باید روی آن کلیک کنید. + لطفا توجه کنید که در هر %tokenLifetime% ساعت تنها یک بار می توانید درخواست کلمه عبور جدید ارسال کنید. + + اگر ایمیلی با این مشخصات در باکس خود دریافت نکردید، لطفا پوشه اسپم خود را چک کرده و یا مجددا تلاش کنید. + request: + username: 'نام کاربری یا ایمیل' + submit: 'بازنشانی کلمه عبور' + reset: + submit: 'تغییر کلمه عبور' + flash: + success: 'کلمه عبور با موفقیت بازنشانی شد' + email: + subject: 'بازنشانی کلمه عبور' + message: | + %username% سلام! + + برای بازنشانی کلمه عبور خود لطفا %confirmationUrl% را مشاهده کنید + + با احترام، + گروه پشتیبانی + +# Global strings +layout: + logout: خروج + login: ورود + register: 'ثبت نام' + logged_in_as: 'ورود به عنوان %username%' + +form: + username: 'نام کاربری' + email: 'ایمیل' + current_password: 'کلمه عبور فعلی' + password: 'کلمه عبور' + password_confirmation: 'تکرار کلمه عبور' + new_password: 'کلمه عبور جدید' + new_password_confirmation: 'تکرار کلمه عبور جدید' diff --git a/src/Resources/translations/FOSUserBundle.fi.yml b/src/Resources/translations/FOSUserBundle.fi.yml new file mode 100644 index 0000000000..e9e41305db --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.fi.yml @@ -0,0 +1,69 @@ +security: + login: + username: Käyttäjätunnus + password: Salasana + remember_me: 'Muista minut' + submit: Kirjaudu +profile: + show: + username: Käyttäjätunnus + email: Sähköpostiosoite + edit: + submit: Päivitä + flash: + updated: 'Profiili on päivitetty.' +change_password: + submit: 'Vaihda salasana' + flash: + success: 'Salasana on vaihdettu.' +registration: + check_email: 'Sähköposti on lähetetty osoitteeseen %email%. Se sisältää aktivointilinkin, jota klikkaamalla tunnus aktivoidaan.' + confirmed: 'Onnittelut %username%, tunnuksesi on nyt aktivoitu.' + back: 'Takaisin alkuperäiselle sivulle.' + submit: 'Luo tunnus' + flash: + user_created: 'Käyttäjätunnus on luotu onnistuneesti.' + email: + subject: 'Tervetuloa %username%!' + message: | + Hei %username%! + + Aktivoidaksesi tunnuksesi, ole hyvä ja klikkaa %confirmationUrl% + + Terveisin ylläpito + +resetting: + check_email: | + Viesti on lähetetty. Se sisältää linkin jota klikkaamalla voit palauttaa salasanasi. + Huom! Et voi palauttaa salasanaasi useammin kuin %tokenLifetime% tunnin välein. + + Jos et saanut viestiä, ole hyvä ja tarkasta roskapostilaatikkosi tai yritä uudelleen. + request: + username: 'Käyttäjätunnus tai sähköpostiosoite' + submit: 'Alusta salasana' + reset: + submit: 'Vaihda salasana' + flash: + success: 'Salasana on alustettu onnistuneesti.' + email: + subject: 'Alusta salasana' + message: | + Hei %username%! + + Alustaaksesi salasanasi, ole hyvä ja klikkaa %confirmationUrl% + + Terveisin ylläpito + +layout: + logout: Ulos + login: Kirjaudu + register: 'Luo tunnus' + logged_in_as: 'Kirjautuneena %username%' +form: + username: Käyttäjätunnus + email: Sähköpostiosoite + current_password: 'Nykyinen salasana' + password: Salasana + password_confirmation: 'Salasana uudestaan' + new_password: 'Uusi salasana' + new_password_confirmation: 'Salasana uudestaan' diff --git a/src/Resources/translations/FOSUserBundle.fr.yml b/src/Resources/translations/FOSUserBundle.fr.yml new file mode 100644 index 0000000000..7de0d2ae10 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.fr.yml @@ -0,0 +1,75 @@ +security: + login: + username: 'Nom d''utilisateur' + password: 'Mot de passe' + remember_me: 'Se souvenir de moi' + submit: Connexion +profile: + show: + username: 'Nom d''utilisateur' + email: 'Adresse e-mail' + edit: + submit: 'Mettre à jour' + flash: + updated: 'Le profil a été mis à jour.' +change_password: + submit: 'Modifier le mot de passe' + flash: + success: 'Le mot de passe a été modifié.' + +registration: + check_email: 'Un e-mail a été envoyé à l''adresse %email%. Il contient un lien d''activation sur lequel il vous faudra cliquer afin d''activer votre compte.' + confirmed: 'Félicitations %username%, votre compte est maintenant activé.' + back: 'Retour à la page d''origine.' + submit: 'Créer un compte' + flash: + user_created: 'L''utilisateur a été créé avec succès.' + email: + subject: 'Bienvenue %username% !' + message: | + Bonjour %username% ! + + Pour valider votre compte utilisateur, merci de vous rendre sur %confirmationUrl% + + Ce lien ne peut être utilisé qu'une seule fois pour valider votre compte. + + Cordialement, + L'équipe + +resetting: + check_email: | + Un e-mail a été envoyé. Il contient un lien sur lequel il vous faudra cliquer pour réinitialiser votre mot de passe. + Remarque : Vous ne pouvez demander un nouveau mot de passe que toutes les %tokenLifetime% heures. + + Si vous ne recevez pas un email, vérifiez votre dossier spam ou essayez à nouveau. + + request: + username: 'Nom d''utilisateur ou adresse e-mail' + submit: 'Réinitialiser le mot de passe' + reset: + submit: 'Modifier le mot de passe' + flash: + success: 'Le mot de passe a été réinitialisé avec succès.' + email: + subject: 'Réinitialisation de votre mot de passe' + message: | + Bonjour %username% ! + + Pour réinitialiser votre mot de passe, merci de vous rendre sur %confirmationUrl% + + Cordialement, + L'équipe + +layout: + logout: Déconnexion + login: Connexion + register: Inscription + logged_in_as: 'Connecté en tant que %username%' +form: + username: 'Nom d''utilisateur' + email: 'Adresse e-mail' + current_password: 'Mot de passe actuel' + password: 'Mot de passe' + password_confirmation: 'Répéter le mot de passe' + new_password: 'Nouveau mot de passe' + new_password_confirmation: 'Répéter le nouveau mot de passe' diff --git a/src/Resources/translations/FOSUserBundle.gl.yml b/src/Resources/translations/FOSUserBundle.gl.yml new file mode 100644 index 0000000000..4b21dd648f --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.gl.yml @@ -0,0 +1,79 @@ +# Security +security: + login: + username: Nome de usuario + password: Contrasinal + remember_me: Lembrar + submit: Entrar + +# Profile +profile: + show: + username: Nome de usuario + email: Email + edit: + submit: Actualizar usuario + flash: + updated: Actualizouse o perfil. + +# Password change +change_password: + submit: Cambiar contrasinal + flash: + success: O contrasinal cambiouse con éxito. + +# Registration +registration: + check_email: Enviouse un email a %email%. Contén un enlace de activación que deberás premer para activala túa conta. + confirmed: Parabéns %username%, confirmouse a túa conta. + back: Volver á páxina orixinal. + submit: Rexistrar + flash: + user_created: Creouse o usuario satisfactoriamente. + email: + subject: Benvido %username%! + message: | + Hola %username%! + + Para completala validación da túa conta - por favor visita %confirmationUrl% + + Atentamente, + o Equipo. + +# Password resetting +resetting: + password_already_requested: O contrasinal para este usuario solicitouse xa dentro das 24 horas. + check_email: Enviouse un email %email%. Contén un enlace de activación que deberás premer para restablecelo teu contrasinal. + request: + username: Nome de usuario + submit: Restablecer contrasinal + reset: + submit: Cambiar contrasinal + flash: + success: O contrasinal cambiaouse con éxito. + email: + subject: Benvido %username%! + message : | + Hola %username%! + + Para restablecelo teu contrasinal - por favor visita %confirmationUrl% + + Atentamente, + o Equipo. + +# Global strings +layout: + logout: Saír + login: Entrar + register: Rexistrar + logged_in_as: Identificado como %username% + +# Form field labels +form: + username: Nome de usuario + email: Email + current_password: Contrasinal actualizado + password: Contrasinal + password_confirmation: Repita o contrasinal + new_password: Novo contrasinal + new_password_confirmation: Repita o contrasinal diff --git a/src/Resources/translations/FOSUserBundle.he.yml b/src/Resources/translations/FOSUserBundle.he.yml new file mode 100644 index 0000000000..fd82524637 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.he.yml @@ -0,0 +1,77 @@ +security: + login: + username: 'שם משתמש' + password: סיסמה + remember_me: 'זכור אותי' + submit: התחבר +profile: + show: + username: 'שם משתמש' + email: 'דוא"ל' + edit: + submit: עדכון + flash: + updated: 'הפרופיל עודכן בהצלחה' +change_password: + submit: 'שינוי סיסמה' + flash: + success: 'הסיסמה שונתה בהצלחה.' +registration: + check_email: 'הודעה בדוא"ל נשלחה ל-%email%. ההודעה מכילה קישור להפעלת חשבון משתמש שלך. יש ללחוץ על הקישור כדי להפעיל את החשבון.' + confirmed: 'ברוכים הבאים %username%, חשבון שלך הופעל.' + back: 'השב לדף הקודם.' + submit: הרשם + flash: + user_created: 'המשתמש נוצר בהצלחה' + email: + subject: 'שלום %username%!' + message: | + %username% שלום רב, + + כדי לסיים הרשמתך יש ללחוץ על קישור זה: %confirmationUrl% + + זה קישור חד-פעמי לאמת סיסמתך. + + בברכה, + הצוות. + +resetting: + check_email: | + הודעת דוא"ל נשלחה. ההודעה מכילה קישור לאיפוס סיסמה שלך. + + ניתן לבקש איפוס סיסמה תוך %tokenLifetime% שעות. + + יש לבדוק תיקיים דואר זבל במידע ולא הגיעה ההודעה. + + request: + username: 'שם משתמש או דואר אלקטרוני' + submit: 'איפוס סיסמה' + reset: + submit: 'שינוי סיסמה' + flash: + success: 'הסיסמה אופסה בהצלחה' + email: + subject: 'איפוס סיסמה' + message: | + %username% שלום רב, + + כדי לאפס סיסמה שלך יש ללחוץ %confirmationUrl% + + בברכה, + הצוות. + +# Global strings +layout: + logout: התנתק + login: כניסה + register: רישום + logged_in_as: 'שלום %username%' + +form: + username: 'שם משתמש' + email: 'דוא\"ל' + current_password: 'סיסמה נוכחות' + password: סיסמה + password_confirmation: 'אימות סיסמה' + new_password: 'סיסמה חדשה' + new_password_confirmation: 'אימות סיסמה חדשה' diff --git a/src/Resources/translations/FOSUserBundle.hr.yml b/src/Resources/translations/FOSUserBundle.hr.yml new file mode 100644 index 0000000000..51a9859920 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.hr.yml @@ -0,0 +1,67 @@ +security: + login: + username: 'Korisničko ime' + password: Lozinka + remember_me: 'Zapamti me' + submit: Prijava +profile: + show: + username: 'Korisničko ime' + email: Email + edit: + submit: Ažuriraj + flash: + updated: 'Profil je uspješno ažuriran.' +change_password: + submit: 'Izmijeni lozinku' + flash: + success: 'Lozinka je uspješno spremljena.' +registration: + check_email: 'Na %email% smo vam poslali poruku s poveznicom za aktivaciju. Korisnički račun aktivirate klikom na tu poveznicu.' + confirmed: 'Pozdrav %username%, vaš korisnički račun je sada aktiviran.' + back: 'Natrag na polaznu stranicu.' + submit: Registracija + flash: + user_created: 'Korisnički račun je uspješno kreiran.' + email: + subject: 'Aktivacija korisničkog računa' + message: | + Pozdrav %username%! + + Za aktivaciju korisničkog računa molimo kliknite na %confirmationUrl% + + Pozdrav, + Autori stranice + +resetting: +# check_email: 'Na %email% smo vam poslali poruku s poveznicom za promjenu lozinke. Lozinku resetirajte klikom na tu poveznicu.' + request: + username: 'Korisničko ime' + submit: 'Izmijeni lozinku' + reset: + submit: 'Spremi lozinku' + flash: + success: 'Lozinka je uspješno spremljena.' + email: + subject: 'Promjena lozinke' + message: | + Pozdrav %username%! + + Za promjenu lozinke molimo kliknite na %confirmationUrl% + + Pozdrav, + Autori stranice + +layout: + logout: Odjava + login: Prijava + register: Registracija + logged_in_as: 'Prijavljen kao %username%' +form: + username: 'Korisničko ime' + email: Email + current_password: 'Trenutna lozinka' + password: Lozinka + password_confirmation: 'Potvrda lozinke' + new_password: 'Nova lozinka' + new_password_confirmation: 'Potvrda lozinke' diff --git a/src/Resources/translations/FOSUserBundle.hu.yml b/src/Resources/translations/FOSUserBundle.hu.yml new file mode 100644 index 0000000000..d11e4723ae --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.hu.yml @@ -0,0 +1,77 @@ +security: + login: + username: Felhasználónév + password: Jelszó + remember_me: Megjegyzés + submit: Belépés + +profile: + show: + username: Felhasználónév + email: E-mail + edit: + submit: Frissítés + flash: + updated: 'A profil frissítve.' + +change_password: + submit: 'Jelszó megváltoztazása' + flash: + success: 'A jelszó megváltoztatva.' + +registration: + check_email: 'A regisztrált fiók aktiválásához a megadott "%email%" címre küldött üzenetben lévő aktivációs linkre kell kattintani.' + confirmed: 'Gratulálunk %username%, a regisztrált fiók sikeresen aktiválásra került.' + back: 'Vissza az előző oldalra.' + submit: Regisztráció + flash: + user_created: 'A felhasználó sikeresen létrehozva.' + email: + subject: 'Üdvözöljük %username%!' + message: | + Hello %username%! + + A regisztrált fiók a következő linkre kattintva aktiválható: %confirmationUrl% + + A link csak egyszer használható, rákattintás után ismételt felhasználása nem lehetséges. + + Üdvözlettel, + a Csapat. + +resetting: + check_email: | + Elfelejtett jelszava lecseréléséhez, kérjük, kattintson az e-mailben található linkre. + Felhívjuk figyelmát rá, hogy a link érvényessége %tokenLifetime% óra múlva lejár. + + Amennyiben nem találja az e-mailt a postafiókjában, ellenőrizze a levélszemét mappát vagy kérje újra a kiküldést! + request: + username: 'Felhasználónév vagy e-mail cím' + submit: 'Jelszó lecserélése' + reset: + submit: 'Jelszó megváltoztatása' + flash: + success: 'A jelszó sikeresen lecserélve.' + email: + subject: 'Jelszó lecserélése' + message: | + Hello %username%! + + A jelszó a következő oldalon cserélhető le: %confirmationUrl% + + Üdvözlettel, + a Csapat. + +layout: + logout: Kijelentkezés + login: Bejelentkezés + register: Regisztráció + logged_in_as: 'Belépve mint %username%' + +form: + username: Felhasználónév + email: E-mail + current_password: 'Jelenlegi jelszó' + password: Jelszó + password_confirmation: 'Jelszó megerősítése' + new_password: 'Új jelszó' + new_password_confirmation: 'Új jelszó megerősítése' diff --git a/src/Resources/translations/FOSUserBundle.id.yml b/src/Resources/translations/FOSUserBundle.id.yml new file mode 100644 index 0000000000..9afaf99d5f --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.id.yml @@ -0,0 +1,78 @@ +security: + login: + username: 'Nama Pengguna' + password: 'Kata sandi' + remember_me: 'Ingat saya' + submit: 'Masuk' + +profile: + show: + username: 'Nama Pengguna' + email: 'Surel' + edit: + submit: 'Perbaharui' + flash: + updated: 'Profil telah berhasil diperbaharui' + +change_password: + submit: 'Ubah kata sandi' + flash: + success: 'Kata sandi telah berhasil diubah.' + +registration: + check_email: 'Sebuah surel telah berhasil dikirimkan ke %email%. Anda harus mengklik tautan aktivasi di dalam surel tersebut untuk mengaktifkan akun anda.' + confirmed: 'Selamat %username%, akun anda telah berhasil diaktifkan.' + back: 'Kembali ke halaman sebelumnya.' + submit: Daftar + flash: + user_created: 'Pengguna telah berhasil ditambahkan.' + email: + subject: 'Selamat datang %username%!' + message: | + Hallo %username%! + + Untuk menyelesaikan aktifasi akun anda, mohon kunjungi halaman berikut %confirmationUrl% + + Tautan ini hanya bisa digunakan sekali untuk akun anda. + + Hormat Kami, + Tim Website. + +resetting: + check_email: | + Sebuah surel telah dikirim. Anda harus mengklik tautan di dalam email tersebut agar dapat menata ulang kata sandi anda. + Catatan: Anda hanya bisa meminta kata sandi baru dalam %tokenLifetime% jam. + + Apabila anda tidak menerima surel, cek spam atau ulangi kembali + request: + username: 'Nama pengguna atau alamat surel' + submit: 'Tata ulang kata sandi' + reset: + submit: 'Ubah kata sandi' + flash: + success: 'Kata sandi telah berhasil ditata ulang.' + email: + subject: 'Tata ulang kata sandi' + message: | + Halo %username%! + + Untuk menata kata sandi anda mohon kunjungi halaman berikut %confirmationUrl% + + Hormat Kami, + Tim Website. + +# Global strings +layout: + logout: 'Keluar' + login: 'Masuk' + register: 'Pendaftaran' + logged_in_as: 'Masuk sebagai %username%' + +form: + username: 'Nama Pengguna' + email: 'Surel' + current_password: 'Kata sandi saat ini' + password: 'Kata sandi' + password_confirmation: 'Ulangi kata sandi' + new_password: 'Kata sandi Baru' + new_password_confirmation: 'Ulangi kata sandi baru' diff --git a/src/Resources/translations/FOSUserBundle.it.yml b/src/Resources/translations/FOSUserBundle.it.yml new file mode 100644 index 0000000000..c99a6dc233 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.it.yml @@ -0,0 +1,78 @@ +security: + login: + username: Username + password: Password + remember_me: Ricordami + submit: Login + +profile: + show: + username: Username + email: Email + edit: + submit: Aggiorna + flash: + updated: 'Il profilo è stato aggiornato.' + +change_password: + submit: 'Cambia password' + flash: + success: 'La password è stata cambiata.' + +registration: + check_email: 'Un''email è stata inviata a %email%. Contiene il link d''attivazione che devi utilizzare per attivare il tuo account.' + confirmed: 'Congratulazioni %username%, il tuo account è confermato.' + back: 'Torna alla pagina d''origine.' + submit: Registra + flash: + user_created: 'Utente creato con successo.' + email: + subject: 'Benvenuto %username%!' + message: | + Ciao %username%! + + Per terminare la validazione del tuo account visita %confirmationUrl% + + Questo link può essere utilizzato solo una volta per verificare il tuo account. + + Saluti, + il Team. + +resetting: + check_email: | + È stata inviata un'email con il link d'attivazione che devi utilizzare per il reset della password. + Nota: è possibile richiedere una password entro %tokenLifetime% ore. + + Se non ricevi un'email verifica la cartella di spam o riprova. + request: + username: 'Username o indirizzo email' + submit: 'Password reset' + reset: + submit: 'Cambia password' + flash: + success: 'La password è stata resettata.' + email: + subject: 'Password reset' + message: | + Ciao %username%! + + Per resettare la password visita %confirmationUrl% + + Saluti, + il Team. + +# Global strings +layout: + logout: Logout + login: Login + register: Registra + logged_in_as: 'Collegato come %username%' + +form: + username: Username + email: Email + current_password: 'Password corrente' + password: Password + password_confirmation: 'Ripeti password' + new_password: 'Nuova password' + new_password_confirmation: 'Ripeti password' diff --git a/src/Resources/translations/FOSUserBundle.ja.yml b/src/Resources/translations/FOSUserBundle.ja.yml new file mode 100644 index 0000000000..d82757e723 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.ja.yml @@ -0,0 +1,73 @@ +security: + login: + username: ユーザー名 + password: パスワード + remember_me: ログイン状態を記憶する + submit: ログイン +profile: + show: + username: ユーザー名 + email: メールアドレス + edit: + submit: 更新 + flash: + updated: プロフィールが更新されました +change_password: + submit: パスワードの変更 + flash: + success: パスワードが変更されました +registration: + check_email: '%email% 宛にメールを送信しました。メールに記載された確認 URL にアクセスして、アカウントを有効化してください。' + confirmed: '%username% さんのアカウントの確認が完了しました。' + back: 元のページに戻る + submit: 登録 + flash: + user_created: ユーザーの作成が完了しました + email: + subject: '%username% さん、ようこそ!' + message: | + こんにちは %username% さん! + + アカウントを有効化するには、次の確認 URL へアクセスしてください %confirmationUrl% + + このリンクは一度しか使用できません。 + + 開発チーム + +resetting: + check_email: | + メールが送信されました。送信されたリンクをクリックしてパスワードをリセットして下さい。 + 注意:パスワードのリセットは %tokenLifetime% 時間のあいだに一度しか要求できません。 + + もしメールが届かない場合は、迷惑メールフォルダをご確認いただき、その上で再度やり直して下さい。 + request: + username: ユーザー名またはメールアドレス + submit: パスワードのリセット + reset: + submit: パスワードの変更 + flash: + success: パスワードのリセットが完了しました + email: + subject: パスワードのリセット + message: | + こんにちは %username% さん! + + パスワードをリセットするには、次のリセット URL へアクセスしてください %confirmationUrl% + + + 開発チーム + +# Global strings +layout: + logout: ログアウト + login: ログイン + register: 登録 + logged_in_as: '%username% でログイン中' +form: + username: ユーザー名 + email: メールアドレス + current_password: 現在のパスワード + password: パスワード + password_confirmation: 確認 + new_password: 新しいパスワード + new_password_confirmation: 確認 diff --git a/src/Resources/translations/FOSUserBundle.ky.yml b/src/Resources/translations/FOSUserBundle.ky.yml new file mode 100644 index 0000000000..83f70e75c9 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.ky.yml @@ -0,0 +1,65 @@ +security: + login: + username: 'Колдонуучунун аты' + password: 'Сырдык сөз' + remember_me: 'Эске тутуу' + submit: Кирүү +profile: + show: + username: 'Колдонуучунун аты' + email: 'Электрондук почтасы' + edit: + submit: Жаңылоо + flash: + updated: 'Колдонуучунун профили жаңыланды' +change_password: + submit: 'Сырдык сөздү өзгөртүү' + flash: + success: 'Сырдык сөз өзгөрүлдү' +registration: + check_email: 'Кат %email% почтасына жөнөтүлдү. Анын ичинде каттону аныктоочу шилтеме бар.' + confirmed: 'Куттуктайбыз %username%, сиздин аккаунтуңуз ийгиликтүү катталды.' + back: 'Мурунку баракчага кайтуу.' + submit: Катталуу + flash: + user_created: 'Колдонуучу ийгиликтүү түзүлдү.' + email: + subject: 'Кош келдиңиз, %username%!' + message: | + Саламатсызбы, %username%! + + Регистрацияны анныктоо үчүн %confirmationUrl% + + шилтемеси кириңиз. +resetting: +# check_email: 'Кат %email% почтасына жөнөтүлдү. Ал жакта шилтеме бар жана ал шилтемеге кирүү менен сиздин сырдык сөзүңүз өзгөрөт.' + request: + username: 'Колдонуучунун аты' + submit: 'Сырдык сөздү өзгөртүү' + reset: + submit: 'Сырдык сөздү өзгөртүү' + flash: + success: 'Сырдык сөз ийгиликтүү өзгөрдү' + email: + subject: 'Сырдык сөздү өзгөртүү' + message: | + Саламатсызбы, %username%! + + Сырдык сөздү өзгөртүү үчүн, сураныч боюнча кириңиз + + Ийгилик каалоо менен, + сайттын командасы. + +layout: + logout: Чыгуу + login: Кирүү + register: Регистрация + logged_in_as: 'Сиз %username% аты менен кирдиңиз' +form: + username: 'Колдонуучунун аты' + email: 'Электрондук почтасы' + current_password: 'Учурдагы сырдык сөз' + password: 'Сырдык сөз' + password_confirmation: 'Сырдык сөздү аныктоо' + new_password: 'Жаңы сырдык сөз' + new_password_confirmation: 'Сырдык сөздү аныктоо' diff --git a/src/Resources/translations/FOSUserBundle.lb.yml b/src/Resources/translations/FOSUserBundle.lb.yml new file mode 100644 index 0000000000..0f6ca28155 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.lb.yml @@ -0,0 +1,79 @@ +security: + login: + username: Benotzernumm + password: Passwuert + remember_me: 'U mech erënneren' + submit: Umellen + +profile: + show: + username: Benotzernumm + email: E-Mail + edit: + submit: 'Benotzer aktualiséieren' + flash: + updated: 'De Benotzerprofil gouf aktualiséiert.' + +change_password: + submit: 'Passwuert ännern' + flash: + success: 'D''Passwuert gouf geännert.' + +registration: + check_email: | + Et gouf eng E-Mail un %email% geschéckt. Se enthält e Link deen ugeklickt muss ginn, fir de Benotzerkont ze bestätegen. + confirmed: 'Gléckwonsch %username%, Däi Benutzerkont ass elo bestätegt.' + back: 'Zréck op déi ursprénglech Säit.' + submit: Registréieren + flash: + user_created: 'De Benotzer gouf erfollegräich creéiert.' + email: + subject: 'Wëllkomm %username%!' + message: | + Hallo %username%! + + Fir Däi Benotzerkont ze bestätegen, besich wann ech gelift d'Säit %confirmationUrl% + + De Link ka just eemol benotzt gi fir de Kont ze validéieren. + + Mat beschte Gréiss, + d'Equipe. + +resetting: + check_email: | + Eng E-Mail gouf verschéckt. Se enthält e Link fir d'Passwuert zréckzesetzen. + Hiwäis: En neit Passwuert kann nëmmen all %tokenLifetime% Stonnen ugefrot ginn. + + Eventuell gouf dës E-Mail als Spam markéiert, falls se net ukomm ass. + request: + username: Benotzernumm + submit: 'Passwuert zerécksetzen' + reset: + submit: 'Passwuert ännern' + flash: + success: 'D''Passwuert gouf erfollegräich zeréckgesat.' + email: + subject: 'Passwuert zrécksetzen' + message: | + Hallo %username%! + + Fir Däi Passwuert zeréckzesetzen, besich wann ech gelift d'Säit %confirmationUrl% + + Mat beschte Gréiss, + d'Equipe. + +# Global strings +layout: + logout: Ofmellen + login: Umellen + register: Registréieren + logged_in_as: 'Ugemellt als %username%' + +form: + username: Benotzernumm + email: E-Mail-Adress + current_password: 'Aktuellt Passwuert' + password: Passwuert + password_confirmation: Bestätegung + new_password: 'Neit Passwuert' + new_password_confirmation: Bestätegung diff --git a/src/Resources/translations/FOSUserBundle.lt.yml b/src/Resources/translations/FOSUserBundle.lt.yml new file mode 100644 index 0000000000..14b3fbdd49 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.lt.yml @@ -0,0 +1,67 @@ +security: + login: + username: 'Naudotojo vardas' + password: Slaptažodis + remember_me: Atsiminti + submit: Prisijungti +profile: + show: + username: 'Naudotojo vardas' + email: 'El. paštas' + edit: + submit: Atnaujinti + flash: + updated: 'Profilis atnaujintas.' +change_password: + submit: 'Keisti slaptažodį' + flash: + success: 'Slaptažodis pakeistas.' +registration: + check_email: 'El. pašto pranešimas išsiųstas adresu %email%. Jame rasite nuorodą, kurią aplankę, aktyvuosite savo paskyrą.' + confirmed: 'Sveikiname, jūsų paskyra aktyvuota.' + back: 'Grįžti atgal' + submit: Registruotis + flash: + user_created: 'Naudotojas sukurtas.' + email: + subject: 'Registracijos patvirtinimas' + message: | + Sveiki %username%! + + Norėdami aktyvuoti savo paskyrą, apsilankykite adresu %confirmationUrl% + + Pagarbiai, + Komanda. + +resetting: +# check_email: 'El. pašto pranešimas išsiųstas adresu %email%. Jame rasite nuorodą, kurią paspaudę, galėsite pakeisti savo slaptažodį.' + request: + username: 'Naudotojo vardas arba el. paštas' + submit: Tęsti + reset: + submit: 'Pakeisti slaptažodį' + flash: + success: 'Slaptažodis pakeistas.' + email: + subject: 'Slaptažodžio keitimas' + message: | + Sveiki %username%! + + Norėdami pakeisti savo slaptažodį, apsilankykite adresu %confirmationUrl% + + Pagarbiai, + Komanda. + +layout: + logout: Atsijungti + login: Prisijungti + register: Registruotis + logged_in_as: 'Prisijungęs kaip %username%' +form: + username: 'Naudotojo vardas' + email: 'El. paštas' + current_password: 'Dabartinis slaptažodis' + password: Slaptažodis + password_confirmation: 'Pakartoti slaptažodį' + new_password: 'Naujas slaptažodis' + new_password_confirmation: 'Naujas slaptažodis (pakartoti)' diff --git a/src/Resources/translations/FOSUserBundle.lv.yml b/src/Resources/translations/FOSUserBundle.lv.yml new file mode 100644 index 0000000000..ef55541144 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.lv.yml @@ -0,0 +1,67 @@ +security: + login: + username: Lietotājvārds + password: Parole + remember_me: 'Atcerēties mani' + submit: Ienākt +profile: + show: + username: Lietotājvārds + email: E-pasts + edit: + submit: Saglabāt + flash: + updated: 'Profila izmaiņas tika saglabātas.' +change_password: + submit: 'Nomainīt paroli' + flash: + success: 'Parole tika nomainīta.' +registration: + check_email: 'Uz %email% tika nosūtīts e-pasts ar aktivizācijas saiti. Lūdzu sekojiet tai, lai aktivizētu savu kontu.' + confirmed: 'Apsveicam, %username%, tavs konts tika aktivizēts!' + back: 'Atpakaļ uz sākotnējo lapu.' + submit: Reģistrēties + flash: + user_created: 'Lietotājs tika izveidots.' + email: + subject: 'Sveiki, %username%!' + message: | + Sveiks, %username%! + + Lai pabeigtu aktivizēt savu kontu, lūdzu apmeklē %confirmationUrl% + + Jauku dienu, + lapas kolektīvs. + +resetting: +# check_email: 'Uz %email% tika nosūtīts e-pasts ar saiti, kurai jāseko, lai atiestatītu paroli.' + request: + username: 'Lietotājvārds vai e-pasta adrese' + submit: 'Atiestatīt paroli' + reset: + submit: 'Nomainīt paroli' + flash: + success: 'Parole tika atiestatīta.' + email: + subject: 'Paroles atiestatīšana' + message: | + Sveiki, %username%! + + Lai atiestatītu savu paroli, lūdzu apmeklē %confirmationUrl% + + Jauku dienu, + lapas kolektīvs. + +layout: + logout: Iziet + login: Ienākt + register: Reģistrēties + logged_in_as: 'Autorizējies kā %username%' +form: + username: Lietotājvārds + email: E-pasts + current_password: 'Esošā parole' + password: Parole + password_confirmation: Atkārtoti + new_password: 'Jaunā parole' + new_password_confirmation: Atkārtoti diff --git a/src/Resources/translations/FOSUserBundle.mn.yml b/src/Resources/translations/FOSUserBundle.mn.yml new file mode 100644 index 0000000000..8ab4da3aa9 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.mn.yml @@ -0,0 +1,79 @@ +security: + login: + username: Хэрэглэгчийн нэр + password: Нууц үг + remember_me: 'Намайг сана' + submit: 'Нэвтрэх' + +profile: + show: + username: Хэрэглэгчийн нэр + email: И-мэйл + edit: + submit: Шинэчлэх + flash: + updated: 'Хувийн мэдээлэл шинэчлэгдлээ.' + +change_password: + submit: 'Нууц үгээ өөрчлөх' + flash: + success: 'Нууц үг өөрчлөгдлөө.' + +registration: + check_email: | + И-мэйл %email% гэсэн хаяг руу илгээгдлээ. И-мэйлээр ирсэн баталгаажуулах холбоосоор орж эрхээ баталгаажуулна уу. + confirmed: 'Баяр хүргэе %username%, таны эрх баталгаажлаа.' + back: 'Өмнөх хуудас руу буцах.' + submit: Бүртгүүлэх + flash: + user_created: 'Хэрэглэгчийн эрх амжилттай үүслээ.' + email: + subject: 'Тавтай морилно уу %username%!' + message: | + Сайн байна уу %username%! + + Эрхээ баталгаажуулахын тулд - дараах холбоосоор орж баталгаажуулна уу %confirmationUrl% + + Энэхүү баталгаажуулах холбоос ганцхан удаа ашиглагдах боломжтойг анхаарна уу. + + Хүндэтгэсэн, + Багийн хамт олон. + +resetting: + check_email: | + И-мэйл илгээгдлээ. И-мэйлээр ирсэн холбоосоор орж шинэ нууц үгээ авна уу. + Note: Шинэ нууц үг авах хүсэлтийг та %tokenLifetime% цагийн дотор нэг удаа явуулах боломжтой. + + Хэрэв таньд и-мэйл ирээгүй бол спам хавтас дотроо шалгана уу эсвэл дахин шинээр оролдоно уу. + request: + username: 'Хэрэглэгчийн нэр эсвэл и-мэйл' + submit: 'Шинээр нууц үг авах' + reset: + submit: 'Нууц үгээ өөрчлөх' + flash: + success: 'Шинэ нууц үг авах хүсэлт амжилттай.' + email: + subject: 'Шинээр нууц үг авах' + message: | + Сайн байна уу %username%! + + Шинэ нууц үг авах бол - дараах холбоосоор орно уу %confirmationUrl% + + Хүндэтгэсэн, + Багийн хамт олон. + +# Global strings +layout: + logout: 'Гарах' + login: 'Нэвтрэх' + register: Бүртгүүлэх + logged_in_as: '%username% -ээр нэвтрэх' + +form: + username: Хэрэглэгчийн нэр + email: И-мэйл + current_password: 'Одоогийн нууц үг' + password: Нууц үг + password_confirmation: 'Нууц үгээ давтана уу' + new_password: 'Шинэ нууц үг' + new_password_confirmation: 'Шинэ нууц үгээ давтана уу' diff --git a/src/Resources/translations/FOSUserBundle.nb.yml b/src/Resources/translations/FOSUserBundle.nb.yml new file mode 100644 index 0000000000..e95abda15c --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.nb.yml @@ -0,0 +1,67 @@ +security: + login: + username: Brukernavn + password: Passord + remember_me: 'Husk meg' + submit: 'Logg inn' +profile: + show: + username: Brukernavn + email: Epost + edit: + submit: Oppdater + flash: + updated: 'Profilen har blitt oppdatert' +change_password: + submit: 'Endre passord' + flash: + success: 'Passordet har blitt endret' +registration: + check_email: 'En epost har blitt sendt til %email%. Den inneholder en aktiveringslenke du må klikke på for å aktivere kontoen din.' + confirmed: 'Gratulerer %username%, din konto er nå aktivert.' + back: 'Tilbake til den opprinnelige siden.' + submit: Registrer + flash: + user_created: 'Brukeren har blitt opprettet' + email: + subject: 'Velkommen %username%!' + message: | + Hallo %username%! + + For å fullføre aktiveringen av din konto - vennligst besøk %confirmationUrl% + + Med vennlig hilsen, + Teamet. + +resetting: +# check_email: 'En epost har blitt sendt til %email%. Den inneholder en lenke du må klikke på for å tilbakestille passordet.' + request: + username: 'Brukernavn eller epostadresse' + submit: 'Tilbakestill passord' + reset: + submit: 'Endre passord' + flash: + success: 'Passordet har blitt tilbakestilt' + email: + subject: 'Tilbakestill Passord' + message: | + Hallo %username%! + + For å tilbakestille ditt passord - vennligst besøk %confirmationUrl% + + Med vennlig hilsen, + Teamet. + +layout: + logout: 'Logg ut' + login: 'Logg inn' + register: Registrer + logged_in_as: 'Logget inn som %username%' +form: + username: Brukernavn + email: Epost + current_password: 'Nåværende passord' + password: Passord + password_confirmation: Verifisering + new_password: 'Nytt passord' + new_password_confirmation: Verifisering diff --git a/src/Resources/translations/FOSUserBundle.nl.yml b/src/Resources/translations/FOSUserBundle.nl.yml new file mode 100644 index 0000000000..0a6c1a518e --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.nl.yml @@ -0,0 +1,74 @@ +security: + login: + username: Gebruikersnaam + password: Wachtwoord + remember_me: 'Onthoud mijn gegevens' + submit: Inloggen +profile: + show: + username: Gebruikersnaam + email: E-mail + edit: + submit: Bijwerken + flash: + updated: 'Het profiel is bijgewerkt.' +change_password: + submit: 'Wachtwoord wijzigen' + flash: + success: 'Het wachtwoord is gewijzigd.' +registration: + check_email: 'Er is een e-mail verstuurd naar %email%, met een link om uw account te activeren.' + confirmed: 'Gefeliciteerd %username%, uw account is nu geactiveerd.' + back: 'Terug naar de oorspronkelijke pagina.' + submit: Registreer + flash: + user_created: 'De gebruiker is succesvol aangemaakt.' + email: + subject: 'Welkom %username%!' + message: | + Hallo %username%! + + U moet uw account nog valideren, ga naar %confirmationUrl% + + Deze link kan maar één keer gebruikt worden om je account te valideren. + + Vriendelijke groeten, + het Team. + +resetting: + check_email: | + Er werd een email gestuurd. De email bevat een link om je wachtwoord te resetten. + Opgelet: Je kan pas na %tokenLifetime% uur een nieuw wachtwoord aanvragen. + + Indien je geen email ontvangen hebt, controleer dan zeker je spam folder of probeer opnieuw. + request: + username: 'Gebruikersnaam of e-mailadres' + submit: 'Reset wachtwoord' + reset: + submit: 'Wijzig wachtwoord' + flash: + success: 'Het wachtwoord is succesvol gereset.' + email: + subject: 'Reset wachtwoord' + message: | + Hallo %username%! + + Om uw wachtwoord te resetten, klikt u op de volgende link %confirmationUrl% + + Met vriendelijke groet, + het Team. + +#Global strings +layout: + logout: Uitloggen + login: Inloggen + register: Registreer + logged_in_as: 'Ingelogd als %username%' +form: + username: Gebruikersnaam + email: E-mail + current_password: 'Huidig wachtwoord' + password: Wachtwoord + password_confirmation: 'Wachtwoord controle' + new_password: 'Nieuw wachtwoord' + new_password_confirmation: 'Wachtwoord controle' diff --git a/src/Resources/translations/FOSUserBundle.pl.yml b/src/Resources/translations/FOSUserBundle.pl.yml new file mode 100644 index 0000000000..2fe11e22bb --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.pl.yml @@ -0,0 +1,73 @@ +security: + login: + username: 'Nazwa użytkownika' + password: Hasło + remember_me: 'Zapamiętaj mnie' + submit: Zaloguj +profile: + show: + username: 'Nazwa użytkownika' + email: E-mail + edit: + submit: 'Edytuj użytkownika' + flash: + updated: 'Zapisano zmiany w profilu' +change_password: + submit: 'Zmień hasło' + flash: + success: 'Hasło zostało zmienione' +registration: + check_email: 'Na adres %email% wysłano wiadomość e-mail z linkiem. Kliknij go, aby aktywować Twoje konto.' + confirmed: 'Gratulacje %username%, Twoje konto zostało aktywowane.' + back: 'Powrót do poprzedniej strony' + submit: Zarejestruj + flash: + user_created: 'Twoje konto zostało utworzone' + email: + subject: 'Cześć %username%!' + message: | + Cześć %username%! + + Aby aktywować Twoje konto, przejdź pod adres %confirmationUrl% + + Pozdrawiamy, + Zespół. +resetting: + check_email: | + E-mail został wysłany. Zawiera on link do formularza zmiany hasła. + Uwaga: Możesz zresetować hasło tylko jeden raz w ciągu %tokenLifetime% godzin. + + Sprawdź swoją skrzynkę pocztową, jeśli jednak nie widzisz wiadomości od nas, sprawdź folder spam lub spróbuj + ponownie później. + + request: + username: 'Nazwa użytkownika lub e-mail' + submit: 'Resetuj hasło' + reset: + submit: 'Zmień hasło' + flash: + success: 'Hasło zostało zresetowane' + email: + subject: 'Resetowanie hasła' + message: | + Cześć %username%! + + Aby zresetować hasło, przejdź pod adres %confirmationUrl% + + Pozdrawiamy, + Zespół. + +# Global strings +layout: + logout: Wyloguj + login: Zaloguj + register: Zarejestruj + logged_in_as: 'Zalogowano jako %username%' +form: + username: 'Nazwa użytkownika' + email: E-mail + current_password: 'Obecne hasło' + password: Hasło + password_confirmation: 'Powtórz hasło' + new_password: 'Nowe hasło' + new_password_confirmation: 'Powtórz hasło' diff --git a/src/Resources/translations/FOSUserBundle.pt.yml b/src/Resources/translations/FOSUserBundle.pt.yml new file mode 100644 index 0000000000..0d071d4436 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.pt.yml @@ -0,0 +1,72 @@ +security: + login: + username: 'Utilizador' + password: 'Password' + remember_me: 'Lembrar-me' + submit: 'Entrar' +profile: + show: + username: 'Utilizador' + email: 'Email' + edit: + submit: 'Atualizar conta' + flash: + updated: 'O perfil foi atualizado' +change_password: + submit: 'Mudar a password' + flash: + success: 'A password foi alterada' +registration: + check_email: 'Foi enviado um email para %email%. Este contém um link de ativação que terá de visitar para ativar a sua conta.' + confirmed: 'Parabéns %username%, a sua conta foi confirmada.' + back: 'Voltar para a página anterior.' + submit: 'Registar' + flash: + user_created: 'O utilizador foi criado com sucesso' + email: + subject: 'Bem-vindo(a) %username%!' + message: | + Olá %username%! + + Para completar a validação da sua conta, por favor visite o seguinte link: %confirmationUrl% + + Cumprimentos, + A equipa. + +resetting: + check_email: | + Foi enviado um email. Este contém um link que terá de visitar para recuperar a sua password. + Nota: Só poderá voltar a pedir para recuperar a sua password dentro de %tokenLifetime% horas. + + Se não receber o email verifique a pasta de spam do seu cliente de email ou tente novamente. + request: + username: 'Utilizador ou endereço de email' + submit: 'Recuperar password' + reset: + submit: 'Alterar password' + flash: + success: 'A password foi recuperada com sucesso' + email: + subject: 'Recuperar password' + message: | + Olá %username%! + + Para recuperar a password, por favor aceda a %confirmationUrl% + + Cumprimentos, + A equipa. + +# Global strings +layout: + logout: 'Sair' + login: 'Entrar' + register: 'Registar' + logged_in_as: 'Entrou como %username%' +form: + username: 'Utilizador' + email: 'Email' + current_password: 'Password atual' + password: 'Password' + password_confirmation: 'Verificar password' + new_password: 'Nova password' + new_password_confirmation: 'Verificar a nova password' diff --git a/src/Resources/translations/FOSUserBundle.pt_BR.yml b/src/Resources/translations/FOSUserBundle.pt_BR.yml new file mode 100644 index 0000000000..521a40f9db --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.pt_BR.yml @@ -0,0 +1,71 @@ +security: + login: + username: Usuário + password: Senha + remember_me: 'Permanecer conectado' + submit: Entrar +profile: + show: + username: Usuário + email: Email + edit: + submit: Atualizar + flash: + updated: 'O perfil foi atualizado.' +change_password: + submit: 'Alterar senha' + flash: + success: 'A senha foi alterada.' +registration: + check_email: 'Um email foi enviado para o endereço %email%. Para ativar a sua conta, clique no link na mensagem.' + confirmed: 'Parabéns, %username%. A sua conta foi ativada.' + back: 'Retornar para a página de origem.' + submit: Registrar + flash: + user_created: 'O usuário foi criado com sucesso.' + email: + subject: 'Bem-vindo(a), %username%!' + message: | + Olá, %username%! + + Para completar a validação da sua conta, clique no link: %confirmationUrl% + + Atenciosamente, + a Equipe. + +resetting: + check_email: | + Um email foi enviado. Ele contem um link que deve ser acessado para resetar sua senha. + Nota: Você apenas poderá requisitar uma nova senha dentro de %tokenLifetime% horas. + + Se você não receber sua senha, cheque sua pasta de spam ou tente novamente. + request: + username: 'Usuário ou email' + submit: 'Recuperar senha' + reset: + submit: 'Alterar senha' + flash: + success: 'A senha foi redefinida com sucesso.' + email: + subject: 'Redefinir senha' + message: | + Olá, %username%! + + Para redefinir sua senha, clique no link: %confirmationUrl% + + Atenciosamente, + a Equipe. + +layout: + logout: Sair + login: Entrar + register: Registrar + logged_in_as: 'Conectado como %username%' +form: + username: Usuário + email: Email + current_password: 'Senha atual' + password: Senha + password_confirmation: 'Repita a senha' + new_password: 'Nova senha' + new_password_confirmation: 'Repita a nova senha' diff --git a/src/Resources/translations/FOSUserBundle.ro.yml b/src/Resources/translations/FOSUserBundle.ro.yml new file mode 100644 index 0000000000..5dcf9a052f --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.ro.yml @@ -0,0 +1,78 @@ +security: + login: + username: 'Nume de utilizator' + password: Parola + remember_me: 'Ține-mă minte' + submit: Autentificare + +profile: + show: + username: 'Numele de utilizator' + email: Email + edit: + submit: Actualizează + flash: + updated: 'Profilul a fost actualizat.' + +change_password: + submit: 'Schimbă parola' + flash: + success: 'Parola a fost schimbată.' +registration: + check_email: | + Un email a fost trimis către %email%. Conține un link de activare pe care trebuie să îl accesezi pentru a-ți activa contul. + confirmed: 'Felicitări %username%, contul tău a fost activat.' + back: 'Înapoi la pagina de la care ai venit.' + submit: Înregistrează-te + flash: + user_created: 'Utilizatorul a fost creat cu succes.' + email: + subject: 'Bine ai venit %username%!' + message: | + Salut %username%! + + Pentru a-ți activa contul - accesează %confirmationUrl% + + Acest link poate fi folosit numai o singură dată pentru a activa contul. + + Toate cele bune, + Echipa + +resetting: + check_email: | + Un email a fost trimis. Conține un link pe care trebuie să îl accesezi pentru a-ți reseta parola. + Notă: Poți cere o nouă parolă numai în următoarele %tokenLifetime% ore. + + Daca nu primești email-ul verifica in spam sau mai incearca o dată. + request: + username: 'Numele de utilizator sau adresa de email' + submit: 'Resetează parola' + reset: + submit: 'Schimbă parola' + flash: + success: 'Parola a fost resetată cu succes.' + email: + subject: 'Resetare parolă' + message: | + Salut %username%! + + Pentru a-ți reseta parola - accesează %confirmationUrl% + + Toate cele bune, + Echipa + +# Global strings +layout: + logout: 'Ieșire' + login: Autentificare + register: Înregistrare + logged_in_as: 'Autentificat ca %username%' + +form: + username: 'Nume de utilizator' + email: Email + current_password: 'Parola curentă' + password: Parolă + password_confirmation: 'Verificare parolă' + new_password: 'Parola nouă' + new_password_confirmation: 'Verificare parolă nouă' diff --git a/src/Resources/translations/FOSUserBundle.ru.yml b/src/Resources/translations/FOSUserBundle.ru.yml new file mode 100644 index 0000000000..118ed7f727 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.ru.yml @@ -0,0 +1,75 @@ +security: + login: + username: 'Имя пользователя' + password: Пароль + remember_me: 'Запомнить меня' + submit: Войти +profile: + show: + username: 'Имя пользователя' + email: 'Электронная почта' + edit: + submit: Обновить + flash: + updated: 'Профиль пользователя обновлен.' +change_password: + submit: 'Изменить пароль' + flash: + success: 'Пароль изменен.' + +registration: + check_email: 'Письмо отправлено на адрес %email%. В нём содержится ссылка, по которой вы можете подтвердить свою регистрацию.' + confirmed: 'Поздравляем %username%, ваш аккаунт подтвержден.' + back: 'Вернуться на предыдущую страницу.' + submit: Зарегистрироваться + flash: + user_created: 'Пользователь успешно создан.' + email: + subject: 'Добро пожаловать, %username%!' + message: | + Приветствуем, %username%! + + Для подтверждения вашей регистрации, пройдите по ссылке %confirmationUrl% + + Эта ссылка может быть использована только единожды для подтверждения вашей учетной записи. + + С наилучшими пожеланиями, + команда сайта. + +resetting: + check_email: | + Письмо отправлено. Оно содержит ссылку, при переходе по которой ваш пароль будет сброшен. + Заметьте, вы сможете запросить новый пароль только через %tokenLifetime% часов. + + Если вы не получили письмо, проверьте папку Спам или попробуйте снова. + + request: + username: 'Имя пользователя или электронная почта' + submit: 'Сбросить пароль' + reset: + submit: 'Изменить пароль' + flash: + success: 'Пароль успешно сброшен.' + email: + subject: 'Сброс пароля' + message: | + Приветствуем, %username%! + + Для сброса пароля, пожалуйста, пройдите по ссылке %confirmationUrl% + + С наилучшими пожеланиями, + команда сайта. + +layout: + logout: Выход + login: Вход + register: Регистрация + logged_in_as: 'Вы вошли как %username%' +form: + username: 'Имя пользователя' + email: 'Электронная почта' + current_password: 'Текущий пароль' + password: Пароль + password_confirmation: 'Подтвердите пароль' + new_password: 'Новый пароль' + new_password_confirmation: 'Подтвердите пароль' diff --git a/src/Resources/translations/FOSUserBundle.sk.yml b/src/Resources/translations/FOSUserBundle.sk.yml new file mode 100644 index 0000000000..32a852c17a --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.sk.yml @@ -0,0 +1,67 @@ +security: + login: + username: 'Používateľské meno' + password: Heslo + remember_me: 'Zapamätať si' + submit: Prihlásenie +profile: + show: + username: 'Používateľské meno' + email: Email + edit: + submit: Upraviť + flash: + updated: 'Profil bol úspešne zmenený.' +change_password: + submit: 'Zmena hesla' + flash: + success: 'Heslo bolo úspešne zmenené.' +registration: + check_email: 'Aktivačný email bol zaslaný na adresu %email%. V tele správy je aktivačný link, na ktorý treba kliknúť pre úspešné dokončenie registrácie.' + confirmed: 'Gratulujeme %username%, Vaše konto bolo aktivované.' + back: 'Návrat na predchádzajúcu stránku.' + submit: Registrácia + flash: + user_created: 'Vaše konto bolo úspešne vytvorené.' + email: + subject: 'Vitaj %username%!' + message: | + Ahoj %username%! + + Pre ukončenie registrácie, prosím klikni na nasledovný link: %confirmationUrl% + + S pozdravom, + Realizačný tím + +resetting: +# check_email: 'Postup pre zmenu hesla bol zaslaný na adresu %email%. Správa obsahuje link, na ktorú treba kliknúť pre úspešné dokončenie zmeny hesla.' + request: + username: 'Používateľské meno alebo emailová adresa' + submit: 'Obnova hesla' + reset: + submit: 'Zmeniť heslo' + flash: + success: 'Heslo bolo úspešne zmenené.' + email: + subject: 'Vynulovanie hesla' + message: | + Ahoj %username%! + + Pre zmenu hesla, klikni prosím na nasledovný link: %confirmationUrl% + + S pozdravom, + Realizačný tím + +layout: + logout: Odhlásenie + login: Prihlásenie + register: Registrácia + logged_in_as: 'Ste prihlásený ako %username%' +form: + username: 'Používateľské meno' + email: Email + current_password: 'Súčasné heslo' + password: Heslo + password_confirmation: 'Potvrdenie hesla' + new_password: 'Nové heslo' + new_password_confirmation: 'Potvrdenie hesla' diff --git a/src/Resources/translations/FOSUserBundle.sl.yml b/src/Resources/translations/FOSUserBundle.sl.yml new file mode 100644 index 0000000000..f276fdf227 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.sl.yml @@ -0,0 +1,67 @@ +security: + login: + username: 'Uporabniško ime' + password: Geslo + remember_me: 'Zapomni si me' + submit: Prijava +profile: + show: + username: 'Uporabniško ime' + email: Email + edit: + submit: Shrani + flash: + updated: 'Profil je bil uspešno shranjen.' +change_password: + submit: 'Spremeni geslo' + flash: + success: 'Geslo je bilo uspešno spremenjeno.' +registration: + check_email: 'Na %email% smo vam poslali sporočilo s kodo za aktivacijo. Račun aktivirate s klikom na to povezavo.' + confirmed: 'Pozdravljeni %username%, vaš račun je sedaj aktiviran.' + back: 'Nazaj na prvotno stran.' + submit: Registracija + flash: + user_created: 'Uporabnik je bil uspešno ustvarjen.' + email: + subject: 'Dobrodošli %username%!' + message: | + Pozdravljeni %username%! + + Za aktivacijo računa prosimo, obiščite %confirmationUrl% + + Lep pozdrav, + ekipa. + +resetting: +# check_email: 'Na %email% smo vam poslali sporočilo s kodo za spremembo gesla. Geslo boste spremenili po kliku na to povezavo.' + request: + username: 'Uporabniško ime' + submit: 'Spremeni geslo' + reset: + submit: 'Spremeni geslo' + flash: + success: 'Geslo je bilo uspešno spremenjeno.' + email: + subject: 'Sprememba gesla' + message: | + Pozdravljeni %username%! + + Za spremembo gesla prosimo, obiščite %confirmationUrl% + + Lep pozdrav, + ekipa. + +layout: + logout: Odjava + login: Prijava + register: Registracija + logged_in_as: 'Prijavljen kot %username%' +form: + username: 'Uporabniško ime' + email: Email + current_password: 'Trenutno geslo' + password: Geslo + password_confirmation: 'Preverjanje gesla' + new_password: 'Novo geslo' + new_password_confirmation: 'Preverjanje gesla' diff --git a/src/Resources/translations/FOSUserBundle.sr_Latn.yml b/src/Resources/translations/FOSUserBundle.sr_Latn.yml new file mode 100644 index 0000000000..c130519b35 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.sr_Latn.yml @@ -0,0 +1,69 @@ +security: + login: + username: 'Korisničko ime' + password: Lozinka + remember_me: 'Zapamti me' + submit: 'Prijavi se' +profile: + show: + username: 'Korisničko ime' + email: 'Adresa e-pošte' + edit: + submit: Ažuriraj + flash: + updated: 'Profil je ažuriran.' +change_password: + submit: 'Izmeni lozinku' + flash: + success: 'Lozinka je izmenjena.' +registration: + check_email: 'Poruka je poslata na adresu %email%. Ona sadrži aktivacioni link koji morate kliknuti da bi aktivirali Vaš nalog.' + confirmed: 'Čestitamo %username%, Vaš nalog je sada aktivan.' + back: 'Nazad na prethodnu stranu.' + submit: 'Registruj se' + flash: + user_created: 'Korisnički nalog je uspešno kreiran.' + email: + subject: 'Dobrodošli %username%!' + message: | + Zdravo %username%! + + Da bi aktivirali Vaš nalog posetite %confirmationUrl% + + Ovaj link možete upotrebiti samo jednom da biste potvrdili Vaš nalog. + + S poštovanjem, + tim sajta. + +resetting: + check_email: 'Poruka je poslata na adresu %email%. Ona sadrži link koji morate kliknuti da bi resetovali Vašu lozinku.' + request: + username: 'Korisničko ime ili adresa e-pošte' + submit: 'Resetuj lozinku' + reset: + submit: 'Izmeni lozinku' + flash: + success: 'Lozinka je uspešno resetovana.' + email: + subject: 'Resetovanje lozinke' + message: | + Zdravo %username%! + + Da bi resetovali Vašu lozinku posetite %confirmationUrl% + + S poštovanjem, + tim sajta. + +layout: + logout: Odjavljivanje + login: Prijavljivanje + register: Registracija + logged_in_as: 'Prijavljen kao %username%' +form: + username: 'Korisničko ime' + email: 'Adresa e-pošte' + current_password: 'Trenutna lozinka' + password: Lozinka + password_confirmation: 'Potvrda lozinke' + new_password: 'Nova lozinka' + new_password_confirmation: 'Potvrda lozinke' diff --git a/src/Resources/translations/FOSUserBundle.sv.yml b/src/Resources/translations/FOSUserBundle.sv.yml new file mode 100644 index 0000000000..eae1b7d7f5 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.sv.yml @@ -0,0 +1,73 @@ +security: + login: + username: Användarnamn + password: Lösenord + remember_me: 'Kom ihåg mig' + submit: 'Logga in' +profile: + show: + username: Användarnamn + email: Epost + edit: + submit: Uppdatera + flash: + updated: 'Profilen har uppdaterats.' +change_password: + submit: 'Ändra lösenord' + flash: + success: 'Lösenordet har ändrats.' +registration: + check_email: 'Ett meddelande har skickats till %email%. Det innehåller en länk som du måste klicka på för att aktivera ditt konto.' + confirmed: 'Grattis %username%, ditt konto är nu aktiverat.' + back: 'Tillbaka till den ursprungliga sidan.' + submit: Registrera + flash: + user_created: 'Användaren har skapats.' + email: + subject: 'Välkommen %username%!' + message: | + Hej %username%! + + För att aktivera ditt konto - vänligen besök %confirmationUrl% + + Länken kan endast användas en gång för att aktivera ditt konto. + + Hälsningar, + Teamet. + +resetting: + check_email: | + Ett meddelande har skickats. Det innehåller en länk som du kan klicka för att återställa ditt lösenord. + Obs! Man kan inte återställa sitt lösenord oftare än var %tokenLifetime% timme. + + Om du inte har fått ett meddelande, vänligen kolla din skräpinkorg eller försök igen. + request: + username: 'Användarnamn eller epost-adress' + submit: 'Återställ lösenord' + reset: + submit: 'Ändra lösenord' + flash: + success: 'Lösenordet har återställts.' + email: + subject: 'Återställ lösenord' + message: | + Hej %username%! + + För att återställa ditt lösenord - vänligen besök %confirmationUrl% + + Hälsningar, + Teamet. + +layout: + logout: 'Logga ut' + login: 'Logga in' + register: Registrera + logged_in_as: 'Inloggad som %username%' +form: + username: Användarnamn + email: Epost + current_password: 'Nuvarande lösenord' + password: Lösenord + password_confirmation: Verifiering + new_password: 'Nytt lösenord' + new_password_confirmation: Verifiering diff --git a/src/Resources/translations/FOSUserBundle.th.yml b/src/Resources/translations/FOSUserBundle.th.yml new file mode 100644 index 0000000000..eac5c466de --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.th.yml @@ -0,0 +1,70 @@ +security: + login: + username: ชื่อผู้ใช้ + password: รหัสผ่าน + remember_me: จำการเข้าระบบ + submit: เข้าระบบ +profile: + show: + username: ชื่อผู้ใช้ + email: อีเมล์ + edit: + submit: บันทึกการแก้ไข + flash: + updated: ข้อมูลส่วนตัวถูกแก้ไขแล้ว +change_password: + submit: เปลี่ยนรหัสผ่าน + flash: + success: รหัสผ่านถูกแก้ไขแล้ว +registration: + check_email: 'ระบบได้ส่งอีเมลยืนยันการสมัครสมาชิกไปที่ %email% คุณต้องคลิกลิงค์ที่ส่งไปเพื่อยืนยันการสมัคร' + confirmed: 'ยินดีด้วย %username%, บัญชีของคุณถูกยืนยันแล้ว' + back: กลับไปหน้าเดิม + submit: สมัครสมาชิก + flash: + user_created: สร้างผู้ใช้เรียบร้อยแล้ว + email: + subject: 'ยินดีต้อนรับ %username%!' + message: | + สวัสดี %username%! + + เพื่อยืนยันการสมัครสมาชิกให้เสร็จสมบูรณ์ กรุณาไปที่ %confirmationUrl% + + ด้วยความนับถือ, + ทีมงาน + +resetting: + check_email: | + อีเมลถูกส่งไปให้คุณพร้อมกับลิงค์เพื่อคลิ๊กสำหรับตั้งรหัสผ่านใหม่ โดยลิงค์นี้จะมีอายุใช้ได้ %tokenLifetime% ชั่วโมง + + หากคุณไม่ได้รับอีเมลกรุณาเช็คที่โฟลเดอร์สแปมหรือลองทำอีกครั้ง + request: + username: ชื่อผู้ใช้หรืออีเมล์ + submit: เปลี่ยนรหัสผ่าน + reset: + submit: บันทึกการเปลี่ยนแปลงรหัส + flash: + success: เปลี่ยนรหัสผ่านเรียบร้อยแล้ว + email: + subject: การเปลี่ยนรหัสผ่าน + message: | + สวัสดี %username%! + + เพื่อดำเนินการเปลี่ยนรหัสผ่านของคุณ กรุณาไปที่ %confirmationUrl% + + ด้วยความนับถือ, + ทีมงาน + +layout: + logout: ออกจากระบบ + login: เข้าระบบ + register: สมัครสมาชิก + logged_in_as: 'เข้าระบบด้วย %username%' +form: + username: ชื่อเข้าระบบ + email: อีเมล์ + current_password: รหัสผ่านปัจจุบัน + password: รหัสผ่าน + password_confirmation: ยืนยันรหัสผ่าน + new_password: รหัสผ่านใหม่ + new_password_confirmation: ยืนยันรหัสผ่านใหม่ diff --git a/src/Resources/translations/FOSUserBundle.tr.yml b/src/Resources/translations/FOSUserBundle.tr.yml new file mode 100644 index 0000000000..136b66216a --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.tr.yml @@ -0,0 +1,73 @@ +security: + login: + username: 'Kullanıcı adı' + password: Parola + remember_me: 'Beni hatırla' + submit: Giriş +profile: + show: + username: 'Kullanıcı adı' + email: 'E-posta adresi' + edit: + submit: Güncelle + flash: + updated: 'Profil Güncellendi.' +change_password: + submit: 'Parolayı Değiştir' + flash: + success: 'Parola değiştirildi.' +registration: + check_email: 'E-posta adresinize hesabınızı aktive etmek için kullanacağınız aktivasyon linkini barındıran bir e-posta gönderildi.' + confirmed: 'Tebrikler %username%. Hesabınız şu anda aktifleştirildi.' + back: 'Asıl sayfaya geri dön.' + submit: 'Kayıt ol' + flash: + user_created: 'Kullanıcı başarıyla yaratıldı.' + email: + subject: 'Hoşgeldin %username%!' + message: | + Hoşgeldiniz %username%! + + Hesabınızın aktivasyonunu tamamlamak için lütfen %confirmationUrl% + adresini ziyaret ediniz. + + Teşekkürler, + Web Takımı. + +resetting: + check_email: | + E-posta adresinize parolanızı sıfırlama adresi bulunan bir e-posta gönderildi. + Not: Yeni şifre talep etmek için %tokenLifetime% saat beklemelisiniz. + + E-posta gelmedi ise spam klasörünü kontrol edin veya tekrar deneyin. + request: + username: 'Kullanıcı adı ya da e-posta adresi' + submit: 'Parolayı sıfırla' + reset: + submit: 'Parolayı değiştir' + flash: + success: 'Parola başarıyla değiştirildi.' + email: + subject: 'Parola Yenileme' + message: | + Merhaba %username%! + + Parolanızı yenilemek için %confirmationUrl% + adresini ziyaret ediniz. + + Teşekkürler, + Web Takımı. + +layout: + logout: Çıkış + login: Giriş + register: 'Kayıt ol' + logged_in_as: '%username% olarak giriş yapıldı' +form: + username: 'Kullanıcı adı' + email: 'E-posta adresi' + current_password: 'Geçerli parola' + password: Parola + password_confirmation: Parola tekrar + new_password: 'Yeni parola' + new_password_confirmation: Yeni parola tekrar diff --git a/src/Resources/translations/FOSUserBundle.uk.yml b/src/Resources/translations/FOSUserBundle.uk.yml new file mode 100644 index 0000000000..754d10cd25 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.uk.yml @@ -0,0 +1,73 @@ +security: + login: + username: 'Ім''я користувача' + password: Пароль + remember_me: 'Запам''ятати мене' + submit: Увійти +profile: + show: + username: 'Ім''я користувача' + email: 'Електронна адреса' + edit: + submit: Оновити + flash: + updated: 'Профіль користувача оновлено.' +change_password: + submit: 'Змінити пароль' + flash: + success: 'Пароль змінено.' +registration: + check_email: 'Листа надіслано на адресу %email%. У ньому міститься посилання, за яким Ви можете підтвердити свою реєстрацію.' + confirmed: 'Вітаємо %username%, Ваш аккаунт підтверджено.' + back: 'Повернутися на попередню сторінку.' + submit: Реєстрація + flash: + user_created: 'Користувач успішно створений.' + email: + subject: 'Вітаємо %username%!' + message: | + Вітаємо %username%! + + Для підтверждення Вашої реєстрації, перейдіть за посиланням %confirmationUrl% + + Це посилання може бути використано лише один раз для підтвердження Вашого облікового запису. + + З найкращими побажаннями, + команда сайту. + +resetting: + check_email: | + Лист відправлено. Він містить посилання, при переході за яким Ваш пароль буде скинуто. + Примітка: Ви можете змінити пароль тільки протягом %tokenLifetime% годин. + + Якщо Ви не отримали лист то перевірте будь-ласка папку зі спамом. + request: + username: 'Ім''я користувача' + submit: 'Скинути пароль' + reset: + submit: 'Змінити пароль' + flash: + success: 'Пароль успішно скинуто.' + email: + subject: 'Скидання пароля' + message: | + Вітаємо %username%! + + Для скидання пароля - будь ласка, перейдіть за посиланням %confirmationUrl% + + З найкращими побажаннями, + команда сайту. + +layout: + logout: Вихід + login: Вхід + register: Реєстрація + logged_in_as: 'Ви увійшли як %username%' +form: + username: 'Ім''я користувача' + email: 'Електронна адреса' + current_password: 'Поточний пароль' + password: Пароль + password_confirmation: 'Підтвердіть пароль' + new_password: 'Новий пароль' + new_password_confirmation: 'Підтвердіть пароль' diff --git a/src/Resources/translations/FOSUserBundle.vi.yml b/src/Resources/translations/FOSUserBundle.vi.yml new file mode 100644 index 0000000000..2607d07607 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.vi.yml @@ -0,0 +1,74 @@ +security: + login: + username: 'Tên đăng nhập:' + password: 'Mật khẩu:' + remember_me: 'Ghi nhớ thông tin của tôi' + submit: 'Đăng nhập' + +profile: + show: + username: 'Tên đăng nhập' + email: Email + edit: + submit: 'Cập nhật' + flash: + updated: 'Hồ sơ cá nhân đã được cập nhật' + +change_password: + submit: 'Thay đổi mật khẩu' + flash: + success: 'Mật khẩu đã được thay đổi' + +registration: + check_email: 'Một email đã được gửi đến %email%. Nó có chứa một liên kết kích hoạt bạn phải bấm vào để kích hoạt tài khoản của bạn.' + confirmed: 'Chúc mừng %username%, tài khoản của bạn đã được kích hoạt.' + back: 'Quay lại' + submit: 'Đăng ký' + flash: + user_created: 'Người sử dụng đã được khởi tạo' + email: + subject: 'Chào mừng %username%!' + message: | + Xin chào %username%! + + Để hoàn tất kích hoạt tài khoản của bạn - vui lòng truy cập %confirmationUrl%. + + Cám ơn.' + +resetting: + check_email: | + Một email đã được gởi đến cho bạn. Nó có chứa một liên kết kích hoạt bạn phải bấm vào để kích hoạt tài khoản của bạn. + Lưu ý: Bạn chỉ có thể yêu cầu mật khẩu mới một lần trong vòng %tokenLifetime% giờ. + + Nếu bạn không thấy email mới vui lòng kiểm tra hộp thư quảng cáo hoặc thử lại lần nữa. + request: + username: 'Tên đăng nhập hoặc địa chỉ email:' + submit: 'Thiết lập lại mật khẩu' + reset: + submit: 'Thay đổi mật khẩu' + flash: + success: 'Mật khẩu đã được thiết lập lại thành công' + email: + subject: 'Thiết lập lại mật khẩu' + message: | + Xin chào %username%! + + Để thiết lập lại mật khẩu của bạn - vui lòng truy cập %confirmationUrl% + + Trân trọng. + +# Global strings +layout: + logout: 'Đăng xuất' + login: 'Đăng nhập' + register: 'Đăng ký' + logged_in_as: 'Đăng nhập như %username%' + +form: + username: 'Tên đăng nhập:' + email: 'Email:' + current_password: 'Mật khẩu hiện tại:' + password: 'Mật khẩu:' + password_confirmation: 'Xác minh:' + new_password: 'Mật khẩu mới:' + new_password_confirmation: 'Xác minh:' diff --git a/src/Resources/translations/FOSUserBundle.zh_CN.yml b/src/Resources/translations/FOSUserBundle.zh_CN.yml new file mode 100644 index 0000000000..6215be6810 --- /dev/null +++ b/src/Resources/translations/FOSUserBundle.zh_CN.yml @@ -0,0 +1,67 @@ +security: + login: + username: 用户名 + password: 密码 + remember_me: 自动登录 + submit: 登录 +profile: + show: + username: 用户名 + email: 电子邮箱 + edit: + submit: 更新 + flash: + updated: 用户信息已更新 +change_password: + submit: 修改密码 + flash: + success: 密码已成功修改 +registration: + check_email: 系统向%email%发送了一封包含激活链接的邮件,请访问该链接以启用你的帐户。 + confirmed: '%username%,恭喜你,你的帐户已启用!' + back: 返回前一页面 + submit: 注册 + flash: + user_created: 用户已创建 + email: + subject: '%username%,欢迎你' + message: | + %username%,你好! + + 要启用你的用户帐号,请访问:%confirmationUrl% + + 祝好 + 网站团队 + +resetting: +# check_email: 系统向%email%发送了一封包含密码重置链接的邮件,请查收。 + request: + username: 用户名或邮箱 + submit: 重置密码 + reset: + submit: 修改密码 + flash: + success: 密码已重置。 + email: + subject: 重置密码 + message: | + %username%,你好! + + 请访问 %confirmationUrl% 以重置你的帐户密码。 + + 祝好 + 网站团队 + +layout: + logout: 退出 + login: 登录 + register: 注册 + logged_in_as: 已登录为:%username% +form: + username: 用户名 + email: 电子邮箱 + current_password: 当前密码 + password: 密码 + password_confirmation: 确认密码 + new_password: 新密码 + new_password_confirmation: 确认新密码 diff --git a/src/Resources/translations/validators.ar.yml b/src/Resources/translations/validators.ar.yml new file mode 100644 index 0000000000..67f70e6dea --- /dev/null +++ b/src/Resources/translations/validators.ar.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'الإسم المختار مسجل مسبقا' + blank: 'يرجى تعيين إسم المستخدم' + short: ' إسم المستخدم قصير جدا' + long: ' إسم المستخدم طويل جدا' + email: + already_used: 'البريد الإلكتروني مسجل مسبقا' + blank: 'يرجى تعين البريد الإلكتروني' + short: ' البريد الإلكتروني قصير' + long: ' البريد الإلكتروني طويل' + invalid: 'البريد الإلكتروني غير صحيح' + password: + blank: 'يرجى تعيين كلمة المرور' + short: 'كلمة المرور قصيرة' + mismatch: 'كلمة المرور المدخلة غير مطابقة للكلمة الأصلية' + new_password: + blank: 'يرجى تعيين كلمة مرور جديدة' + short: ' كلمة المرور الجديدة قصيرة' + current_password: + invalid: 'كلمة المرور المدخلة غير صحيحة' diff --git a/src/Resources/translations/validators.bg.yml b/src/Resources/translations/validators.bg.yml new file mode 100644 index 0000000000..6c540a848d --- /dev/null +++ b/src/Resources/translations/validators.bg.yml @@ -0,0 +1,20 @@ +fos_user: + username: + already_used: 'Потребителското име е заето.' + blank: 'Моля въведете потребителско име.' + short: 'Потребителското име е прекалено късо.' + long: 'Потребителското име е прекалено дълго.' + email: + already_used: 'E-mail адреса е зает.' + blank: 'Моля въведете E-mail адрес.' + short: 'E-mail адреса е прекалено къс.' + long: 'E-mail адреса е прекалено дълъг.' + invalid: 'E-mail адреса е невалиден.' + password: + blank: 'Моля въведете парола.' + short: 'Паролата е прекалено къса.' + new_password: + blank: 'Моля въведете нова парола.' + short: 'Новата парола е прекалено къса.' + current_password: + invalid: 'Въведената парола е невалидна.' diff --git a/src/Resources/translations/validators.bn.yml b/src/Resources/translations/validators.bn.yml new file mode 100644 index 0000000000..d6c3f477da --- /dev/null +++ b/src/Resources/translations/validators.bn.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'ব্যবহারকারীর নামটি ইতিমধ্যে ব্যবহার করা হয়েছে' + blank: 'অনুগ্রহ করে ব্যবহারকারীর নাম লিখুন' + short: 'নামটি থুবই ছোট' + long: 'নামটি থুবই বড়' + email: + already_used: 'ই-মেইল টি ইতিমধ্যে ব্যবহার করা হয়েছে' + blank: 'অনুগ্রহ করে একটি ই-মেইল লিখুন' + short: 'ই-মেইল টি থুবই ছোট' + long: 'ই-মেইল টি থুবই বড়' + invalid: 'ই-মেইল টি সঠিক নয়' + password: + blank: 'অনুগ্রহ করে পাসওয়ার্ড লিখুন' + short: 'পাসওয়ার্ড টি থুবই ছোট' + mismatch: 'পাসওয়ার্ডটি মেলেনি' + new_password: + blank: 'অনুগ্রহ করে একটি নতুন পাসওয়ার্ড লিখুন' + short: 'নতুন পাসওয়ার্ড টি থুবই ছোট' + current_password: + invalid: 'পাসওয়ার্ডটি সঠিক নয়' diff --git a/src/Resources/translations/validators.bn_BD.yml b/src/Resources/translations/validators.bn_BD.yml new file mode 100644 index 0000000000..d6c3f477da --- /dev/null +++ b/src/Resources/translations/validators.bn_BD.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'ব্যবহারকারীর নামটি ইতিমধ্যে ব্যবহার করা হয়েছে' + blank: 'অনুগ্রহ করে ব্যবহারকারীর নাম লিখুন' + short: 'নামটি থুবই ছোট' + long: 'নামটি থুবই বড়' + email: + already_used: 'ই-মেইল টি ইতিমধ্যে ব্যবহার করা হয়েছে' + blank: 'অনুগ্রহ করে একটি ই-মেইল লিখুন' + short: 'ই-মেইল টি থুবই ছোট' + long: 'ই-মেইল টি থুবই বড়' + invalid: 'ই-মেইল টি সঠিক নয়' + password: + blank: 'অনুগ্রহ করে পাসওয়ার্ড লিখুন' + short: 'পাসওয়ার্ড টি থুবই ছোট' + mismatch: 'পাসওয়ার্ডটি মেলেনি' + new_password: + blank: 'অনুগ্রহ করে একটি নতুন পাসওয়ার্ড লিখুন' + short: 'নতুন পাসওয়ার্ড টি থুবই ছোট' + current_password: + invalid: 'পাসওয়ার্ডটি সঠিক নয়' diff --git a/src/Resources/translations/validators.ca.yml b/src/Resources/translations/validators.ca.yml new file mode 100644 index 0000000000..19d4bc8d97 --- /dev/null +++ b/src/Resources/translations/validators.ca.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'El nom d''usuari ja està sent utilitzat' + blank: 'Si us plau, introdueixi un nom d''usuari' + short: 'El nom d''usuari és massa curt' + long: 'El nom d''usuari és massa llarg' + email: + already_used: 'L''adreça de correu ja està sent utilitzada' + blank: 'Si us plau, introdueixi una adreça de correu' + short: 'L''adreça de correu és massa curta' + long: 'L''adreça de correu és massa llarga' + invalid: 'L''adreça de correu no és vàlida' + password: + blank: 'Si us plau, introdueixi una contrasenya' + short: 'La contrasenya és massa curta' + mismatch: 'Les dues contrasenyes no coincideixen' + new_password: + blank: 'Si us plau, introdueixi una nova contrasenya' + short: 'La nova contrasenya és massa curta' + current_password: + invalid: 'La contrasenya no és vàlida' diff --git a/src/Resources/translations/validators.cs.yml b/src/Resources/translations/validators.cs.yml new file mode 100644 index 0000000000..7bf2c89e73 --- /dev/null +++ b/src/Resources/translations/validators.cs.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Toto uživatelské jméno je již zaregistrované.' + blank: 'Vyplňte uživatelské jméno.' + short: 'Toto uživatelské jméno je příliš krátké.' + long: 'Toto uživatelské jméno je příliš dlouhé.' + email: + already_used: 'Tento e-mail je již zaregistrovaný.' + blank: 'Vyplňte e-mail.' + short: 'E-mail je příliš krátký.' + long: 'E-mail je příliš dlouhý.' + invalid: 'Neplatný e-mail.' + password: + blank: 'Vyplňte heslo.' + short: 'Heslo je příliš krátké.' + mismatch: 'Hesla nejsou stejná.' + new_password: + blank: 'Vyplňte nové heslo.' + short: 'Nové heslo je příliš krátké.' + current_password: + invalid: 'Nesprávné heslo.' diff --git a/src/Resources/translations/validators.da.yml b/src/Resources/translations/validators.da.yml new file mode 100644 index 0000000000..9c6b6ed278 --- /dev/null +++ b/src/Resources/translations/validators.da.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Brugernavnet er allerede i brug.' + blank: 'Indtast venligst et brugernavn.' + short: 'Brugernavnet er for kort.' + long: 'Brugernavnet er for langt.' + email: + already_used: 'E-mailadressen er allerede i brug.' + blank: 'Indtast venligst en e-mailadresse.' + short: 'E-mailadressen er for kort.' + long: 'E-mailadressen er for lang.' + invalid: 'E-mailadressen er ikke gyldig.' + password: + blank: 'Indtast venligst en adgangskode.' + short: 'Adgangskoden er for kort.' + mismatch: 'De valgte adgangskoder matcher ikke.' + new_password: + blank: 'Indtast venligst en ny adgangskode.' + short: 'Den nye adgangskode er for kort.' + current_password: + invalid: 'Den indtastede adgangskode er forkert.' diff --git a/src/Resources/translations/validators.de.yml b/src/Resources/translations/validators.de.yml new file mode 100644 index 0000000000..8375cf5074 --- /dev/null +++ b/src/Resources/translations/validators.de.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Dieser Benutzername wird bereits verwendet.' + blank: 'Bitte geben Sie einen Benutzernamen an.' + short: 'Dieser Benutzername ist zu kurz.' + long: 'Dieser Benutzername ist zu lang.' + email: + already_used: 'Diese E-Mail-Adresse wird bereits verwendet.' + blank: 'Bitte geben Sie eine E-Mail-Adresse an.' + short: 'Diese E-Mail-Adresse ist zu kurz.' + long: 'Diese E-Mail-Adresse ist zu lang.' + invalid: 'Diese E-Mail-Adresse ist ungültig.' + password: + blank: 'Bitte geben Sie ein Passwort an.' + short: 'Das Passwort ist zu kurz.' + mismatch: 'Die Passwörter stimmen nicht überein.' + new_password: + blank: 'Bitte geben Sie ein neues Passwort an.' + short: 'Das neue Passwort ist zu kurz.' + current_password: + invalid: 'Das angegebene Passwort ist ungültig.' diff --git a/src/Resources/translations/validators.el.yml b/src/Resources/translations/validators.el.yml new file mode 100644 index 0000000000..4a3f9f58a9 --- /dev/null +++ b/src/Resources/translations/validators.el.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Το όνομα χρήστη χρησιμοποιείται ήδη' + blank: 'Παρακαλώ εισάγετε ένα όνομα χρήστη' + short: 'Το όνομα χρήστη είναι πολύ μικρό ' + long: 'Το όνομα χρήστη είναι πολύ μεγάλο' + email: + already_used: 'Το email χρησιμοποιείται ήδη' + blank: 'Παρακαλώ εισάγετε ένα e-mail' + short: 'Το email ίναι πολύ μικρό' + long: 'Το email είναι πολύ μεγάλο' + invalid: 'Το email δεν είναι έγκυρο' + password: + blank: 'Παρακαλώ εισάγετε έναν κωδικό πρόσβασης' + short: 'Ο κωδικός πρόσβασης είναι πολύ μικρός' + mismatch: 'Οι κωδικοί πρόσβασης δεν ταιριάζουν' + new_password: + blank: 'Παρακαλώ εισάγετε ένα νέο κωδικό πρόσβασης' + short: 'Ο νέος κωδικός πρόσβασης είναι πολύ μικρός' + current_password: + invalid: 'Ο κωδικός δεν είναι έγκυρος' diff --git a/src/Resources/translations/validators.en.yml b/src/Resources/translations/validators.en.yml new file mode 100644 index 0000000000..24be7be476 --- /dev/null +++ b/src/Resources/translations/validators.en.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'The username is already used.' + blank: 'Please enter a username.' + short: 'The username is too short.' + long: 'The username is too long.' + email: + already_used: 'The email is already used.' + blank: 'Please enter an email.' + short: 'The email is too short.' + long: 'The email is too long.' + invalid: 'The email is not valid.' + password: + blank: 'Please enter a password.' + short: 'The password is too short.' + mismatch: 'The entered passwords don''t match.' + new_password: + blank: 'Please enter a new password.' + short: 'The new password is too short.' + current_password: + invalid: 'The entered password is invalid.' diff --git a/src/Resources/translations/validators.eo.yml b/src/Resources/translations/validators.eo.yml new file mode 100644 index 0000000000..6726b8a281 --- /dev/null +++ b/src/Resources/translations/validators.eo.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Tiu ĉi uzantnomo estas jam okupita.' + blank: 'Enmetu vian uzantnomo.' + short: 'La uzantnomo estas tro mallonga.' + long: 'La uzantnomo estas tro longa.' + email: + already_used: 'Tiu ĉi retpoŝtadreso jam estas registrita.' + blank: 'Enmetu validan retpoŝtan adreson.' + short: 'La retpoŝtadreso estas tro mallonga.' + long: 'La retpoŝtadreso estas tro longa.' + invalid: 'La retpoŝtadreso ne estas valida.' + password: + blank: 'Enmetu pasvorton.' + short: 'La pasvorto estas tro mallonga.' + mismatch: 'La pasvortoj ne samas.' + new_password: + blank: 'Bonvolu enmeti novan pasvorton.' + short: 'La nova pasvorto estas tro mallonga.' + current_password: + invalid: 'La pasvorto estas nevalida.' diff --git a/src/Resources/translations/validators.es.yml b/src/Resources/translations/validators.es.yml new file mode 100644 index 0000000000..3b625eda3e --- /dev/null +++ b/src/Resources/translations/validators.es.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'El nombre de usuario ya está en uso.' + blank: 'Por favor, ingrese un nombre de usuario.' + short: 'El nombre de usuario es demasiado corto.' + long: 'El nombre de usuario es demasiado largo.' + email: + already_used: 'La dirección de correo ya está en uso.' + blank: 'Por favor, ingrese una dirección de correo.' + short: 'La dirección de correo es demasiado corta.' + long: 'La dirección de correo es demasiado larga.' + invalid: 'La dirección de correo no es válida.' + password: + blank: 'Por favor, ingrese una contraseña.' + short: 'La contraseña es demasiado corta.' + mismatch: 'Las dos contraseñas no coinciden.' + new_password: + blank: 'Por favor, ingrese una nueva contraseña.' + short: 'La nueva contraseña es demasiado corta.' + current_password: + invalid: 'La contraseña ingresada no es válida.' diff --git a/src/Resources/translations/validators.eu.yml b/src/Resources/translations/validators.eu.yml new file mode 100644 index 0000000000..57780c474e --- /dev/null +++ b/src/Resources/translations/validators.eu.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Erabiltzaile izena hori erabiltzen da dagoeneko.' + blank: 'Sartu erabiltzaile izena mesedez.' + short: 'Erabiltzaile izena motzegia da.' + long: 'Erabiltzaile izena luzeegia da.' + email: + already_used: 'E-posta helbide hori erabiltzen da dagoeneko.' + blank: 'Sarty E-posta helbidea mesedez.' + short: 'E-posta helbidea motzegia da.' + long: 'E-posta helbidea luzeegia da.' + invalid: 'E-posta helbidea ez da zuzena.' + password: + blank: 'Sartu pasahitza mesedez.' + short: 'Pasahitza motzegia da.' + mismatch: 'Bi pasahitzek ez dute bat egiten.' + new_password: + blank: 'Sartu pasahitz berria mesedez.' + short: 'Pasahitz berria motzegia da.' + current_password: + invalid: 'Sartutako pasahitza ez da baliagarria.' diff --git a/src/Resources/translations/validators.fa.yml b/src/Resources/translations/validators.fa.yml new file mode 100644 index 0000000000..bb4082b0d0 --- /dev/null +++ b/src/Resources/translations/validators.fa.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'نام کاربری قبلا ثبت شده است' + blank: 'لطفا یک نام کاربری وارد کنید' + short: 'نام کاربری کوتاه‌تر از حد مجاز است' + long: 'نام کاربری بلندتر از حد مجاز است' + email: + already_used: 'ایمیل قبلا ثبت شده است' + blank: 'لطفا یک ایمیل وارد کنید' + short: 'ایمیل کوتاه‌تر از حد مجاز است' + long: 'ایمیل بلندتر از حد مجاز است' + invalid: 'ایمیل وارد شده معتبر نیست' + password: + blank: 'لطفا یک کلمه عبور وارد کنید' + short: 'کلمه عبور کوتاه‌تر از حد مجاز است' + mismatch: 'کلمه عبور وارد شده با تکرارش متفاوت است' + new_password: + blank: 'لطفا یک کلمه عبور جدید وارد کنید' + short: '‌کلمه عبور جدید کوتاه‌تر از حد مجاز است' + current_password: + invalid: 'کلمه عبور فعلی معتبر نیست' diff --git a/src/Resources/translations/validators.fi.yml b/src/Resources/translations/validators.fi.yml new file mode 100644 index 0000000000..5df5e218c4 --- /dev/null +++ b/src/Resources/translations/validators.fi.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Käyttäjätunnus on jo käytössä.' + blank: 'Syötä käyttäjätunnus.' + short: 'Käyttäjätunnus on liian lyhyt.' + long: 'Käyttäjätunnus on liian pitkä.' + email: + already_used: 'Sähköpostiosoite on jo käytössä.' + blank: 'Syötä sähköpostiosoite.' + short: 'Sähköpostiosoite on liian lyhyt.' + long: 'Sähköpostiosoite on liian pitkä.' + invalid: 'Sähköpostiosoite ei ole kelvollinen.' + password: + blank: 'Syötä salasana.' + short: 'Salasana on liian lyhyt.' + mismatch: 'Salasanat eivät täsmää.' + new_password: + blank: 'Syötä uusi salasana.' + short: 'Uusi salasana on liian lyhyt.' + current_password: + invalid: 'Annettu salasana ei ole kelvollinen.' diff --git a/src/Resources/translations/validators.fr.yml b/src/Resources/translations/validators.fr.yml new file mode 100644 index 0000000000..b4d39836a0 --- /dev/null +++ b/src/Resources/translations/validators.fr.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Le nom d''utilisateur est déjà utilisé.' + blank: 'Entrez un nom d''utilisateur s''il vous plait.' + short: 'Le nom d''utilisateur est trop court.' + long: 'Le nom d''utilisateur est trop long.' + email: + already_used: 'L''adresse e-mail est déjà utilisée.' + blank: 'Entrez une adresse e-mail s''il vous plait.' + short: 'L''adresse e-mail est trop courte.' + long: 'L''adresse e-mail est trop longue.' + invalid: 'L''adresse e-mail est invalide.' + password: + blank: 'Entrez un mot de passe s''il vous plait.' + short: 'Le mot de passe est trop court.' + mismatch: 'Les deux mots de passe ne sont pas identiques.' + new_password: + blank: 'Entrez un nouveau mot de passe s''il vous plait.' + short: 'Le nouveau mot de passe est trop court.' + current_password: + invalid: 'Le mot de passe est invalide.' diff --git a/src/Resources/translations/validators.he.yml b/src/Resources/translations/validators.he.yml new file mode 100644 index 0000000000..f2560dd2f8 --- /dev/null +++ b/src/Resources/translations/validators.he.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'שם המשתמש טפוס ע"ל משתמש אחר' + blank: 'יש להזין שם משתמש' + short: 'שם המשתמש קצר מדי' + long: 'שם המשתמש ארוך מדי' + email: + already_used: 'כתובת דואר אלקטרוני תפוסה' + blank: 'יש להזין כתובת דואר אלקטרוני' + short: 'כתובת הדוא"ל קצרה מדי' + long: 'כתובת הדוא"ל ארוכה מדי' + invalid: 'הדוא\"ל אינו תקין' + password: + blank: 'יש להזין סיסמה' + short: 'הסיסמה קצרה מדי' + mismatch: 'אין התאמה בין סיסמאות' + new_password: + blank: 'יש להזין סיסמה חדשה' + short: 'סיסמה החדשה קצרה מדי' + current_password: + invalid: 'הסיסמה שגויה' diff --git a/src/Resources/translations/validators.hr.yml b/src/Resources/translations/validators.hr.yml new file mode 100644 index 0000000000..3ea5a0e3e6 --- /dev/null +++ b/src/Resources/translations/validators.hr.yml @@ -0,0 +1,20 @@ +fos_user: + username: + already_used: 'Korisničko ime već postoji.' + blank: 'Molimo, unesite korisničko ime.' + short: 'Korisničko ime je prekratko.' + long: 'Korisničko ime je predugo.' + email: + already_used: 'Email već postoji.' + blank: 'Molimo, unesite email.' + short: 'Email je prekratak.' + long: 'Email je predug.' + invalid: 'Email nije ispravan.' + password: + blank: 'Molimo, unesite lozinku.' + short: 'Lozinka je prekratka.' + new_password: + blank: 'Molimo, unesite novu lozinku.' + short: 'Nova lozinka je prekratka.' + current_password: + invalid: 'Lozinka nije ispravna.' diff --git a/src/Resources/translations/validators.hu.yml b/src/Resources/translations/validators.hu.yml new file mode 100644 index 0000000000..9727ef1381 --- /dev/null +++ b/src/Resources/translations/validators.hu.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'A felhasználónév már foglalt.' + blank: 'A felhasználónév nem lehet üres.' + short: 'A felhasználónév túl rövid.' + long: 'A felhasználónév túl hosszú.' + email: + already_used: 'Az e-mail cím már használatban van.' + blank: 'Az e-mail cím nem lehet üres.' + short: 'Az e-mail cím túl rövid.' + long: 'Az e-mail cím túl hosszú.' + invalid: 'A megadott e-mail cím helytelen.' + password: + blank: 'A jelszó nem lehet üres.' + short: 'A jelszó túl rövid.' + mismatch: 'A megadott jelszavak nem egyeznek.' + new_password: + blank: 'Az új jelszó nem lehet üres.' + short: 'Az új jelszó túl rövid.' + current_password: + invalid: 'A megadott jelszó helytelen.' diff --git a/src/Resources/translations/validators.id.yml b/src/Resources/translations/validators.id.yml new file mode 100644 index 0000000000..c943bcd72d --- /dev/null +++ b/src/Resources/translations/validators.id.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Nama pengguna telah digunakan.' + blank: 'Masukkan nama pengguna.' + short: 'Nama pengguna terlalu pendek.' + long: 'Nama pengguna terlalu panjang.' + email: + already_used: 'Surel telah digunakan.' + blank: 'Masukkan alamat surel.' + short: 'Alamat surel terlalu pendek.' + long: 'Alamat surel terlalu panjang.' + invalid: 'Alamat surel salah.' + password: + blank: 'Masukkan kata sandi.' + short: 'Kata sandi terlalu pendek.' + mismatch: 'kata sandi yang anda masukkan tidak sama.' + new_password: + blank: 'Masukkan kata sandi baru.' + short: 'Kata sandi baru anda terlalu pendek.' + current_password: + invalid: 'Kata sandi yang anda masukkan salah.' diff --git a/src/Resources/translations/validators.it.yml b/src/Resources/translations/validators.it.yml new file mode 100644 index 0000000000..84e97afdf2 --- /dev/null +++ b/src/Resources/translations/validators.it.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Username già in uso.' + blank: 'Si prega di inserire un username.' + short: 'Username troppo breve.' + long: 'Username troppo lungo.' + email: + already_used: 'Email già in uso.' + blank: 'Si prega di inserire una email.' + short: 'Email troppo breve.' + long: 'Email troppo lunga.' + invalid: 'Email non valida.' + password: + blank: 'Si prega di inserire una password.' + short: 'Password troppo breve.' + mismatch: 'Le due password non coincidono.' + new_password: + blank: 'Si prega di inserire nuovamente la password.' + short: 'Nuova password troppo breve.' + current_password: + invalid: 'Password non valida.' diff --git a/src/Resources/translations/validators.ja.yml b/src/Resources/translations/validators.ja.yml new file mode 100644 index 0000000000..8e42d9be21 --- /dev/null +++ b/src/Resources/translations/validators.ja.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: ユーザー名は既に使用されています + blank: ユーザー名を入力してください + short: ユーザー名が短すぎます + long: ユーザー名が長すぎます + email: + already_used: メールアドレスは既に使用されています + blank: メールアドレスを入力してください + short: メールアドレスが短すぎます + long: メールアドレスが長すぎます + invalid: メールアドレスが正しくありません + password: + blank: パスワードを入力してください + short: パスワードが短すぎます + mismatch: 入力されたパスワードが一致しません + new_password: + blank: 新しいパスワードを入力してください + short: パスワードが短すぎます + current_password: + invalid: 正しいパスワードを入力してください diff --git a/src/Resources/translations/validators.ky.yml b/src/Resources/translations/validators.ky.yml new file mode 100644 index 0000000000..94772780de --- /dev/null +++ b/src/Resources/translations/validators.ky.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Логин колдонулууда' + blank: 'Логиниңизди жазыңыз' + short: 'Логиниңиз биз күткөндөн кыска' + long: 'Логиниңиз биз күткөндөн узун' + email: + already_used: 'Email колдонулууда' + blank: 'Электрондук почтаңызды жазыңыз' + short: 'Email биз күткөндөн кыска' + long: 'Email биз күткөндөн узун' + invalid: 'Email туура эмес форматта' + password: + blank: 'Сырдык сөзүңүздү жазыңыз' + short: 'Сырдык сөзүңүз биз күткөндөн кыска' + mismatch: 'Сырдык сөздөр бири-бирине туура келбейт' + new_password: + blank: 'Жаңы сырдык сөз жазыңыз' + short: 'Жаңы сырдык сөз биз күткөндөн кыска' + current_password: + invalid: 'Сырдык сөз туура эмес жазылды' diff --git a/src/Resources/translations/validators.lb.yml b/src/Resources/translations/validators.lb.yml new file mode 100644 index 0000000000..c830419c9d --- /dev/null +++ b/src/Resources/translations/validators.lb.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Dëse Benotzernumm ass schonn am Gebrauch.' + blank: 'Wann ech gelift e Benotzernumm uginn.' + short: 'Dëse Benotzernumm ass ze kuerz.' + long: 'Dëse Benotzernumm ass ze laang.' + email: + already_used: 'Dës E-Mail-Adress ass schonn am Gebrauch.' + blank: 'Wann ech gelift eng E-Mail-Adress uginn.' + short: 'Dës E-Mail-Adress ass ze kuerz.' + long: 'Dës E-Mail-Adress ass ze laang.' + invalid: 'Dës E-Mail-Adress ass ongëlteg.' + password: + blank: 'Wann ech gelift e Passwuert uginn.' + short: 'Dëst Passwuert ass ze kuerz.' + mismatch: 'D''Passwierder stëmmen net iwwerteneen.' + new_password: + blank: 'Wann ech gelift en neit Passwuert aginn.' + short: 'Dat neit Passwuert ass ze kuerz.' + current_password: + invalid: 'D''Passwuert ass net korrekt.' diff --git a/src/Resources/translations/validators.lt.yml b/src/Resources/translations/validators.lt.yml new file mode 100644 index 0000000000..1916b161c0 --- /dev/null +++ b/src/Resources/translations/validators.lt.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Naudotojo vardas jau užimtas.' + blank: 'Prašome įvesti naudotojo vardą.' + short: 'Naudotojo vardas per trumpas.' + long: 'Naudotojo vardas per ilgas.' + email: + already_used: 'El. paštas jau užimtas.' + blank: 'Prašome įvesti el. paštą.' + short: 'El. paštas pert rumpas.' + long: 'El. paštas per ilgas.' + invalid: 'El. paštas neteisingas.' + password: + blank: 'Prašome įvesti slaptažodį.' + short: 'Slaptažodis per trumpas.' + mismatch: 'Įvesti skirtingi slaptažodžiai.' + new_password: + blank: 'Prašome įvesti naują slaptažodį.' + short: 'Naujas slaptažodis per trumpas.' + current_password: + invalid: 'Neteisingai įvestas slaptažodis.' diff --git a/src/Resources/translations/validators.lv.yml b/src/Resources/translations/validators.lv.yml new file mode 100644 index 0000000000..78b3d29b81 --- /dev/null +++ b/src/Resources/translations/validators.lv.yml @@ -0,0 +1,20 @@ +fos_user: + username: + already_used: 'Šis lietotājvārds jau ir aizņemts.' + blank: 'Lūdzu ievadiet lietotājvārdu.' + short: 'Lietotājvārds ir pārāk īss.' + long: 'Lietotājvārds ir pārāk garš.' + email: + already_used: 'Šis e-pasts jau ir aizņemts.' + blank: 'Lūdzu ievadiet e-pastu.' + short: 'E-pasts ir pārāk īss.' + long: 'E-pasts ir pārāk garš.' + invalid: 'E-pasts nav derīgs.' + password: + blank: 'Lūdzu ievadiet paroli.' + short: 'Parole ir pārāk īsa.' + new_password: + blank: 'Lūdzu ievadiet jaunu paroli.' + short: 'Jaunā parole ir pārāk īsa.' + current_password: + invalid: 'Ievadītā parole nav pareiza.' diff --git a/src/Resources/translations/validators.nb.yml b/src/Resources/translations/validators.nb.yml new file mode 100644 index 0000000000..05b65855b5 --- /dev/null +++ b/src/Resources/translations/validators.nb.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Brukernavnet er allerede i bruk' + blank: 'Vennligst skriv inn et brukernavn' + short: 'Brukernavnet er for kort' + long: 'Brukernavnet er for langt' + email: + already_used: 'Epostadressen er allerede i bruk' + blank: 'Vennligst skriv inn en epostadresse' + short: 'Epostadressen er for kort' + long: 'Epostadressen er for lang' + invalid: 'Epostadressen er ikke gyldig' + password: + blank: 'Vennligst skriv inn et passord' + short: 'Passordet er for kort' + mismatch: 'De innskrevne passordene stemmer ikke overens' + new_password: + blank: 'Vennligst skriv inn et nytt passord' + short: 'Det nye passordet er for kort' + current_password: + invalid: 'Det innskrevne passordet er ugyldig' diff --git a/src/Resources/translations/validators.nl.yml b/src/Resources/translations/validators.nl.yml new file mode 100644 index 0000000000..4095c6b800 --- /dev/null +++ b/src/Resources/translations/validators.nl.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'De gebruikersnaam is al in gebruik.' + blank: 'Voer een gebruikersnaam in.' + short: 'De gebruikersnaam is te kort.' + long: 'De gebruikersnaam is te lang.' + email: + already_used: 'Het e-mailadres is al in gebruik.' + blank: 'Voer een e-mailadres in.' + short: 'Het e-mailadres is te kort.' + long: 'Het e-mailadres is te lang.' + invalid: 'Het e-mailadres is ongeldig.' + password: + blank: 'Voer een wachtwoord in.' + short: 'Het wachtwoord is te kort.' + mismatch: 'Het ingevoerde wachtwoord komt niet overeen.' + new_password: + blank: 'Voer een nieuw wachtwoord in.' + short: 'Het nieuwe wachtwoord is te kort.' + current_password: + invalid: 'Het ingevulde wachtwoord is ongeldig.' diff --git a/src/Resources/translations/validators.pl.yml b/src/Resources/translations/validators.pl.yml new file mode 100644 index 0000000000..ca83a854d4 --- /dev/null +++ b/src/Resources/translations/validators.pl.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Taki użytkownik już istnieje. Podaj inną nazwę.' + blank: 'Podaj nazwę użytkownika' + short: 'Podaj dłuższą nazwę użytkownika' + long: 'Podaj krótszą nazwę użytkownika' + email: + already_used: 'Taki użytkownik już istnieje. Podaj inny email.' + blank: 'Podaj email' + short: 'Podaj dłuższy email' + long: 'Podaj krótszy email' + invalid: 'Podaj prawidłowy email' + password: + blank: 'Podaj hasło' + short: 'Podaj dłuższe hasło' + mismatch: 'Hasła nie pasują do siebie. Podaj dwa razy to samo hasło.' + new_password: + blank: 'Podaj nowe hasło' + short: 'Podaj dłuższe hasło' + current_password: + invalid: 'Podaj prawidłowe hasło' diff --git a/src/Resources/translations/validators.pt.yml b/src/Resources/translations/validators.pt.yml new file mode 100644 index 0000000000..83db170b02 --- /dev/null +++ b/src/Resources/translations/validators.pt.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Este utilizador já existe.' + blank: 'Por favor introduza o nome de utilizador.' + short: 'O nome de utilizador é muito curto.' + long: 'O nome de utilizador é muito longo.' + email: + already_used: 'Este email já está a ser usado.' + blank: 'Por favor introduza o email.' + short: 'Este email é muito curto.' + long: 'Este email é muito longo.' + invalid: 'Este email é inválido.' + password: + blank: 'Por favor introduza a senha.' + short: 'Esta senha é muito curta.' + mismatch: 'As senhas não correspondem.' + new_password: + blank: 'Por favor introduza a nova senha.' + short: 'A nova senha é muito curta.' + current_password: + invalid: 'A senha está incorreta.' diff --git a/src/Resources/translations/validators.pt_BR.yml b/src/Resources/translations/validators.pt_BR.yml new file mode 100644 index 0000000000..bc5a97dd1f --- /dev/null +++ b/src/Resources/translations/validators.pt_BR.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Este nome de usuário já está sendo usado.' + blank: 'É necessário preencher o nome de usuário.' + short: 'Este de usuário é muito curto.' + long: 'Este de usuário é muito longo.' + email: + already_used: 'Este email já está sendo usado.' + blank: 'É necessário preencher o email.' + short: 'Este email é muito curto.' + long: 'Este email é muito longo.' + invalid: 'Este email é inválido.' + password: + blank: 'É necessário preencher sua senha.' + short: 'Esta senha é muito curta.' + mismatch: 'As senhas não correspondem.' + new_password: + blank: 'É necessário preencher a nova senha.' + short: 'A nova senha é muito curta.' + current_password: + invalid: 'A senha está incorreta.' diff --git a/src/Resources/translations/validators.ro.yml b/src/Resources/translations/validators.ro.yml new file mode 100644 index 0000000000..5ef0934d79 --- /dev/null +++ b/src/Resources/translations/validators.ro.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Numele de utilizator este deja folosit.' + blank: 'Introduceți un nume de utilizator.' + short: 'Numele de utilizator este prea scurt.' + long: 'Numele de utilizator este prea lung.' + email: + already_used: 'E-mailul este deja folosit.' + blank: 'Introduceți e-mailul.' + short: 'E-mailul este prea scurt.' + long: 'E-mailul este prea lung.' + invalid: 'E-mailul nu este corect.' + password: + blank: 'Introduceți parola.' + short: 'Parola este prea scurtă.' + mismatch: 'Parolele introduse nu coincid.' + new_password: + blank: 'Introduceți o parolă nouă.' + short: 'Parola nouă este prea scurtă.' + current_password: + invalid: 'Parola introdusă nu este corectă.' diff --git a/src/Resources/translations/validators.ru.yml b/src/Resources/translations/validators.ru.yml new file mode 100644 index 0000000000..bef6876680 --- /dev/null +++ b/src/Resources/translations/validators.ru.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Логин уже используется.' + blank: 'Пожалуйста, укажите логин.' + short: 'Логин слишком короткий.' + long: 'Логин слишком длинный.' + email: + already_used: 'Email уже используется.' + blank: 'Пожалуйста, укажите Ваш email.' + short: 'Email слишком короткий.' + long: 'Email слишком длинный.' + invalid: 'Email в неправильном формате.' + password: + blank: 'Пожалуйста, укажите пароль.' + short: 'Пароль слишком короткий.' + mismatch: 'Введенные пароли не совпадают.' + new_password: + blank: 'Пожалуйста, укажите новый пароль.' + short: 'Новый пароль слишком короткий.' + current_password: + invalid: 'Вы неправильно ввели Ваш текущий пароль.' diff --git a/src/Resources/translations/validators.sk.yml b/src/Resources/translations/validators.sk.yml new file mode 100644 index 0000000000..1e75a267f6 --- /dev/null +++ b/src/Resources/translations/validators.sk.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Používateľské meno už existuje.' + blank: 'Zadajte, prosím, svoje používateľské meno.' + short: 'Používateľské meno je príliš krátke.' + long: 'Používateľské meno je príliš dlhé.' + email: + already_used: 'E-mail už existuje.' + blank: 'Zadajte prosím e-mail.' + short: 'E-mail je príliš krátky.' + long: 'E-mail je príliš dlhý.' + invalid: 'E-mail nie je platný.' + password: + blank: 'Zadajte, prosím, heslo.' + short: 'Heslo je príliš krátke.' + mismatch: 'Zadané heslá sa nezhodujú.' + new_password: + blank: 'Zadajte, prosím, nové heslo.' + short: 'Nové heslo je príliš krátke.' + current_password: + invalid: 'Zadané heslo je neplatné.' diff --git a/src/Resources/translations/validators.sl.yml b/src/Resources/translations/validators.sl.yml new file mode 100644 index 0000000000..3ffb21fd2c --- /dev/null +++ b/src/Resources/translations/validators.sl.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Uporabniško ime že obstaja.' + blank: 'Prosimo, vnesite uporabniško ime.' + short: 'Uporabniško ime je prekratko.' + long: 'Uporabniško ime je predolgo.' + email: + already_used: 'Email že obstaja.' + blank: 'Prosimo, vnesite email.' + short: 'Email je prekratek.' + long: 'Email je predolg.' + invalid: 'Email je napačen.' + password: + blank: 'Prosimo, vnesite geslo.' + short: 'Geslo je prekratko.' + mismatch: 'Vpisani gesli se ne ujemata.' + new_password: + blank: 'Prosimo, vnesite novo geslo.' + short: 'Novo geslo je prekratko.' + current_password: + invalid: 'Geslo je napačno.' diff --git a/src/Resources/translations/validators.sr_Latn.yml b/src/Resources/translations/validators.sr_Latn.yml new file mode 100644 index 0000000000..1834f422cb --- /dev/null +++ b/src/Resources/translations/validators.sr_Latn.yml @@ -0,0 +1,20 @@ +fos_user: + username: + already_used: 'Korisničko ime već postoji.' + blank: 'Molimo, unesite korisničko ime.' + short: 'Korisničko ime je prekratko.' + long: 'Korisničko ime je predugo.' + email: + already_used: 'Adresa e-pošte već postoji.' + blank: 'Molimo, unesite adresu e-pošte.' + short: 'Adresa e-pošte je prekratka.' + long: 'Adresa e-pošte je preduga.' + invalid: 'Adresa e-pošte nije validna.' + password: + blank: 'Molimo, unesite lozinku.' + short: 'Lozinka je prekratka.' + new_password: + blank: 'Molimo, unesite novu lozinku.' + short: 'Nova lozinka je prekratka.' + current_password: + invalid: 'Lozinka nije validna.' diff --git a/src/Resources/translations/validators.sv.yml b/src/Resources/translations/validators.sv.yml new file mode 100644 index 0000000000..3f2385298e --- /dev/null +++ b/src/Resources/translations/validators.sv.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Användarnamnet är upptaget.' + blank: 'Ange ett användarnamn.' + short: 'Användarnamnet är för kort.' + long: 'Användarnamnet är för långt.' + email: + already_used: 'E-postadressen är upptagen.' + blank: 'Ange en e-postadress.' + short: 'E-postadressen är för kort.' + long: 'E-postadressen är för lång.' + invalid: 'E-postadressen är ogiltig.' + password: + blank: 'Ange ett lösenord.' + short: 'Lösenordet är för kort.' + mismatch: 'De angivna lösenorden matchar inte.' + new_password: + blank: 'Ange ett nytt lösenord.' + short: 'Det nya lösenordet är för kort.' + current_password: + invalid: 'Det nuvarande lösenordet är felaktigt.' diff --git a/src/Resources/translations/validators.th.yml b/src/Resources/translations/validators.th.yml new file mode 100644 index 0000000000..ed17427846 --- /dev/null +++ b/src/Resources/translations/validators.th.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: ชื่อผู้ใช้นี้ถูกใช้ไปแล้ว + blank: กรุณาระบุชื่อผู้ใช้ + short: ชื่อผู้ใช้สั้นเกินไป + long: ชื่อผู้ใช้ยาวเกินไป + email: + already_used: อีเมล์นี้มีอยู่ในระบบแล้ว + blank: กรุณาระบุอีเมล์ + short: ที่อยู่อีเมล์สั้นเกินไป + long: ที่อยู่อีเมล์ยาวเกินไป + invalid: ที่อยู่อีเมล์ไม่ถูกต้อง + password: + blank: กรุณาระบุรหัสผ่าน + short: รหัสผ่านสั้นเกินไป + mismatch: รหัสผ่านที่ระบุไม่ตรงกัน + new_password: + blank: กรุณาระบุระหัสผ่านใหม่ + short: รหัสผ่านใหม่สั้นเกินไป + current_password: + invalid: รหัสผ่านปัจจุบันไม่ถูกต้อง diff --git a/src/Resources/translations/validators.tr.yml b/src/Resources/translations/validators.tr.yml new file mode 100644 index 0000000000..3a427f846e --- /dev/null +++ b/src/Resources/translations/validators.tr.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Kullanıcı adı zaten kullanılıyor.' + blank: 'Lütfen bir kullanıcı adı girin.' + short: 'Kullanıcı adı çok kısa.' + long: 'Kullanıcı adı çok uzun.' + email: + already_used: 'E-posta zaten kullanılıyor.' + blank: 'Lütfen bir e-posta girin.' + short: 'E-posta çok kısa.' + long: 'E-posta çok uzun.' + invalid: 'E-posta geçerli değil.' + password: + blank: 'Lütfen bir parola girin.' + short: 'Parola çok kısa.' + mismatch: 'Şifreler uyuşmuyor!.' + new_password: + blank: 'Lütfen yeni parolayı girin.' + short: 'Yeni parola çok kısa.' + current_password: + invalid: 'Girilen parola geçersiz.' diff --git a/src/Resources/translations/validators.uk.yml b/src/Resources/translations/validators.uk.yml new file mode 100644 index 0000000000..5754113b1a --- /dev/null +++ b/src/Resources/translations/validators.uk.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Логін вже використовується.' + blank: 'Будь ласка, вкажіть логін.' + short: 'Логін занадто короткий.' + long: 'Логін занадто довгий.' + email: + already_used: 'Email вже використовується.' + blank: 'Будь ласка, вкажіть Ваш email.' + short: 'Email занадто короткий.' + long: 'Email занадто довгий.' + invalid: 'Email у невірному форматі.' + password: + blank: 'Будь ласка, вкажіть пароль.' + short: 'Пароль занадто короткий.' + mismatch: 'Введені паролі не співпадають.' + new_password: + blank: 'Будь ласка, вкажіть новый пароль.' + short: 'Новий пароль занадто короткий.' + current_password: + invalid: 'Ви невірно ввели Ваш поточний пароль.' diff --git a/src/Resources/translations/validators.vi.yml b/src/Resources/translations/validators.vi.yml new file mode 100644 index 0000000000..6703b7938a --- /dev/null +++ b/src/Resources/translations/validators.vi.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 'Tên người dùng đã được sử dụng' + blank: 'Xin vui lòng nhập tên người dùng' + short: '[-Inf, Inf] Các tên tài khoản quá ngắn' + long: '[-Inf, Inf] Tên người dùng quá dài' + email: + already_used: 'Thư điện tử đã được sử dụng' + blank: 'Vui lòng nhập email' + short: '[-Inf, Inf] Thư điện tử là quá ngắn' + long: '[-Inf, Inf] Email quá dài' + invalid: 'Các email không hợp lệ' + password: + blank: 'Vui lòng nhập mật khẩu' + short: '[-Inf, Inf] Mật khẩu quá ngắn' + mismatch: 'Các mật khẩu nhập không khớp' + new_password: + blank: 'Hãy nhập mật khẩu mới' + short: '[-Inf, Inf] Mật khẩu mới là quá ngắn' + current_password: + invalid: 'Mật khẩu nhập không hợp lệ' diff --git a/src/Resources/translations/validators.zh_CN.yml b/src/Resources/translations/validators.zh_CN.yml new file mode 100644 index 0000000000..e442490ef0 --- /dev/null +++ b/src/Resources/translations/validators.zh_CN.yml @@ -0,0 +1,21 @@ +fos_user: + username: + already_used: 用户名已存在 + blank: 请输入用户名 + short: 用户名字数不够 + long: 用户名字数超出限制 + email: + already_used: 邮箱已被使用 + blank: 请输入邮箱 + short: 邮箱字数不够 + long: 邮箱字数超出限制 + invalid: 邮箱格式不正确 + password: + blank: 请输入密码 + short: 密码字数不够 + mismatch: 两次输入密码不一致 + new_password: + blank: 请输入新密码 + short: 新密码字数不够 + current_password: + invalid: 密码格式不正确 diff --git a/src/Resources/views/ChangePassword/change_password.html.twig b/src/Resources/views/ChangePassword/change_password.html.twig new file mode 100644 index 0000000000..cdcd10d0de --- /dev/null +++ b/src/Resources/views/ChangePassword/change_password.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} +{% include "@FOSUser/ChangePassword/change_password_content.html.twig" %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/ChangePassword/change_password_content.html.twig b/src/Resources/views/ChangePassword/change_password_content.html.twig new file mode 100644 index 0000000000..6c3d0cc1c3 --- /dev/null +++ b/src/Resources/views/ChangePassword/change_password_content.html.twig @@ -0,0 +1,8 @@ +{% trans_default_domain 'FOSUserBundle' %} + +{{ form_start(form, { 'action': path('fos_user_change_password'), 'attr': { 'class': 'fos_user_change_password' } }) }} + {{ form_widget(form) }} +
+ +
+{{ form_end(form) }} diff --git a/src/Resources/views/Profile/edit.html.twig b/src/Resources/views/Profile/edit.html.twig new file mode 100644 index 0000000000..bd7fbdc9af --- /dev/null +++ b/src/Resources/views/Profile/edit.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} +{% include "@FOSUser/Profile/edit_content.html.twig" %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Profile/edit_content.html.twig b/src/Resources/views/Profile/edit_content.html.twig new file mode 100644 index 0000000000..c82743648e --- /dev/null +++ b/src/Resources/views/Profile/edit_content.html.twig @@ -0,0 +1,8 @@ +{% trans_default_domain 'FOSUserBundle' %} + +{{ form_start(form, { 'action': path('fos_user_profile_edit'), 'attr': { 'class': 'fos_user_profile_edit' } }) }} + {{ form_widget(form) }} +
+ +
+{{ form_end(form) }} diff --git a/src/Resources/views/Profile/show.html.twig b/src/Resources/views/Profile/show.html.twig new file mode 100644 index 0000000000..1e4a259138 --- /dev/null +++ b/src/Resources/views/Profile/show.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} +{% include "@FOSUser/Profile/show_content.html.twig" %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Profile/show_content.html.twig b/src/Resources/views/Profile/show_content.html.twig new file mode 100644 index 0000000000..3c819d22bf --- /dev/null +++ b/src/Resources/views/Profile/show_content.html.twig @@ -0,0 +1,6 @@ +{% trans_default_domain 'FOSUserBundle' %} + +
+

{{ 'profile.show.username'|trans }}: {{ user.username }}

+

{{ 'profile.show.email'|trans }}: {{ user.email }}

+
diff --git a/src/Resources/views/Registration/check_email.html.twig b/src/Resources/views/Registration/check_email.html.twig new file mode 100644 index 0000000000..41af21dea9 --- /dev/null +++ b/src/Resources/views/Registration/check_email.html.twig @@ -0,0 +1,7 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% trans_default_domain 'FOSUserBundle' %} + +{% block fos_user_content %} +

{{ 'registration.check_email'|trans({'%email%': user.email}) }}

+{% endblock fos_user_content %} diff --git a/src/Resources/views/Registration/confirmed.html.twig b/src/Resources/views/Registration/confirmed.html.twig new file mode 100644 index 0000000000..4402b4f713 --- /dev/null +++ b/src/Resources/views/Registration/confirmed.html.twig @@ -0,0 +1,10 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% trans_default_domain 'FOSUserBundle' %} + +{% block fos_user_content %} +

{{ 'registration.confirmed'|trans({'%username%': user.username}) }}

+ {% if targetUrl %} +

{{ 'registration.back'|trans }}

+ {% endif %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Registration/email.txt.twig b/src/Resources/views/Registration/email.txt.twig new file mode 100644 index 0000000000..4fd0db3d1d --- /dev/null +++ b/src/Resources/views/Registration/email.txt.twig @@ -0,0 +1,13 @@ +{% trans_default_domain 'FOSUserBundle' %} +{% block subject %} +{%- autoescape false -%} +{{ 'registration.email.subject'|trans({'%username%': user.username}) }} +{%- endautoescape -%} +{% endblock %} + +{% block body_text %} +{% autoescape false %} +{{ 'registration.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}) }} +{% endautoescape %} +{% endblock %} +{% block body_html %}{% endblock %} diff --git a/src/Resources/views/Registration/register.html.twig b/src/Resources/views/Registration/register.html.twig new file mode 100644 index 0000000000..92b687895b --- /dev/null +++ b/src/Resources/views/Registration/register.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} +{% include "@FOSUser/Registration/register_content.html.twig" %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Registration/register_content.html.twig b/src/Resources/views/Registration/register_content.html.twig new file mode 100644 index 0000000000..ecedeb9696 --- /dev/null +++ b/src/Resources/views/Registration/register_content.html.twig @@ -0,0 +1,8 @@ +{% trans_default_domain 'FOSUserBundle' %} + +{{ form_start(form, {'method': 'post', 'action': path('fos_user_registration_register'), 'attr': {'class': 'fos_user_registration_register'}}) }} + {{ form_widget(form) }} +
+ +
+{{ form_end(form) }} diff --git a/src/Resources/views/Resetting/check_email.html.twig b/src/Resources/views/Resetting/check_email.html.twig new file mode 100644 index 0000000000..1b589f726e --- /dev/null +++ b/src/Resources/views/Resetting/check_email.html.twig @@ -0,0 +1,9 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% trans_default_domain 'FOSUserBundle' %} + +{% block fos_user_content %} +

+{{ 'resetting.check_email'|trans({'%tokenLifetime%': tokenLifetime})|nl2br }} +

+{% endblock %} diff --git a/src/Resources/views/Resetting/email.txt.twig b/src/Resources/views/Resetting/email.txt.twig new file mode 100644 index 0000000000..bd52c9e253 --- /dev/null +++ b/src/Resources/views/Resetting/email.txt.twig @@ -0,0 +1,13 @@ +{% trans_default_domain 'FOSUserBundle' %} +{% block subject %} +{%- autoescape false -%} +{{ 'resetting.email.subject'|trans({'%username%': user.username}) }} +{%- endautoescape -%} +{% endblock %} + +{% block body_text %} +{% autoescape false %} +{{ 'resetting.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}) }} +{% endautoescape %} +{% endblock %} +{% block body_html %}{% endblock %} diff --git a/src/Resources/views/Resetting/request.html.twig b/src/Resources/views/Resetting/request.html.twig new file mode 100644 index 0000000000..87e7b41053 --- /dev/null +++ b/src/Resources/views/Resetting/request.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} +{% include "@FOSUser/Resetting/request_content.html.twig" %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Resetting/request_content.html.twig b/src/Resources/views/Resetting/request_content.html.twig new file mode 100644 index 0000000000..3d1211b835 --- /dev/null +++ b/src/Resources/views/Resetting/request_content.html.twig @@ -0,0 +1,11 @@ +{% trans_default_domain 'FOSUserBundle' %} + +
+
+ + +
+
+ +
+
diff --git a/src/Resources/views/Resetting/reset.html.twig b/src/Resources/views/Resetting/reset.html.twig new file mode 100644 index 0000000000..fab676a810 --- /dev/null +++ b/src/Resources/views/Resetting/reset.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} +{% include "@FOSUser/Resetting/reset_content.html.twig" %} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Resetting/reset_content.html.twig b/src/Resources/views/Resetting/reset_content.html.twig new file mode 100644 index 0000000000..d8b0c4d8f3 --- /dev/null +++ b/src/Resources/views/Resetting/reset_content.html.twig @@ -0,0 +1,8 @@ +{% trans_default_domain 'FOSUserBundle' %} + +{{ form_start(form, { 'action': path('fos_user_resetting_reset', {'token': token}), 'attr': { 'class': 'fos_user_resetting_reset' } }) }} + {{ form_widget(form) }} +
+ +
+{{ form_end(form) }} diff --git a/src/Resources/views/Security/login.html.twig b/src/Resources/views/Security/login.html.twig new file mode 100644 index 0000000000..e5651197d9 --- /dev/null +++ b/src/Resources/views/Security/login.html.twig @@ -0,0 +1,5 @@ +{% extends "@FOSUser/layout.html.twig" %} + +{% block fos_user_content %} + {{ include('@FOSUser/Security/login_content.html.twig') }} +{% endblock fos_user_content %} diff --git a/src/Resources/views/Security/login_content.html.twig b/src/Resources/views/Security/login_content.html.twig new file mode 100644 index 0000000000..d1cd740e3b --- /dev/null +++ b/src/Resources/views/Security/login_content.html.twig @@ -0,0 +1,22 @@ +{% trans_default_domain 'FOSUserBundle' %} + +{% if error %} +
{{ error.messageKey|trans(error.messageData, 'security') }}
+{% endif %} + +
+ {% if csrf_token %} + + {% endif %} + + + + + + + + + + + +
diff --git a/src/Resources/views/layout.html.twig b/src/Resources/views/layout.html.twig new file mode 100644 index 0000000000..91e674fad9 --- /dev/null +++ b/src/Resources/views/layout.html.twig @@ -0,0 +1,33 @@ + + + + + + +
+ {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} + {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} | + + {{ 'layout.logout'|trans({}, 'FOSUserBundle') }} + + {% else %} + {{ 'layout.login'|trans({}, 'FOSUserBundle') }} + {% endif %} +
+ + {% if app.request.hasSession and app.request.hasPreviousSession %} + {% for type, messages in app.session.flashbag.all() %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endfor %} + {% endif %} + +
+ {% block fos_user_content %} + {% endblock fos_user_content %} +
+ + diff --git a/src/Security/EmailProvider.php b/src/Security/EmailProvider.php new file mode 100644 index 0000000000..49080b78ab --- /dev/null +++ b/src/Security/EmailProvider.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Security; + +use FOS\UserBundle\Model\UserInterface; + +class EmailProvider extends UserProvider +{ + protected function findUser($username): ?UserInterface + { + return $this->userManager->findUserByEmail($username); + } +} diff --git a/src/Security/EmailUserProvider.php b/src/Security/EmailUserProvider.php new file mode 100644 index 0000000000..7d02b78860 --- /dev/null +++ b/src/Security/EmailUserProvider.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Security; + +use FOS\UserBundle\Model\UserInterface; + +class EmailUserProvider extends UserProvider +{ + protected function findUser($username): ?UserInterface + { + return $this->userManager->findUserByUsernameOrEmail($username); + } +} diff --git a/src/Security/LoginManager.php b/src/Security/LoginManager.php new file mode 100644 index 0000000000..3bc90f605c --- /dev/null +++ b/src/Security/LoginManager.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Security; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; + +/** + * Abstracts process for manually logging in a user. + * + * @author Johannes M. Schmitt + */ +class LoginManager implements LoginManagerInterface +{ + /** + * @var TokenStorageInterface + */ + private $tokenStorage; + + /** + * @var UserCheckerInterface + */ + private $userChecker; + + /** + * @var SessionAuthenticationStrategyInterface + */ + private $sessionStrategy; + + /** + * @var RequestStack + */ + private $requestStack; + + /** + * @var RememberMeHandlerInterface|null + */ + private $rememberMeHandler; + + public function __construct(TokenStorageInterface $tokenStorage, UserCheckerInterface $userChecker, + SessionAuthenticationStrategyInterface $sessionStrategy, + RequestStack $requestStack, + ?RememberMeHandlerInterface $rememberMeHandler = null + ) { + $this->tokenStorage = $tokenStorage; + $this->userChecker = $userChecker; + $this->sessionStrategy = $sessionStrategy; + $this->requestStack = $requestStack; + $this->rememberMeHandler = $rememberMeHandler; + } + + final public function logInUser($firewallName, UserInterface $user, ?Response $response = null): void + { + $this->userChecker->checkPreAuth($user); + + $token = $this->createToken($firewallName, $user); + $request = $this->requestStack->getCurrentRequest(); + + if (null !== $request) { + $this->sessionStrategy->onAuthentication($request, $token); + + if ($this->rememberMeHandler instanceof RememberMeHandlerInterface) { + $this->rememberMeHandler->createRememberMeCookie($user); + } + } + + $this->tokenStorage->setToken($token); + } + + /** + * @param string $firewall + */ + protected function createToken($firewall, UserInterface $user): UsernamePasswordToken + { + return new UsernamePasswordToken($user, $firewall, $user->getRoles()); + } +} diff --git a/src/Security/LoginManagerInterface.php b/src/Security/LoginManagerInterface.php new file mode 100644 index 0000000000..5aa9a3f753 --- /dev/null +++ b/src/Security/LoginManagerInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Security; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\HttpFoundation\Response; + +interface LoginManagerInterface +{ + /** + * @param string $firewallName + */ + public function logInUser($firewallName, UserInterface $user, ?Response $response = null): void; +} diff --git a/src/Security/UserChecker.php b/src/Security/UserChecker.php new file mode 100644 index 0000000000..f8f133b291 --- /dev/null +++ b/src/Security/UserChecker.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Security; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\Security\Core\Exception\DisabledException; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface; + +/** + * UserChecker checks the user account flags. + * + * @author Julian Finkler (Devtronic) + */ +class UserChecker implements UserCheckerInterface +{ + public function checkPreAuth(BaseUserInterface $user): void + { + if ($user instanceof UserInterface && !$user->isEnabled()) { + $ex = new DisabledException('User account is disabled.'); + $ex->setUser($user); + throw $ex; + } + } + + public function checkPostAuth(BaseUserInterface $user): void + { + } +} diff --git a/src/Security/UserProvider.php b/src/Security/UserProvider.php new file mode 100644 index 0000000000..80d352565b --- /dev/null +++ b/src/Security/UserProvider.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Security; + +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; +use Symfony\Component\Security\Core\User\UserInterface as SecurityUserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; + +/** + * @template-implements UserProviderInterface + */ +class UserProvider implements UserProviderInterface +{ + /** + * @var UserManagerInterface + */ + protected $userManager; + + /** + * Constructor. + */ + public function __construct(UserManagerInterface $userManager) + { + $this->userManager = $userManager; + } + + public function loadUserByIdentifier(string $identifier): SecurityUserInterface + { + $user = $this->findUser($identifier); + + if (!$user) { + throw new UserNotFoundException(sprintf('Username "%s" does not exist.', $identifier)); + } + + return $user; + } + + public function refreshUser(SecurityUserInterface $user): SecurityUserInterface + { + if (!$user instanceof UserInterface) { + throw new UnsupportedUserException(sprintf('Expected an instance of FOS\UserBundle\Model\UserInterface, but got "%s".', get_class($user))); + } + + if (!$this->supportsClass(get_class($user))) { + throw new UnsupportedUserException(sprintf('Expected an instance of %s, but got "%s".', $this->userManager->getClass(), get_class($user))); + } + + if (null === $reloadedUser = $this->userManager->findUserBy(['id' => $user->getId()])) { + throw new UserNotFoundException(sprintf('User with ID "%s" could not be reloaded.', $user->getId())); + } + + return $reloadedUser; + } + + /** + * @param string $class + */ + public function supportsClass($class): bool + { + $userClass = $this->userManager->getClass(); + + return $userClass === $class || is_subclass_of($class, $userClass); + } + + /** + * Finds a user by username. + * + * This method is meant to be an extension point for child classes. + * + * @param string $username + */ + protected function findUser($username): ?UserInterface + { + return $this->userManager->findUserByUsername($username); + } +} diff --git a/src/Util/CanonicalFieldsUpdater.php b/src/Util/CanonicalFieldsUpdater.php new file mode 100644 index 0000000000..923f3c7b79 --- /dev/null +++ b/src/Util/CanonicalFieldsUpdater.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +use FOS\UserBundle\Model\UserInterface; + +/** + * Class updating the canonical fields of the user. + * + * @author Christophe Coevoet + * + * @final + */ +class CanonicalFieldsUpdater +{ + private $usernameCanonicalizer; + private $emailCanonicalizer; + + public function __construct(CanonicalizerInterface $usernameCanonicalizer, CanonicalizerInterface $emailCanonicalizer) + { + $this->usernameCanonicalizer = $usernameCanonicalizer; + $this->emailCanonicalizer = $emailCanonicalizer; + } + + public function updateCanonicalFields(UserInterface $user): void + { + $user->setUsernameCanonical($this->canonicalizeUsername($user->getUsername())); + $user->setEmailCanonical($this->canonicalizeEmail($user->getEmail())); + } + + /** + * Canonicalizes an email. + * + * @param string|null $email + */ + public function canonicalizeEmail($email): ?string + { + return $this->emailCanonicalizer->canonicalize($email); + } + + /** + * Canonicalizes a username. + * + * @param string|null $username + */ + public function canonicalizeUsername($username): ?string + { + return $this->usernameCanonicalizer->canonicalize($username); + } +} diff --git a/src/Util/Canonicalizer.php b/src/Util/Canonicalizer.php new file mode 100644 index 0000000000..53f58ea1ba --- /dev/null +++ b/src/Util/Canonicalizer.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +class Canonicalizer implements CanonicalizerInterface +{ + public function canonicalize($string): ?string + { + if (null === $string) { + return null; + } + + $encoding = mb_detect_encoding($string); + $result = $encoding + ? mb_convert_case($string, MB_CASE_LOWER, $encoding) + : mb_convert_case($string, MB_CASE_LOWER); + + return $result; + } +} diff --git a/src/Util/CanonicalizerInterface.php b/src/Util/CanonicalizerInterface.php new file mode 100644 index 0000000000..ad893840b0 --- /dev/null +++ b/src/Util/CanonicalizerInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +interface CanonicalizerInterface +{ + /** + * @param string|null $string + * + * @phpstan-return ($string is null ? null : string) + */ + public function canonicalize($string): ?string; +} diff --git a/src/Util/HashingPasswordUpdater.php b/src/Util/HashingPasswordUpdater.php new file mode 100644 index 0000000000..2d0e71f832 --- /dev/null +++ b/src/Util/HashingPasswordUpdater.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; +use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface; + +/** + * Class updating the hashed password in the user when there is a new password. + * + * @author Christophe Coevoet + */ +class HashingPasswordUpdater implements PasswordUpdaterInterface +{ + private $passwordHasherFactory; + + public function __construct(PasswordHasherFactoryInterface $passwordHasherFactory) + { + $this->passwordHasherFactory = $passwordHasherFactory; + } + + public function hashPassword(UserInterface $user): void + { + $plainPassword = $user->getPlainPassword(); + + if (null === $plainPassword || '' === $plainPassword) { + return; + } + + $hasher = $this->passwordHasherFactory->getPasswordHasher($user); + + if (!$hasher instanceof LegacyPasswordHasherInterface) { + $user->setSalt(null); + $hashedPassword = $hasher->hash($plainPassword); + } else { + $salt = rtrim(str_replace('+', '.', base64_encode(random_bytes(32))), '='); + $user->setSalt($salt); + + $hashedPassword = $hasher->hash($plainPassword, $salt); + } + + $user->setPassword($hashedPassword); + $user->eraseCredentials(); + } +} diff --git a/src/Util/PasswordUpdaterInterface.php b/src/Util/PasswordUpdaterInterface.php new file mode 100644 index 0000000000..d628f17c64 --- /dev/null +++ b/src/Util/PasswordUpdaterInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +use FOS\UserBundle\Model\UserInterface; + +/** + * @author Christophe Coevoet + */ +interface PasswordUpdaterInterface +{ + /** + * Updates the hashed password in the user when there is a new password. + * + * The implement should be a no-op in case there is no new password (it should not erase the + * existing hash with a wrong one). + */ + public function hashPassword(UserInterface $user): void; +} diff --git a/src/Util/TokenGenerator.php b/src/Util/TokenGenerator.php new file mode 100644 index 0000000000..0f303cd7c0 --- /dev/null +++ b/src/Util/TokenGenerator.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +class TokenGenerator implements TokenGeneratorInterface +{ + public function generateToken(): string + { + return rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '='); + } +} diff --git a/src/Util/TokenGeneratorInterface.php b/src/Util/TokenGeneratorInterface.php new file mode 100644 index 0000000000..cd5e573b39 --- /dev/null +++ b/src/Util/TokenGeneratorInterface.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +interface TokenGeneratorInterface +{ + public function generateToken(): string; +} diff --git a/src/Util/UserManipulator.php b/src/Util/UserManipulator.php new file mode 100644 index 0000000000..31a8c91532 --- /dev/null +++ b/src/Util/UserManipulator.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Util; + +use FOS\UserBundle\Event\UserEvent; +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Model\UserManagerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * Executes some manipulations on the users. + * + * @author Christophe Coevoet + * @author Luis Cordova + * + * @internal + * + * @final + */ +class UserManipulator +{ + /** + * User manager. + * + * @var UserManagerInterface + */ + private $userManager; + + /** + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * @var RequestStack + */ + private $requestStack; + + /** + * UserManipulator constructor. + */ + public function __construct(UserManagerInterface $userManager, EventDispatcherInterface $dispatcher, RequestStack $requestStack) + { + $this->userManager = $userManager; + $this->dispatcher = $dispatcher; + $this->requestStack = $requestStack; + } + + /** + * Creates a user and returns it. + * + * @param string $username + * @param string $password + * @param string $email + * @param bool $active + * @param bool $superadmin + */ + public function create($username, $password, $email, $active, $superadmin): UserInterface + { + $user = $this->userManager->createUser(); + $user->setUsername($username); + $user->setEmail($email); + $user->setPlainPassword($password); + $user->setEnabled((bool) $active); + $user->setSuperAdmin((bool) $superadmin); + $this->userManager->updateUser($user); + + $event = new UserEvent($user, $this->getRequest()); + $this->dispatcher->dispatch($event, FOSUserEvents::USER_CREATED); + + return $user; + } + + /** + * Activates the given user. + * + * @param string $username + */ + public function activate($username): void + { + $user = $this->findUserByUsernameOrThrowException($username); + $user->setEnabled(true); + $this->userManager->updateUser($user); + + $event = new UserEvent($user, $this->getRequest()); + $this->dispatcher->dispatch($event, FOSUserEvents::USER_ACTIVATED); + } + + /** + * Deactivates the given user. + * + * @param string $username + */ + public function deactivate($username): void + { + $user = $this->findUserByUsernameOrThrowException($username); + $user->setEnabled(false); + $this->userManager->updateUser($user); + + $event = new UserEvent($user, $this->getRequest()); + $this->dispatcher->dispatch($event, FOSUserEvents::USER_DEACTIVATED); + } + + /** + * Changes the password for the given user. + * + * @param string $username + * @param string $password + */ + public function changePassword($username, $password): void + { + $user = $this->findUserByUsernameOrThrowException($username); + $user->setPlainPassword($password); + $this->userManager->updateUser($user); + + $event = new UserEvent($user, $this->getRequest()); + $this->dispatcher->dispatch($event, FOSUserEvents::USER_PASSWORD_CHANGED); + } + + /** + * Promotes the given user. + * + * @param string $username + */ + public function promote($username): void + { + $user = $this->findUserByUsernameOrThrowException($username); + $user->setSuperAdmin(true); + $this->userManager->updateUser($user); + + $event = new UserEvent($user, $this->getRequest()); + $this->dispatcher->dispatch($event, FOSUserEvents::USER_PROMOTED); + } + + /** + * Demotes the given user. + * + * @param string $username + */ + public function demote($username): void + { + $user = $this->findUserByUsernameOrThrowException($username); + $user->setSuperAdmin(false); + $this->userManager->updateUser($user); + + $event = new UserEvent($user, $this->getRequest()); + $this->dispatcher->dispatch($event, FOSUserEvents::USER_DEMOTED); + } + + /** + * Adds role to the given user. + * + * @param string $username + * @param string $role + * + * @return bool true if role was added, false if user already had the role + */ + public function addRole($username, $role): bool + { + $user = $this->findUserByUsernameOrThrowException($username); + if ($user->hasRole($role)) { + return false; + } + $user->addRole($role); + $this->userManager->updateUser($user); + + return true; + } + + /** + * Removes role from the given user. + * + * @param string $username + * @param string $role + * + * @return bool true if role was removed, false if user didn't have the role + */ + public function removeRole($username, $role): bool + { + $user = $this->findUserByUsernameOrThrowException($username); + if (!$user->hasRole($role)) { + return false; + } + $user->removeRole($role); + $this->userManager->updateUser($user); + + return true; + } + + /** + * Finds a user by his username and throws an exception if we can't find it. + * + * @param string $username + * + * @throws \InvalidArgumentException When user does not exist + */ + private function findUserByUsernameOrThrowException($username): UserInterface + { + $user = $this->userManager->findUserByUsername($username); + + if (!$user) { + throw new \InvalidArgumentException(sprintf('User identified by "%s" username does not exist.', $username)); + } + + return $user; + } + + private function getRequest(): ?Request + { + return $this->requestStack->getCurrentRequest(); + } +} diff --git a/src/Validator/Initializer.php b/src/Validator/Initializer.php new file mode 100644 index 0000000000..d2d6ce13a4 --- /dev/null +++ b/src/Validator/Initializer.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Validator; + +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Util\CanonicalFieldsUpdater; +use Symfony\Component\Validator\ObjectInitializerInterface; + +/** + * Automatically updates the canonical fields before validation. + * + * @author Christophe Coevoet + * + * @internal + */ +final class Initializer implements ObjectInitializerInterface +{ + private $canonicalFieldsUpdater; + + public function __construct(CanonicalFieldsUpdater $canonicalFieldsUpdater) + { + $this->canonicalFieldsUpdater = $canonicalFieldsUpdater; + } + + /** + * @param object $object + */ + public function initialize($object): void + { + if ($object instanceof UserInterface) { + $this->canonicalFieldsUpdater->updateCanonicalFields($object); + } + } +} diff --git a/tests/Command/ActivateUserCommandTest.php b/tests/Command/ActivateUserCommandTest.php new file mode 100644 index 0000000000..379855edef --- /dev/null +++ b/tests/Command/ActivateUserCommandTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Command; + +use FOS\UserBundle\Command\ActivateUserCommand; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class ActivateUserCommandTest extends TestCase +{ + public function testExecute() + { + $commandTester = $this->createCommandTester($this->getManipulator('user')); + $exitCode = $commandTester->execute([ + 'username' => 'user', + ], [ + 'decorated' => false, + 'interactive' => false, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/User "user" has been activated/', $commandTester->getDisplay()); + } + + public function testExecuteInteractiveWithQuestionHelper() + { + $application = new Application(); + + $helper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + ->setMethods(['ask']) + ->getMock(); + + $helper + ->method('ask') + ->will($this->returnValue('user')); + + $application->getHelperSet()->set($helper, 'question'); + + $commandTester = $this->createCommandTester($this->getManipulator('user'), $application); + $exitCode = $commandTester->execute([], [ + 'decorated' => false, + 'interactive' => true, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/User "user" has been activated/', $commandTester->getDisplay()); + } + + private function createCommandTester(UserManipulator $manipulator, ?Application $application = null): CommandTester + { + if (null === $application) { + $application = new Application(); + } + + $application->setAutoExit(false); + + $command = new ActivateUserCommand($manipulator); + + $application->add($command); + + return new CommandTester($application->find('fos:user:activate')); + } + + private function getManipulator(string $username): UserManipulator + { + $manipulator = $this->getMockBuilder('FOS\UserBundle\Util\UserManipulator') + ->disableOriginalConstructor() + ->getMock(); + + $manipulator + ->expects($this->once()) + ->method('activate') + ->with($username) + ; + + return $manipulator; + } +} diff --git a/tests/Command/ChangePasswordCommandTest.php b/tests/Command/ChangePasswordCommandTest.php new file mode 100644 index 0000000000..397baa9e42 --- /dev/null +++ b/tests/Command/ChangePasswordCommandTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Command; + +use FOS\UserBundle\Command\ChangePasswordCommand; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class ChangePasswordCommandTest extends TestCase +{ + public function testExecute() + { + $commandTester = $this->createCommandTester($this->getManipulator('user', 'pass')); + $exitCode = $commandTester->execute([ + 'username' => 'user', + 'password' => 'pass', + ], [ + 'decorated' => false, + 'interactive' => false, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Changed password for user user/', $commandTester->getDisplay()); + } + + public function testExecuteInteractiveWithQuestionHelper() + { + $application = new Application(); + + $helper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + ->setMethods(['ask']) + ->getMock(); + + $helper->expects($this->exactly(2)) + ->method('ask') + ->willReturnOnConsecutiveCalls('user', 'pass'); + + $application->getHelperSet()->set($helper, 'question'); + + $commandTester = $this->createCommandTester($this->getManipulator('user', 'pass'), $application); + $exitCode = $commandTester->execute([], [ + 'decorated' => false, + 'interactive' => true, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Changed password for user user/', $commandTester->getDisplay()); + } + + private function createCommandTester(UserManipulator $userManipulator, ?Application $application = null): CommandTester + { + if (null === $application) { + $application = new Application(); + } + + $application->setAutoExit(false); + + $command = new ChangePasswordCommand($userManipulator); + + $application->add($command); + + return new CommandTester($application->find('fos:user:change-password')); + } + + private function getManipulator(string $username, string $password): UserManipulator + { + $manipulator = $this->getMockBuilder('FOS\UserBundle\Util\UserManipulator') + ->disableOriginalConstructor() + ->getMock(); + + $manipulator + ->expects($this->once()) + ->method('changePassword') + ->with($username, $password) + ; + + return $manipulator; + } +} diff --git a/tests/Command/CreateUserCommandTest.php b/tests/Command/CreateUserCommandTest.php new file mode 100644 index 0000000000..3789bffd58 --- /dev/null +++ b/tests/Command/CreateUserCommandTest.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Command; + +use FOS\UserBundle\Command\CreateUserCommand; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class CreateUserCommandTest extends TestCase +{ + public function testExecute() + { + $commandTester = $this->createCommandTester($this->getManipulator('user', 'pass', 'email', true, false)); + $exitCode = $commandTester->execute([ + 'username' => 'user', + 'email' => 'email', + 'password' => 'pass', + ], [ + 'decorated' => false, + 'interactive' => false, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Created user user/', $commandTester->getDisplay()); + } + + public function testExecuteInteractiveWithQuestionHelper() + { + $application = new Application(); + + $helper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + ->setMethods(['ask']) + ->getMock(); + + $helper->expects($this->exactly(3)) + ->method('ask') + ->willReturnOnConsecutiveCalls('user', 'email', 'pass'); + + $application->getHelperSet()->set($helper, 'question'); + + $commandTester = $this->createCommandTester( + $this->getManipulator('user', 'pass', 'email', true, false), $application + ); + $exitCode = $commandTester->execute([], [ + 'decorated' => false, + 'interactive' => true, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Created user user/', $commandTester->getDisplay()); + } + + private function createCommandTester(UserManipulator $manipulator, ?Application $application = null): CommandTester + { + if (null === $application) { + $application = new Application(); + } + + $application->setAutoExit(false); + + $command = new CreateUserCommand($manipulator); + + $application->add($command); + + return new CommandTester($application->find('fos:user:create')); + } + + private function getManipulator(string $username, string $password, string $email, bool $active, bool $superadmin): UserManipulator + { + $manipulator = $this->getMockBuilder('FOS\UserBundle\Util\UserManipulator') + ->disableOriginalConstructor() + ->getMock(); + + $manipulator + ->expects($this->once()) + ->method('create') + ->with($username, $password, $email, $active, $superadmin) + ; + + return $manipulator; + } +} diff --git a/tests/Command/DeactivateUserCommandTest.php b/tests/Command/DeactivateUserCommandTest.php new file mode 100644 index 0000000000..4f20700e7e --- /dev/null +++ b/tests/Command/DeactivateUserCommandTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Command; + +use FOS\UserBundle\Command\DeactivateUserCommand; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class DeactivateUserCommandTest extends TestCase +{ + public function testExecute() + { + $commandTester = $this->createCommandTester($this->getManipulator('user')); + $exitCode = $commandTester->execute([ + 'username' => 'user', + ], [ + 'decorated' => false, + 'interactive' => false, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/User "user" has been deactivated/', $commandTester->getDisplay()); + } + + public function testExecuteInteractiveWithQuestionHelper() + { + $application = new Application(); + + $helper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + ->setMethods(['ask']) + ->getMock(); + + $helper->expects($this->once()) + ->method('ask') + ->willReturn('user'); + + $application->getHelperSet()->set($helper, 'question'); + + $commandTester = $this->createCommandTester($this->getManipulator('user'), $application); + $exitCode = $commandTester->execute([], [ + 'decorated' => false, + 'interactive' => true, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/User "user" has been deactivated/', $commandTester->getDisplay()); + } + + private function createCommandTester(UserManipulator $manipulator, ?Application $application = null): CommandTester + { + if (null === $application) { + $application = new Application(); + } + + $application->setAutoExit(false); + + $command = new DeactivateUserCommand($manipulator); + + $application->add($command); + + return new CommandTester($application->find('fos:user:deactivate')); + } + + private function getManipulator(string $username): UserManipulator + { + $manipulator = $this->getMockBuilder('FOS\UserBundle\Util\UserManipulator') + ->disableOriginalConstructor() + ->getMock(); + + $manipulator + ->expects($this->once()) + ->method('deactivate') + ->with($username) + ; + + return $manipulator; + } +} diff --git a/tests/Command/DemoteUserCommandTest.php b/tests/Command/DemoteUserCommandTest.php new file mode 100644 index 0000000000..b2db91bf0e --- /dev/null +++ b/tests/Command/DemoteUserCommandTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Command; + +use FOS\UserBundle\Command\DemoteUserCommand; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class DemoteUserCommandTest extends TestCase +{ + public function testExecute() + { + $commandTester = $this->createCommandTester($this->getManipulator('user', 'role', false)); + $exitCode = $commandTester->execute([ + 'username' => 'user', + 'role' => 'role', + ], [ + 'decorated' => false, + 'interactive' => false, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Role "role" has been removed from user "user"/', $commandTester->getDisplay()); + } + + public function testExecuteInteractiveWithQuestionHelper() + { + $application = new Application(); + + $helper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + ->setMethods(['ask']) + ->getMock(); + + $helper->expects($this->exactly(2)) + ->method('ask') + ->willReturnOnConsecutiveCalls('user', 'role'); + + $application->getHelperSet()->set($helper, 'question'); + + $commandTester = $this->createCommandTester($this->getManipulator('user', 'role', false), $application); + $exitCode = $commandTester->execute([], [ + 'decorated' => false, + 'interactive' => true, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Role "role" has been removed from user "user"/', $commandTester->getDisplay()); + } + + private function createCommandTester(UserManipulator $manipulator, ?Application $application = null): CommandTester + { + if (null === $application) { + $application = new Application(); + } + + $application->setAutoExit(false); + + $command = new DemoteUserCommand($manipulator); + + $application->add($command); + + return new CommandTester($application->find('fos:user:demote')); + } + + private function getManipulator(string $username, string $role, bool $super): UserManipulator + { + $manipulator = $this->getMockBuilder('FOS\UserBundle\Util\UserManipulator') + ->disableOriginalConstructor() + ->getMock(); + + if ($super) { + $manipulator + ->expects($this->once()) + ->method('demote') + ->with($username) + ->will($this->returnValue(true)) + ; + } else { + $manipulator + ->expects($this->once()) + ->method('removeRole') + ->with($username, $role) + ->will($this->returnValue(true)) + ; + } + + return $manipulator; + } +} diff --git a/tests/Command/PromoteUserCommandTest.php b/tests/Command/PromoteUserCommandTest.php new file mode 100644 index 0000000000..25b5e38f5d --- /dev/null +++ b/tests/Command/PromoteUserCommandTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Command; + +use FOS\UserBundle\Command\PromoteUserCommand; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class PromoteUserCommandTest extends TestCase +{ + public function testExecute() + { + $commandTester = $this->createCommandTester($this->getManipulator('user', 'role', false)); + $exitCode = $commandTester->execute([ + 'username' => 'user', + 'role' => 'role', + ], [ + 'decorated' => false, + 'interactive' => false, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Role "role" has been added to user "user"/', $commandTester->getDisplay()); + } + + public function testExecuteInteractiveWithQuestionHelper() + { + $application = new Application(); + + $helper = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') + ->setMethods(['ask']) + ->getMock(); + + $helper->expects($this->exactly(2)) + ->method('ask') + ->willReturnOnConsecutiveCalls('user', 'role'); + + $application->getHelperSet()->set($helper, 'question'); + + $commandTester = $this->createCommandTester($this->getManipulator('user', 'role', false), $application); + $exitCode = $commandTester->execute([], [ + 'decorated' => false, + 'interactive' => true, + ]); + + $this->assertSame(0, $exitCode, 'Returns 0 in case of success'); + $this->assertMatchesRegularExpression('/Role "role" has been added to user "user"/', $commandTester->getDisplay()); + } + + private function createCommandTester(UserManipulator $manipulator, ?Application $application = null): CommandTester + { + if (null === $application) { + $application = new Application(); + } + + $application->setAutoExit(false); + + $command = new PromoteUserCommand($manipulator); + + $application->add($command); + + return new CommandTester($application->find('fos:user:promote')); + } + + private function getManipulator(string $username, string $role, bool $super): UserManipulator + { + $manipulator = $this->getMockBuilder('FOS\UserBundle\Util\UserManipulator') + ->disableOriginalConstructor() + ->getMock(); + + if ($super) { + $manipulator + ->expects($this->once()) + ->method('promote') + ->with($username) + ->will($this->returnValue(true)) + ; + } else { + $manipulator + ->expects($this->once()) + ->method('addRole') + ->with($username, $role) + ->will($this->returnValue(true)) + ; + } + + return $manipulator; + } +} diff --git a/tests/DependencyInjection/FOSUserExtensionTest.php b/tests/DependencyInjection/FOSUserExtensionTest.php new file mode 100644 index 0000000000..65cec37c6d --- /dev/null +++ b/tests/DependencyInjection/FOSUserExtensionTest.php @@ -0,0 +1,508 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\DependencyInjection; + +use FOS\UserBundle\DependencyInjection\FOSUserExtension; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Yaml\Parser; + +class FOSUserExtensionTest extends TestCase +{ + /** @var ContainerBuilder */ + protected $configuration; + + protected function tearDown(): void + { + unset($this->configuration); + } + + public function testUserLoadThrowsExceptionUnlessDatabaseDriverSet() + { + $this->expectException(InvalidConfigurationException::class); + + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + unset($config['db_driver']); + $loader->load([$config], new ContainerBuilder()); + } + + public function testUserLoadThrowsExceptionUnlessDatabaseDriverIsValid() + { + $this->expectException(InvalidConfigurationException::class); + + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['db_driver'] = 'foo'; + $loader->load([$config], new ContainerBuilder()); + } + + public function testUserLoadThrowsExceptionUnlessFirewallNameSet() + { + $this->expectException(InvalidConfigurationException::class); + + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + unset($config['firewall_name']); + $loader->load([$config], new ContainerBuilder()); + } + + public function testUserLoadThrowsExceptionUnlessUserModelClassSet() + { + $this->expectException(InvalidConfigurationException::class); + + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + unset($config['user_class']); + $loader->load([$config], new ContainerBuilder()); + } + + public function testCustomDriverWithoutManager() + { + $this->expectException(InvalidConfigurationException::class); + + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['db_driver'] = 'custom'; + $loader->load([$config], new ContainerBuilder()); + } + + public function testCustomDriver() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['db_driver'] = 'custom'; + $config['service']['user_manager'] = 'acme.user_manager'; + $loader->load([$config], $this->configuration); + + $this->assertNotHasDefinition('fos_user.user_manager.default'); + $this->assertAlias('acme.user_manager', 'fos_user.user_manager'); + $this->assertParameter('custom', 'fos_user.storage'); + } + + public function testDisableRegistration() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['registration'] = false; + $loader->load([$config], $this->configuration); + $this->assertNotHasDefinition('fos_user.registration.form.factory'); + + $mailer = $this->configuration->getDefinition('fos_user.mailer.twig_symfony'); + $parameters = $this->configuration->getParameterBag()->resolveValue( + $mailer->getArgument(3) + ); + $this->assertSame( + [ + 'confirmation' => ['address' => 'no-registration@acme.com', 'sender_name' => 'Acme Ltd'], + 'resetting' => ['address' => 'admin@acme.org', 'sender_name' => 'Acme Corp'], + ], + $parameters['from_email'] + ); + } + + public function testDisableResetting() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['resetting'] = false; + $loader->load([$config], $this->configuration); + $this->assertNotHasDefinition('fos_user.resetting.form.factory'); + + $mailer = $this->configuration->getDefinition('fos_user.mailer.twig_symfony'); + $parameters = $this->configuration->getParameterBag()->resolveValue( + $mailer->getArgument(3) + ); + $this->assertSame( + [ + 'confirmation' => ['address' => 'admin@acme.org', 'sender_name' => 'Acme Corp'], + 'resetting' => ['address' => 'no-resetting@acme.com', 'sender_name' => 'Acme Ltd'], + ], + $parameters['from_email'] + ); + } + + public function testDisableProfile() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['profile'] = false; + $loader->load([$config], $this->configuration); + $this->assertNotHasDefinition('fos_user.profile.form.factory'); + } + + public function testDisableChangePassword() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['change_password'] = false; + $loader->load([$config], $this->configuration); + $this->assertNotHasDefinition('fos_user.change_password.form.factory'); + } + + /** + * @dataProvider providerEmailsDisabledFeature + */ + public function testEmailsDisabledFeature($testConfig, $registration, $resetting) + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config = array_merge($config, $testConfig); + $loader->load([$config], $this->configuration); + + $this->assertParameter($registration, 'fos_user.registration.confirmation.from_address'); + $this->assertParameter($resetting, 'fos_user.resetting.email.from_address'); + } + + public function providerEmailsDisabledFeature() + { + $configBothFeaturesDisabled = ['registration' => false, 'resetting' => false]; + $configResettingDisabled = ['resetting' => false]; + $configRegistrationDisabled = ['registration' => false]; + $configOverridenRegistrationEmail = [ + 'registration' => [ + 'confirmation' => [ + 'from_email' => ['address' => 'ltd@acme.com', 'sender_name' => 'Acme Ltd'], + ], + ], + ]; + $configOverridenResettingEmail = [ + 'resetting' => [ + 'email' => [ + 'from_email' => ['address' => 'ltd@acme.com', 'sender_name' => 'Acme Ltd'], + ], + ], + ]; + + $default = ['address' => 'admin@acme.org', 'sender_name' => 'Acme Corp']; + $overriden = ['address' => 'ltd@acme.com', 'sender_name' => 'Acme Ltd']; + + return [ + [$configBothFeaturesDisabled, ['address' => 'no-registration@acme.com', 'sender_name' => 'Acme Ltd'], ['address' => 'no-resetting@acme.com', 'sender_name' => 'Acme Ltd']], + [$configResettingDisabled, $default, ['address' => 'no-resetting@acme.com', 'sender_name' => 'Acme Ltd']], + [$configRegistrationDisabled, ['address' => 'no-registration@acme.com', 'sender_name' => 'Acme Ltd'], $default], + [$configOverridenRegistrationEmail, $overriden, $default], + [$configOverridenResettingEmail, $default, $overriden], + ]; + } + + public function testUserLoadModelClassWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertParameter('Acme\MyBundle\Document\User', 'fos_user.model.user.class'); + } + + public function testUserLoadModelClass() + { + $this->createFullConfiguration(); + + $this->assertParameter('Acme\MyBundle\Entity\User', 'fos_user.model.user.class'); + } + + public function testUserLoadManagerClassWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertParameter('mongodb', 'fos_user.storage'); + $this->assertParameter(null, 'fos_user.model_manager_name'); + $this->assertAlias('fos_user.user_manager.default', 'fos_user.user_manager'); + } + + public function testUserLoadManagerClass() + { + $this->createFullConfiguration(); + + $this->assertParameter('orm', 'fos_user.storage'); + $this->assertParameter('custom', 'fos_user.model_manager_name'); + $this->assertAlias('acme_my.user_manager', 'fos_user.user_manager'); + } + + public function testUserLoadFormClass() + { + $this->createFullConfiguration(); + + $this->assertParameter('acme_my_profile', 'fos_user.profile.form.type'); + $this->assertParameter('acme_my_registration', 'fos_user.registration.form.type'); + $this->assertParameter('acme_my_change_password', 'fos_user.change_password.form.type'); + $this->assertParameter('acme_my_resetting', 'fos_user.resetting.form.type'); + } + + public function testUserLoadFormNameWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertParameter('fos_user_profile_form', 'fos_user.profile.form.name'); + $this->assertParameter('fos_user_registration_form', 'fos_user.registration.form.name'); + $this->assertParameter('fos_user_change_password_form', 'fos_user.change_password.form.name'); + $this->assertParameter('fos_user_resetting_form', 'fos_user.resetting.form.name'); + } + + public function testUserLoadFormName() + { + $this->createFullConfiguration(); + + $this->assertParameter('acme_profile_form', 'fos_user.profile.form.name'); + $this->assertParameter('acme_registration_form', 'fos_user.registration.form.name'); + $this->assertParameter('acme_change_password_form', 'fos_user.change_password.form.name'); + $this->assertParameter('acme_resetting_form', 'fos_user.resetting.form.name'); + } + + public function testUserLoadFormServiceWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertHasDefinition('fos_user.profile.form.factory'); + $this->assertHasDefinition('fos_user.registration.form.factory'); + $this->assertHasDefinition('fos_user.change_password.form.factory'); + $this->assertHasDefinition('fos_user.resetting.form.factory'); + } + + public function testUserLoadFormService() + { + $this->createFullConfiguration(); + + $this->assertHasDefinition('fos_user.profile.form.factory'); + $this->assertHasDefinition('fos_user.registration.form.factory'); + $this->assertHasDefinition('fos_user.change_password.form.factory'); + $this->assertHasDefinition('fos_user.resetting.form.factory'); + } + + public function testUserLoadConfirmationEmailWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertParameter(false, 'fos_user.registration.confirmation.enabled'); + $this->assertParameter(['address' => 'admin@acme.org', 'sender_name' => 'Acme Corp'], 'fos_user.registration.confirmation.from_address'); + $this->assertParameter('@FOSUser/Registration/email.txt.twig', 'fos_user.registration.confirmation.template'); + $this->assertParameter('@FOSUser/Resetting/email.txt.twig', 'fos_user.resetting.email.template'); + $this->assertParameter(['address' => 'admin@acme.org', 'sender_name' => 'Acme Corp'], 'fos_user.resetting.email.from_address'); + $this->assertParameter(86400, 'fos_user.resetting.token_ttl'); + } + + public function testUserLoadConfirmationEmail() + { + $this->createFullConfiguration(); + + $this->assertParameter(true, 'fos_user.registration.confirmation.enabled'); + $this->assertParameter(['address' => 'register@acme.org', 'sender_name' => 'Acme Corp'], 'fos_user.registration.confirmation.from_address'); + $this->assertParameter('AcmeMyBundle:Registration:mail.txt.twig', 'fos_user.registration.confirmation.template'); + $this->assertParameter('AcmeMyBundle:Resetting:mail.txt.twig', 'fos_user.resetting.email.template'); + $this->assertParameter(['address' => 'reset@acme.org', 'sender_name' => 'Acme Corp'], 'fos_user.resetting.email.from_address'); + $this->assertParameter(7200, 'fos_user.resetting.retry_ttl'); + } + + public function testUserLoadUtilServiceWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertAlias('fos_user.mailer.noop', 'fos_user.mailer'); + $this->assertAlias('fos_user.util.canonicalizer.default', 'fos_user.util.email_canonicalizer'); + $this->assertAlias('fos_user.util.canonicalizer.default', 'fos_user.util.username_canonicalizer'); + } + + public function testUserLoadUtilService() + { + $this->createFullConfiguration(); + + $this->assertAlias('acme_my.mailer', 'fos_user.mailer'); + $this->assertAlias('acme_my.email_canonicalizer', 'fos_user.util.email_canonicalizer'); + $this->assertAlias('acme_my.username_canonicalizer', 'fos_user.util.username_canonicalizer'); + } + + public function testUserLoadFlashesByDefault() + { + $this->createEmptyConfiguration(); + + $this->assertHasDefinition('fos_user.listener.flash'); + } + + public function testUserLoadFlashesCanBeDisabled() + { + $this->createFullConfiguration(); + + $this->assertNotHasDefinition('fos_user.listener.flash'); + } + + /** + * @dataProvider userManagerSetFactoryProvider + */ + public function testUserManagerSetFactory($dbDriver, $doctrineService) + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $config['db_driver'] = $dbDriver; + $loader->load([$config], $this->configuration); + + $definition = $this->configuration->getDefinition('fos_user.object_manager'); + + $this->assertAlias($doctrineService, 'fos_user.doctrine_registry'); + + $factory = $definition->getFactory(); + + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $factory[0]); + $this->assertSame('fos_user.doctrine_registry', (string) $factory[0]); + $this->assertSame('getManager', $factory[1]); + } + + /** + * @return iterable + */ + public function userManagerSetFactoryProvider(): iterable + { + return [ + ['orm', 'doctrine'], + ['mongodb', 'doctrine_mongodb'], + ]; + } + + protected function createEmptyConfiguration() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getEmptyConfig(); + $loader->load([$config], $this->configuration); + $this->assertTrue($this->configuration instanceof ContainerBuilder); + } + + protected function createFullConfiguration() + { + $this->configuration = new ContainerBuilder(); + $loader = new FOSUserExtension(); + $config = $this->getFullConfig(); + $loader->load([$config], $this->configuration); + $this->assertTrue($this->configuration instanceof ContainerBuilder); + } + + /** + * getEmptyConfig. + * + * @return array + */ + protected function getEmptyConfig(): array + { + $yaml = <<parse($yaml); + } + + /** + * @return array + */ + protected function getFullConfig(): array + { + $yaml = <<parse($yaml); + } + + /** + * @param string $value + * @param string $key + */ + private function assertAlias($value, $key): void + { + $this->assertSame($value, (string) $this->configuration->getAlias($key), sprintf('%s alias is correct', $key)); + } + + /** + * @param mixed $value + * @param string $key + */ + private function assertParameter($value, $key): void + { + $this->assertSame($value, $this->configuration->getParameter($key), sprintf('%s parameter is correct', $key)); + } + + /** + * @param string $id + */ + private function assertHasDefinition($id): void + { + $this->assertTrue($this->configuration->hasDefinition($id) ?: $this->configuration->hasAlias($id)); + } + + /** + * @param string $id + */ + private function assertNotHasDefinition($id): void + { + $this->assertFalse($this->configuration->hasDefinition($id) ?: $this->configuration->hasAlias($id)); + } +} diff --git a/tests/Doctrine/UserManagerTest.php b/tests/Doctrine/UserManagerTest.php new file mode 100644 index 0000000000..373331c20a --- /dev/null +++ b/tests/Doctrine/UserManagerTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Doctrine; + +use Doctrine\Persistence\Mapping\ClassMetadata; +use Doctrine\Persistence\ObjectManager; +use Doctrine\Persistence\ObjectRepository; +use FOS\UserBundle\Doctrine\UserManager; +use FOS\UserBundle\Model\User; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class UserManagerTest extends TestCase +{ + public const USER_CLASS = 'FOS\UserBundle\Tests\Doctrine\DummyUser'; + + /** @var UserManager */ + protected $userManager; + /** + * @var ObjectManager&MockObject + */ + protected $om; + /** + * @var ObjectRepository&MockObject + */ + protected $repository; + + protected function setUp(): void + { + $passwordUpdater = $this->getMockBuilder('FOS\UserBundle\Util\PasswordUpdaterInterface')->getMock(); + $fieldsUpdater = $this->getMockBuilder('FOS\UserBundle\Util\CanonicalFieldsUpdater') + ->disableOriginalConstructor() + ->getMock(); + $class = $this->getMockBuilder(ClassMetadata::class)->getMock(); + $this->om = $this->getMockBuilder(ObjectManager::class)->getMock(); + $this->repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); + + $this->om->expects($this->any()) + ->method('getRepository') + ->with($this->equalTo(static::USER_CLASS)) + ->will($this->returnValue($this->repository)); + $this->om->expects($this->any()) + ->method('getClassMetadata') + ->with($this->equalTo(static::USER_CLASS)) + ->will($this->returnValue($class)); + $class->expects($this->any()) + ->method('getName') + ->will($this->returnValue(static::USER_CLASS)); + + $this->userManager = new UserManager($passwordUpdater, $fieldsUpdater, $this->om, static::USER_CLASS); + } + + public function testDeleteUser() + { + $user = $this->getUser(); + $this->om->expects($this->once())->method('remove')->with($this->equalTo($user)); + $this->om->expects($this->once())->method('flush'); + + $this->userManager->deleteUser($user); + } + + public function testGetClass() + { + $this->assertSame(static::USER_CLASS, $this->userManager->getClass()); + } + + public function testFindUserBy() + { + $crit = ['foo' => 'bar']; + $this->repository->expects($this->once())->method('findOneBy')->with($this->equalTo($crit))->will($this->returnValue(null)); + + $this->userManager->findUserBy($crit); + } + + public function testFindUsers() + { + $this->repository->expects($this->once())->method('findAll')->will($this->returnValue([])); + + $this->userManager->findUsers(); + } + + public function testUpdateUser() + { + $user = $this->getUser(); + $this->om->expects($this->once())->method('persist')->with($this->equalTo($user)); + $this->om->expects($this->once())->method('flush'); + + $this->userManager->updateUser($user); + } + + protected function getUser(): mixed + { + $userClass = static::USER_CLASS; + + return new $userClass(); + } +} + +class DummyUser extends User +{ +} diff --git a/tests/EventListener/AuthenticationListenerTest.php b/tests/EventListener/AuthenticationListenerTest.php new file mode 100644 index 0000000000..e019fdbe45 --- /dev/null +++ b/tests/EventListener/AuthenticationListenerTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\EventListener; + +use FOS\UserBundle\Event\FilterUserResponseEvent; +use FOS\UserBundle\EventListener\AuthenticationListener; +use FOS\UserBundle\FOSUserEvents; +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +class AuthenticationListenerTest extends TestCase +{ + public const FIREWALL_NAME = 'foo'; + + /** @var EventDispatcherInterface */ + private $eventDispatcher; + + /** @var FilterUserResponseEvent */ + private $event; + + /** @var AuthenticationListener */ + private $listener; + + protected function setUp(): void + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + + $response = $this->getMockBuilder('Symfony\Component\HttpFoundation\Response')->getMock(); + $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); + $this->event = new FilterUserResponseEvent($user, $request, $response); + + $this->eventDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')->getMock(); + $this->eventDispatcher + ->expects($this->once()) + ->method('dispatch'); + + $loginManager = $this->getMockBuilder('FOS\UserBundle\Security\LoginManagerInterface')->getMock(); + + $this->listener = new AuthenticationListener($loginManager, self::FIREWALL_NAME); + } + + public function testAuthenticate() + { + $this->listener->authenticate($this->event, FOSUserEvents::REGISTRATION_COMPLETED, $this->eventDispatcher); + } +} diff --git a/tests/EventListener/FlashListenerTest.php b/tests/EventListener/FlashListenerTest.php new file mode 100644 index 0000000000..611b700dfa --- /dev/null +++ b/tests/EventListener/FlashListenerTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\EventListener; + +use FOS\UserBundle\EventListener\FlashListener; +use FOS\UserBundle\FOSUserEvents; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Contracts\EventDispatcher\Event; +use Symfony\Contracts\Translation\TranslatorInterface; + +class FlashListenerTest extends TestCase +{ + /** @var Event */ + private $event; + + /** @var FlashListener */ + private $listener; + + /** + * @var TranslatorInterface&MockObject + */ + private $translator; + + protected function setUp(): void + { + $this->event = new Event(); + + $flashBag = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Flash\FlashBag')->getMock(); + + $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->disableOriginalConstructor()->getMock(); + $session + ->expects($this->once()) + ->method('getFlashBag') + ->willReturn($flashBag); + + $request = new Request(); + $request->setSession($session); + + $requestStack = new RequestStack(); + $requestStack->push($request); + + $this->translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); + + $this->listener = new FlashListener($requestStack, $this->translator); + } + + public function testAddSuccessFlash() + { + $this->translator->method('trans') + ->with('change_password.flash.success', [], 'FOSUserBundle') + ->willReturn('Success message'); + + $this->listener->addSuccessFlash($this->event, FOSUserEvents::CHANGE_PASSWORD_COMPLETED); + } +} diff --git a/tests/Form/Type/ChangePasswordFormTypeTest.php b/tests/Form/Type/ChangePasswordFormTypeTest.php new file mode 100644 index 0000000000..f3eb7b8bcb --- /dev/null +++ b/tests/Form/Type/ChangePasswordFormTypeTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Form\Type; + +use FOS\UserBundle\Form\Type\ChangePasswordFormType; +use FOS\UserBundle\Tests\TestUser; + +class ChangePasswordFormTypeTest extends ValidatorExtensionTypeTestCase +{ + public function testSubmit() + { + $user = new TestUser(); + $user->setPassword('foo'); + + $form = $this->factory->create(ChangePasswordFormType::class, $user); + $formData = [ + 'current_password' => 'foo', + 'plainPassword' => [ + 'first' => 'bar', + 'second' => 'bar', + ], + ]; + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $this->assertSame($user, $form->getData()); + $this->assertSame('bar', $user->getPlainPassword()); + } + + protected function getTypes(): array + { + return array_merge(parent::getTypes(), [ + new ChangePasswordFormType('FOS\UserBundle\Tests\TestUser'), + ]); + } +} diff --git a/tests/Form/Type/ProfileFormTypeTest.php b/tests/Form/Type/ProfileFormTypeTest.php new file mode 100644 index 0000000000..b86d8c6edc --- /dev/null +++ b/tests/Form/Type/ProfileFormTypeTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Form\Type; + +use FOS\UserBundle\Form\Type\ProfileFormType; +use FOS\UserBundle\Tests\TestUser; + +class ProfileFormTypeTest extends ValidatorExtensionTypeTestCase +{ + public function testSubmit() + { + $user = new TestUser(); + + $form = $this->factory->create(ProfileFormType::class, $user); + $formData = [ + 'username' => 'bar', + 'email' => 'john@doe.com', + ]; + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $this->assertSame($user, $form->getData()); + $this->assertSame('bar', $user->getUsername()); + $this->assertSame('john@doe.com', $user->getEmail()); + } + + protected function getTypes(): array + { + return array_merge(parent::getTypes(), [ + new ProfileFormType('FOS\UserBundle\Tests\TestUser'), + ]); + } +} diff --git a/tests/Form/Type/RegistrationFormTypeTest.php b/tests/Form/Type/RegistrationFormTypeTest.php new file mode 100644 index 0000000000..d99b41238c --- /dev/null +++ b/tests/Form/Type/RegistrationFormTypeTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Form\Type; + +use FOS\UserBundle\Form\Type\RegistrationFormType; +use FOS\UserBundle\Tests\TestUser; + +class RegistrationFormTypeTest extends ValidatorExtensionTypeTestCase +{ + public function testSubmit() + { + $user = new TestUser(); + + $form = $this->factory->create(RegistrationFormType::class, $user); + $formData = [ + 'username' => 'bar', + 'email' => 'john@doe.com', + 'plainPassword' => [ + 'first' => 'test', + 'second' => 'test', + ], + ]; + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $this->assertSame($user, $form->getData()); + $this->assertSame('bar', $user->getUsername()); + $this->assertSame('john@doe.com', $user->getEmail()); + $this->assertSame('test', $user->getPlainPassword()); + } + + protected function getTypes(): array + { + return array_merge(parent::getTypes(), [ + new RegistrationFormType('FOS\UserBundle\Tests\TestUser'), + ]); + } +} diff --git a/tests/Form/Type/ResettingFormTypeTest.php b/tests/Form/Type/ResettingFormTypeTest.php new file mode 100644 index 0000000000..2b877ca818 --- /dev/null +++ b/tests/Form/Type/ResettingFormTypeTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Form\Type; + +use FOS\UserBundle\Form\Type\ResettingFormType; +use FOS\UserBundle\Tests\TestUser; + +class ResettingFormTypeTest extends ValidatorExtensionTypeTestCase +{ + public function testSubmit() + { + $user = new TestUser(); + + $form = $this->factory->create(ResettingFormType::class, $user); + $formData = [ + 'plainPassword' => [ + 'first' => 'test', + 'second' => 'test', + ], + ]; + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + $this->assertSame($user, $form->getData()); + $this->assertSame('test', $user->getPlainPassword()); + } + + protected function getTypes(): array + { + return array_merge(parent::getTypes(), [ + new ResettingFormType('FOS\UserBundle\Tests\TestUser'), + ]); + } +} diff --git a/tests/Form/Type/ValidatorExtensionTypeTestCase.php b/tests/Form/Type/ValidatorExtensionTypeTestCase.php new file mode 100644 index 0000000000..fa08184b10 --- /dev/null +++ b/tests/Form/Type/ValidatorExtensionTypeTestCase.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Form\Type; + +use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension; +use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Component\Validator\ConstraintViolationList; + +/** + * Class ValidatorExtensionTypeTestCase + * FormTypeValidatorExtension added as default. Useful for form types with `constraints` option. + * + * @author Sullivan Senechal + */ +class ValidatorExtensionTypeTestCase extends TypeTestCase +{ + protected function getTypeExtensions(): array + { + $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock(); + $validator->method('validate')->will($this->returnValue(new ConstraintViolationList())); + + return [ + new FormTypeValidatorExtension($validator), + ]; + } +} diff --git a/tests/Model/UserManagerTest.php b/tests/Model/UserManagerTest.php new file mode 100644 index 0000000000..6207dd8ee1 --- /dev/null +++ b/tests/Model/UserManagerTest.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Model; + +use FOS\UserBundle\Model\User; +use FOS\UserBundle\Model\UserManager; +use FOS\UserBundle\Util\CanonicalFieldsUpdater; +use FOS\UserBundle\Util\PasswordUpdaterInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class UserManagerTest extends TestCase +{ + /** @var UserManager&MockObject */ + private $manager; + + /** + * @var PasswordUpdaterInterface&MockObject + */ + private $passwordUpdater; + + /** + * @var CanonicalFieldsUpdater&MockObject + */ + private $fieldsUpdater; + + protected function setUp(): void + { + $this->passwordUpdater = $this->getMockBuilder('FOS\UserBundle\Util\PasswordUpdaterInterface')->getMock(); + $this->fieldsUpdater = $this->getMockBuilder('FOS\UserBundle\Util\CanonicalFieldsUpdater') + ->disableOriginalConstructor() + ->getMock(); + + $this->manager = $this->getUserManager([ + $this->passwordUpdater, + $this->fieldsUpdater, + ]); + } + + public function testUpdateCanonicalFields() + { + $user = $this->getUser(); + + $this->fieldsUpdater->expects($this->once()) + ->method('updateCanonicalFields') + ->with($this->identicalTo($user)); + + $this->manager->updateCanonicalFields($user); + } + + public function testUpdatePassword() + { + $user = $this->getUser(); + + $this->passwordUpdater->expects($this->once()) + ->method('hashPassword') + ->with($this->identicalTo($user)); + + $this->manager->updatePassword($user); + } + + public function testFindUserByUsername() + { + $this->manager->expects($this->once()) + ->method('findUserBy') + ->with($this->equalTo(['usernameCanonical' => 'jack'])); + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeUsername') + ->with('jack') + ->will($this->returnValue('jack')); + + $this->manager->findUserByUsername('jack'); + } + + public function testFindUserByUsernameLowercasesTheUsername() + { + $this->manager->expects($this->once()) + ->method('findUserBy') + ->with($this->equalTo(['usernameCanonical' => 'jack'])); + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeUsername') + ->with('JaCk') + ->will($this->returnValue('jack')); + + $this->manager->findUserByUsername('JaCk'); + } + + public function testFindUserByEmail() + { + $this->manager->expects($this->once()) + ->method('findUserBy') + ->with($this->equalTo(['emailCanonical' => 'jack@email.org'])); + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeEmail') + ->with('jack@email.org') + ->will($this->returnValue('jack@email.org')); + + $this->manager->findUserByEmail('jack@email.org'); + } + + public function testFindUserByEmailLowercasesTheEmail() + { + $this->manager->expects($this->once()) + ->method('findUserBy') + ->with($this->equalTo(['emailCanonical' => 'jack@email.org'])); + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeEmail') + ->with('JaCk@EmAiL.oRg') + ->will($this->returnValue('jack@email.org')); + + $this->manager->findUserByEmail('JaCk@EmAiL.oRg'); + } + + public function testFindUserByUsernameOrEmailWithUsername() + { + $this->manager->expects($this->once()) + ->method('findUserBy') + ->with($this->equalTo(['usernameCanonical' => 'jack'])); + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeUsername') + ->with('JaCk') + ->will($this->returnValue('jack')); + + $this->manager->findUserByUsernameOrEmail('JaCk'); + } + + public function testFindUserByUsernameOrEmailWithEmail() + { + $this->manager->expects($this->once()) + ->method('findUserBy') + ->with($this->equalTo(['emailCanonical' => 'jack@email.org'])) + ->willReturn($this->getUser()); + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeEmail') + ->with('JaCk@EmAiL.oRg') + ->will($this->returnValue('jack@email.org')); + + $this->manager->findUserByUsernameOrEmail('JaCk@EmAiL.oRg'); + } + + public function testFindUserByUsernameOrEmailWithUsernameThatLooksLikeEmail() + { + $usernameThatLooksLikeEmail = 'bob@example.com'; + $user = $this->getUser(); + + $this->manager->expects($this->exactly(2)) + ->method('findUserBy') + ->withConsecutive( + [$this->equalTo(['emailCanonical' => $usernameThatLooksLikeEmail])], + [$this->equalTo(['usernameCanonical' => $usernameThatLooksLikeEmail])] + ) + ->willReturnOnConsecutiveCalls(null, $user); + + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeEmail') + ->with($usernameThatLooksLikeEmail) + ->willReturn($usernameThatLooksLikeEmail); + + $this->fieldsUpdater->expects($this->once()) + ->method('canonicalizeUsername') + ->with($usernameThatLooksLikeEmail) + ->willReturn($usernameThatLooksLikeEmail); + + $actualUser = $this->manager->findUserByUsernameOrEmail($usernameThatLooksLikeEmail); + + $this->assertSame($user, $actualUser); + } + + private function getUser(): User&MockObject + { + return $this->getMockBuilder('FOS\UserBundle\Model\User') + ->getMockForAbstractClass(); + } + + /** + * @param array{PasswordUpdaterInterface, CanonicalFieldsUpdater} $args + */ + private function getUserManager(array $args): UserManager&MockObject + { + return $this->getMockBuilder('FOS\UserBundle\Model\UserManager') + ->setConstructorArgs($args) + ->getMockForAbstractClass(); + } +} diff --git a/tests/Model/UserTest.php b/tests/Model/UserTest.php new file mode 100644 index 0000000000..e4d225878c --- /dev/null +++ b/tests/Model/UserTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Model; + +use FOS\UserBundle\Model\User; +use PHPUnit\Framework\TestCase; + +class UserTest extends TestCase +{ + public function testUsername() + { + $user = $this->getUser(); + $this->assertNull($user->getUsername()); + + $user->setUsername('tony'); + $this->assertSame('tony', $user->getUsername()); + } + + public function testEmail() + { + $user = $this->getUser(); + $this->assertNull($user->getEmail()); + + $user->setEmail('tony@mail.org'); + $this->assertSame('tony@mail.org', $user->getEmail()); + } + + public function testIsPasswordRequestNonExpired() + { + $user = $this->getUser(); + $passwordRequestedAt = new \DateTime('-10 seconds'); + + $user->setPasswordRequestedAt($passwordRequestedAt); + + $this->assertSame($passwordRequestedAt, $user->getPasswordRequestedAt()); + $this->assertTrue($user->isPasswordRequestNonExpired(15)); + $this->assertFalse($user->isPasswordRequestNonExpired(5)); + } + + public function testIsPasswordRequestAtCleared() + { + $user = $this->getUser(); + $passwordRequestedAt = new \DateTime('-10 seconds'); + + $user->setPasswordRequestedAt($passwordRequestedAt); + $user->setPasswordRequestedAt(null); + + $this->assertFalse($user->isPasswordRequestNonExpired(15)); + $this->assertFalse($user->isPasswordRequestNonExpired(5)); + } + + public function testTrueHasRole() + { + $user = $this->getUser(); + $defaultrole = User::ROLE_DEFAULT; + $newrole = 'ROLE_X'; + $this->assertTrue($user->hasRole($defaultrole)); + $user->addRole($defaultrole); + $this->assertTrue($user->hasRole($defaultrole)); + $user->addRole($newrole); + $this->assertTrue($user->hasRole($newrole)); + } + + public function testFalseHasRole() + { + $user = $this->getUser(); + $newrole = 'ROLE_X'; + $this->assertFalse($user->hasRole($newrole)); + $user->addRole($newrole); + $this->assertTrue($user->hasRole($newrole)); + } + + public function testIsEqualTo() + { + $user = $this->getUser(); + $this->assertTrue($user->isEqualTo($user)); + $this->assertFalse($user->isEqualTo($this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock())); + + $user2 = $this->getUser(); + $user2->setPassword('secret'); + $this->assertFalse($user->isEqualTo($user2)); + + $user3 = $this->getUser(); + $user3->setSalt('pepper'); + $this->assertFalse($user->isEqualTo($user3)); + + $user4 = $this->getUser(); + $user4->setUsername('f00b4r'); + $this->assertFalse($user->isEqualTo($user4)); + } + + protected function getUser(): User + { + return $this->getMockForAbstractClass('FOS\UserBundle\Model\User'); + } +} diff --git a/tests/Routing/RoutingTest.php b/tests/Routing/RoutingTest.php new file mode 100644 index 0000000000..0427ce2aa7 --- /dev/null +++ b/tests/Routing/RoutingTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Routing; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Loader\XmlFileLoader; +use Symfony\Component\Routing\RouteCollection; + +class RoutingTest extends TestCase +{ + /** + * @dataProvider loadRoutingProvider + * + * @param string $routeName + * @param string $path + * @param string[] $methods + */ + public function testLoadRouting($routeName, $path, array $methods) + { + $locator = new FileLocator(); + $loader = new XmlFileLoader($locator); + + $collection = new RouteCollection(); + $collection->addCollection($loader->load(__DIR__.'/../../src/Resources/config/routing/change_password.xml')); + $subCollection = $loader->load(__DIR__.'/../../src/Resources/config/routing/profile.xml'); + $subCollection->addPrefix('/profile'); + $collection->addCollection($subCollection); + $subCollection = $loader->load(__DIR__.'/../../src/Resources/config/routing/registration.xml'); + $subCollection->addPrefix('/register'); + $collection->addCollection($subCollection); + $subCollection = $loader->load(__DIR__.'/../../src/Resources/config/routing/resetting.xml'); + $subCollection->addPrefix('/resetting'); + $collection->addCollection($subCollection); + $collection->addCollection($loader->load(__DIR__.'/../../src/Resources/config/routing/security.xml')); + + $route = $collection->get($routeName); + $this->assertNotNull($route, sprintf('The route "%s" should exists', $routeName)); + $this->assertSame($path, $route->getPath()); + $this->assertSame($methods, $route->getMethods()); + } + + /** + * @return iterable + */ + public function loadRoutingProvider(): iterable + { + return [ + ['fos_user_change_password', '/change-password', ['GET', 'POST']], + + ['fos_user_profile_show', '/profile/', ['GET']], + ['fos_user_profile_edit', '/profile/edit', ['GET', 'POST']], + + ['fos_user_registration_register', '/register/', ['GET', 'POST']], + ['fos_user_registration_check_email', '/register/check-email', ['GET']], + ['fos_user_registration_confirm', '/register/confirm/{token}', ['GET']], + ['fos_user_registration_confirmed', '/register/confirmed', ['GET']], + + ['fos_user_resetting_request', '/resetting/request', ['GET']], + ['fos_user_resetting_send_email', '/resetting/send-email', ['POST']], + ['fos_user_resetting_check_email', '/resetting/check-email', ['GET']], + ['fos_user_resetting_reset', '/resetting/reset/{token}', ['GET', 'POST']], + + ['fos_user_security_login', '/login', ['GET', 'POST']], + ['fos_user_security_check', '/login_check', ['POST']], + ['fos_user_security_logout', '/logout', ['GET', 'POST']], + ]; + } +} diff --git a/tests/Security/EmailProviderTest.php b/tests/Security/EmailProviderTest.php new file mode 100644 index 0000000000..4a2dbdab30 --- /dev/null +++ b/tests/Security/EmailProviderTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Security; + +use FOS\UserBundle\Model\UserManagerInterface; +use FOS\UserBundle\Security\EmailProvider; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; + +class EmailProviderTest extends TestCase +{ + /** + * @var UserManagerInterface&MockObject + */ + private $userManager; + + /** + * @var EmailProvider + */ + private $userProvider; + + protected function setUp(): void + { + $this->userManager = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $this->userProvider = new EmailProvider($this->userManager); + } + + public function testLoadUserByUsername() + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + $this->userManager->expects($this->once()) + ->method('findUserByEmail') + ->with('foobar') + ->will($this->returnValue($user)); + + $this->assertSame($user, $this->userProvider->loadUserByIdentifier('foobar')); + } + + public function testLoadUserByInvalidUsername() + { + $this->expectException(UserNotFoundException::class); + + $this->userManager->expects($this->once()) + ->method('findUserByEmail') + ->with('foobar') + ->will($this->returnValue(null)); + + $this->userProvider->loadUserByIdentifier('foobar'); + } + + public function testRefreshUserBy() + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\User') + ->setMethods(['getId']) + ->getMock(); + + $user->expects($this->once()) + ->method('getId') + ->will($this->returnValue('123')); + + $refreshedUser = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + $this->userManager->expects($this->once()) + ->method('findUserBy') + ->with(['id' => '123']) + ->will($this->returnValue($refreshedUser)); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->assertSame($refreshedUser, $this->userProvider->refreshUser($user)); + } + + public function testRefreshDeleted() + { + $this->expectException(UserNotFoundException::class); + + $user = $this->getMockForAbstractClass('FOS\UserBundle\Model\User'); + $this->userManager->expects($this->once()) + ->method('findUserBy') + ->will($this->returnValue(null)); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->userProvider->refreshUser($user); + } + + public function testRefreshInvalidUser() + { + $this->expectException(UnsupportedUserException::class); + + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $this->userManager->expects($this->any()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->userProvider->refreshUser($user); + } + + public function testRefreshInvalidUserClass() + { + $this->expectException(UnsupportedUserException::class); + + $user = $this->getMockBuilder('FOS\UserBundle\Model\User')->getMock(); + $providedUser = $this->getMockBuilder('FOS\UserBundle\Tests\TestUser')->getMock(); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->userProvider->refreshUser($providedUser); + } +} diff --git a/tests/Security/EmailUserProviderTest.php b/tests/Security/EmailUserProviderTest.php new file mode 100644 index 0000000000..33bb7e42b1 --- /dev/null +++ b/tests/Security/EmailUserProviderTest.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Security; + +use FOS\UserBundle\Model\UserManagerInterface; +use FOS\UserBundle\Security\EmailUserProvider; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; + +class EmailUserProviderTest extends TestCase +{ + /** + * @var UserManagerInterface&MockObject + */ + private $userManager; + + /** + * @var EmailUserProvider + */ + private $userProvider; + + protected function setUp(): void + { + $this->userManager = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $this->userProvider = new EmailUserProvider($this->userManager); + } + + public function testLoadUserByUsername() + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + $this->userManager->expects($this->once()) + ->method('findUserByUsernameOrEmail') + ->with('foobar') + ->will($this->returnValue($user)); + + $this->assertSame($user, $this->userProvider->loadUserByIdentifier('foobar')); + } + + public function testLoadUserByInvalidUsername() + { + $this->expectException(UserNotFoundException::class); + + $this->userManager->expects($this->once()) + ->method('findUserByUsernameOrEmail') + ->with('foobar') + ->will($this->returnValue(null)); + + $this->userProvider->loadUserByIdentifier('foobar'); + } + + public function testRefreshUserBy() + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\User') + ->setMethods(['getId']) + ->getMock(); + + $user->expects($this->once()) + ->method('getId') + ->will($this->returnValue('123')); + + $refreshedUser = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + $this->userManager->expects($this->once()) + ->method('findUserBy') + ->with(['id' => '123']) + ->will($this->returnValue($refreshedUser)); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->assertSame($refreshedUser, $this->userProvider->refreshUser($user)); + } + + public function testRefreshInvalidUser() + { + $this->expectException(UnsupportedUserException::class); + + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + + $this->userProvider->refreshUser($user); + } +} diff --git a/tests/Security/LoginManagerTest.php b/tests/Security/LoginManagerTest.php new file mode 100644 index 0000000000..86879ac00d --- /dev/null +++ b/tests/Security/LoginManagerTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Security; + +use FOS\UserBundle\Model\UserInterface; +use FOS\UserBundle\Security\LoginManager; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; + +class LoginManagerTest extends TestCase +{ + public function testLogInUserWithRequestStack() + { + $loginManager = $this->createLoginManager(); + $loginManager->logInUser('main', $this->mockUser()); + } + + public function testLogInUserWithRememberMeHandler() + { + $response = new Response(); + $user = $this->mockUser(); + + $rememberMeHandler = $this->createMock(RememberMeHandlerInterface::class); + $rememberMeHandler->expects($this->once()) + ->method('createRememberMeCookie') + ->with($user); + + $loginManager = $this->createLoginManager($rememberMeHandler); + $loginManager->logInUser('main', $user, $response); + } + + private function createLoginManager(?RememberMeHandlerInterface $rememberMeHandler = null): LoginManager + { + $tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); + $tokenStorage + ->expects($this->once()) + ->method('setToken') + ->with($this->isInstanceOf(TokenInterface::class)); + + $userChecker = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserCheckerInterface')->getMock(); + $userChecker + ->expects($this->once()) + ->method('checkPreAuth') + ->with($this->isInstanceOf(UserInterface::class)); + + $request = new Request(); + + $sessionStrategy = $this->getMockBuilder(SessionAuthenticationStrategyInterface::class)->getMock(); + $sessionStrategy + ->expects($this->once()) + ->method('onAuthentication') + ->with($request, $this->isInstanceOf(TokenInterface::class)); + + $requestStack = new RequestStack(); + $requestStack->push($request); + + return new LoginManager($tokenStorage, $userChecker, $sessionStrategy, $requestStack, $rememberMeHandler); + } + + private function mockUser(): UserInterface + { + $user = $this->getMockBuilder(UserInterface::class)->getMock(); + $user + ->expects($this->once()) + ->method('getRoles') + ->will($this->returnValue(['ROLE_USER'])); + + return $user; + } +} diff --git a/tests/Security/UserCheckerTest.php b/tests/Security/UserCheckerTest.php new file mode 100644 index 0000000000..66815d93ea --- /dev/null +++ b/tests/Security/UserCheckerTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Security; + +use FOS\UserBundle\Model\User; +use FOS\UserBundle\Security\UserChecker; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\DisabledException; + +class UserCheckerTest extends TestCase +{ + public function testCheckPreAuthFailsIsEnabled() + { + $this->expectExceptionMessage('User account is disabled.'); + $this->expectException(DisabledException::class); + + $userMock = $this->getUser(false); + $checker = new UserChecker(); + $checker->checkPreAuth($userMock); + } + + public function testCheckPreAuthSuccess() + { + $userMock = $this->getUser(true); + $checker = new UserChecker(); + + $checker->checkPreAuth($userMock); + $this->expectNotToPerformAssertions(); + } + + public function testCheckPostAuthSuccess() + { + $userMock = $this->getUser(true); + $checker = new UserChecker(); + + $checker->checkPostAuth($userMock); + $this->expectNotToPerformAssertions(); + } + + private function getUser($isEnabled): User + { + $userMock = $this->getMockBuilder('FOS\UserBundle\Model\User')->getMock(); + $userMock + ->method('isEnabled') + ->willReturn($isEnabled); + + return $userMock; + } +} diff --git a/tests/Security/UserProviderTest.php b/tests/Security/UserProviderTest.php new file mode 100644 index 0000000000..e294eca4ee --- /dev/null +++ b/tests/Security/UserProviderTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Security; + +use FOS\UserBundle\Model\UserManagerInterface; +use FOS\UserBundle\Security\UserProvider; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; + +class UserProviderTest extends TestCase +{ + /** + * @var UserManagerInterface&MockObject + */ + private $userManager; + + /** + * @var UserProvider + */ + private $userProvider; + + protected function setUp(): void + { + $this->userManager = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $this->userProvider = new UserProvider($this->userManager); + } + + public function testLoadUserByUsername() + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + $this->userManager->expects($this->once()) + ->method('findUserByUsername') + ->with('foobar') + ->will($this->returnValue($user)); + + $this->assertSame($user, $this->userProvider->loadUserByIdentifier('foobar')); + } + + public function testLoadUserByInvalidUsername() + { + $this->expectException(UserNotFoundException::class); + + $this->userManager->expects($this->once()) + ->method('findUserByUsername') + ->with('foobar') + ->will($this->returnValue(null)); + + $this->userProvider->loadUserByIdentifier('foobar'); + } + + public function testRefreshUserBy() + { + $user = $this->getMockBuilder('FOS\UserBundle\Model\User') + ->setMethods(['getId']) + ->getMock(); + + $user->expects($this->once()) + ->method('getId') + ->will($this->returnValue('123')); + + $refreshedUser = $this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock(); + $this->userManager->expects($this->once()) + ->method('findUserBy') + ->with(['id' => '123']) + ->will($this->returnValue($refreshedUser)); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->assertSame($refreshedUser, $this->userProvider->refreshUser($user)); + } + + public function testRefreshDeleted() + { + $this->expectException(UserNotFoundException::class); + + $user = $this->getMockForAbstractClass('FOS\UserBundle\Model\User'); + $this->userManager->expects($this->once()) + ->method('findUserBy') + ->will($this->returnValue(null)); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->userProvider->refreshUser($user); + } + + public function testRefreshInvalidUser() + { + $this->expectException(UnsupportedUserException::class); + + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $this->userManager->expects($this->any()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->userProvider->refreshUser($user); + } + + public function testRefreshInvalidUserClass() + { + $this->expectException(UnsupportedUserException::class); + + $user = $this->getMockBuilder('FOS\UserBundle\Model\User')->getMock(); + $providedUser = $this->getMockBuilder('FOS\UserBundle\Tests\TestUser')->getMock(); + + $this->userManager->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue(get_class($user))); + + $this->userProvider->refreshUser($providedUser); + } +} diff --git a/tests/TestUser.php b/tests/TestUser.php new file mode 100644 index 0000000000..dc2f3aa28a --- /dev/null +++ b/tests/TestUser.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests; + +use FOS\UserBundle\Model\User; + +class TestUser extends User +{ +} diff --git a/tests/Util/CanonicalFieldsUpdaterTest.php b/tests/Util/CanonicalFieldsUpdaterTest.php new file mode 100644 index 0000000000..ee45fcc118 --- /dev/null +++ b/tests/Util/CanonicalFieldsUpdaterTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Util; + +use FOS\UserBundle\Tests\TestUser; +use FOS\UserBundle\Util\CanonicalFieldsUpdater; +use FOS\UserBundle\Util\CanonicalizerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CanonicalFieldsUpdaterTest extends TestCase +{ + /** + * @var CanonicalFieldsUpdater + */ + private $updater; + /** + * @var CanonicalizerInterface&MockObject + */ + private $usernameCanonicalizer; + /** + * @var CanonicalizerInterface&MockObject + */ + private $emailCanonicalizer; + + protected function setUp(): void + { + $this->usernameCanonicalizer = $this->getMockCanonicalizer(); + $this->emailCanonicalizer = $this->getMockCanonicalizer(); + + $this->updater = new CanonicalFieldsUpdater($this->usernameCanonicalizer, $this->emailCanonicalizer); + } + + public function testUpdateCanonicalFields() + { + $user = new TestUser(); + $user->setUsername('Username'); + $user->setEmail('User@Example.com'); + + $this->usernameCanonicalizer->expects($this->once()) + ->method('canonicalize') + ->with('Username') + ->will($this->returnCallback('strtolower')); + + $this->emailCanonicalizer->expects($this->once()) + ->method('canonicalize') + ->with('User@Example.com') + ->will($this->returnCallback('strtolower')); + + $this->updater->updateCanonicalFields($user); + $this->assertSame('username', $user->getUsernameCanonical()); + $this->assertSame('user@example.com', $user->getEmailCanonical()); + } + + private function getMockCanonicalizer(): CanonicalizerInterface&MockObject + { + return $this->getMockBuilder('FOS\UserBundle\Util\CanonicalizerInterface')->getMock(); + } +} diff --git a/tests/Util/CanonicalizerTest.php b/tests/Util/CanonicalizerTest.php new file mode 100644 index 0000000000..ff56aecd2b --- /dev/null +++ b/tests/Util/CanonicalizerTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Util; + +use FOS\UserBundle\Util\Canonicalizer; +use PHPUnit\Framework\TestCase; + +class CanonicalizerTest extends TestCase +{ + /** + * @dataProvider canonicalizeProvider + */ + public function testCanonicalize(?string $source, ?string $expectedResult) + { + $canonicalizer = new Canonicalizer(); + $this->assertSame($expectedResult, $canonicalizer->canonicalize($source)); + } + + /** + * @return iterable + */ + public function canonicalizeProvider(): iterable + { + return [ + [null, null], + ['FOO', 'foo'], + [chr(171), '?'], + ]; + } +} diff --git a/tests/Util/HashingPasswordUpdaterTest.php b/tests/Util/HashingPasswordUpdaterTest.php new file mode 100644 index 0000000000..7a710967f4 --- /dev/null +++ b/tests/Util/HashingPasswordUpdaterTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Util; + +use FOS\UserBundle\Tests\TestUser; +use FOS\UserBundle\Util\HashingPasswordUpdater; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; +use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface; +use Symfony\Component\PasswordHasher\PasswordHasherInterface; + +class HashingPasswordUpdaterTest extends TestCase +{ + /** + * @var HashingPasswordUpdater + */ + private $updater; + /** + * @var PasswordHasherFactoryInterface&MockObject + */ + private $passwordHasherFactory; + + protected function setUp(): void + { + $this->passwordHasherFactory = $this->getMockBuilder(PasswordHasherFactoryInterface::class)->getMock(); + + $this->updater = new HashingPasswordUpdater($this->passwordHasherFactory); + } + + public function testUpdatePassword() + { + $hasher = $this->getMockBuilder(PasswordHasherInterface::class)->getMock(); + $user = new TestUser(); + $user->setPlainPassword('password'); + + $this->passwordHasherFactory->expects($this->once()) + ->method('getPasswordHasher') + ->with($user) + ->will($this->returnValue($hasher)); + + $hasher->expects($this->once()) + ->method('hash') + ->with('password') + ->will($this->returnValue('hashedPassword')); + + $this->updater->hashPassword($user); + $this->assertSame('hashedPassword', $user->getPassword(), '->updatePassword() sets hashed password'); + $this->assertNull($user->getSalt()); + $this->assertNull($user->getPlainPassword(), '->updatePassword() erases credentials'); + } + + public function testUpdatePasswordWithLegacyHasher() + { + $hasher = $this->getMockBuilder(LegacyPasswordHasherInterface::class)->getMock(); + $user = new TestUser(); + $user->setPlainPassword('password'); + $user->setSalt('old_salt'); + + $this->passwordHasherFactory->expects($this->once()) + ->method('getPasswordHasher') + ->with($user) + ->will($this->returnValue($hasher)); + + $hasher->expects($this->once()) + ->method('hash') + ->with('password', $this->isType('string')) + ->will($this->returnValue('hashedPassword')); + + $this->updater->hashPassword($user); + $this->assertSame('hashedPassword', $user->getPassword(), '->updatePassword() sets hashed password'); + $this->assertNotNull($user->getSalt()); + $this->assertNull($user->getPlainPassword(), '->updatePassword() erases credentials'); + } + + public function testDoesNotUpdateWithEmptyPlainPassword() + { + $user = new TestUser(); + $user->setPassword('hash'); + + $user->setPlainPassword(''); + + $this->updater->hashPassword($user); + $this->assertSame('hash', $user->getPassword()); + } + + public function testDoesNotUpdateWithoutPlainPassword() + { + $user = new TestUser(); + $user->setPassword('hash'); + + $user->setPlainPassword(null); + + $this->updater->hashPassword($user); + $this->assertSame('hash', $user->getPassword()); + } +} diff --git a/tests/Util/UserManipulatorTest.php b/tests/Util/UserManipulatorTest.php new file mode 100644 index 0000000000..411f9f2e69 --- /dev/null +++ b/tests/Util/UserManipulatorTest.php @@ -0,0 +1,401 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Tests\Util; + +use FOS\UserBundle\FOSUserEvents; +use FOS\UserBundle\Tests\TestUser; +use FOS\UserBundle\Util\UserManipulator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; + +class UserManipulatorTest extends TestCase +{ + public function testCreate() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $user = new TestUser(); + + $username = 'test_username'; + $password = 'test_password'; + $email = 'test@email.org'; + $active = true; // it is enabled + $superadmin = false; + + $userManagerMock->expects($this->once()) + ->method('createUser') + ->will($this->returnValue($user)); + + $userManagerMock->expects($this->once()) + ->method('updateUser') + ->will($this->returnValue($user)) + ->with($this->isInstanceOf('FOS\UserBundle\Tests\TestUser')); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_CREATED, true); + + $requestStackMock = $this->getRequestStackMock(true); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->create($username, $password, $email, $active, $superadmin); + + $this->assertSame($username, $user->getUsername()); + $this->assertSame($password, $user->getPlainPassword()); + $this->assertSame($email, $user->getEmail()); + $this->assertSame($active, $user->isEnabled()); + $this->assertSame($superadmin, $user->isSuperAdmin()); + } + + public function testActivateWithValidUsername() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $username = 'test_username'; + + $user = new TestUser(); + $user->setUsername($username); + $user->setEnabled(false); + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $userManagerMock->expects($this->once()) + ->method('updateUser') + ->will($this->returnValue($user)) + ->with($this->isInstanceOf('FOS\UserBundle\Tests\TestUser')); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_ACTIVATED, true); + + $requestStackMock = $this->getRequestStackMock(true); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->activate($username); + + $this->assertSame($username, $user->getUsername()); + $this->assertTrue($user->isEnabled()); + } + + public function testActivateWithInvalidUsername() + { + $this->expectException(\InvalidArgumentException::class); + + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $invalidusername = 'invalid_username'; + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue(null)) + ->with($this->equalTo($invalidusername)); + + $userManagerMock->expects($this->never()) + ->method('updateUser'); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_ACTIVATED, false); + + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->activate($invalidusername); + } + + public function testDeactivateWithValidUsername() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $username = 'test_username'; + + $user = new TestUser(); + $user->setUsername($username); + $user->setEnabled(true); + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $userManagerMock->expects($this->once()) + ->method('updateUser') + ->will($this->returnValue($user)) + ->with($this->isInstanceOf('FOS\UserBundle\Tests\TestUser')); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_DEACTIVATED, true); + + $requestStackMock = $this->getRequestStackMock(true); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->deactivate($username); + + $this->assertSame($username, $user->getUsername()); + $this->assertFalse($user->isEnabled()); + } + + public function testDeactivateWithInvalidUsername() + { + $this->expectException(\InvalidArgumentException::class); + + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $invalidusername = 'invalid_username'; + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue(null)) + ->with($this->equalTo($invalidusername)); + + $userManagerMock->expects($this->never()) + ->method('updateUser'); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_DEACTIVATED, false); + + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->deactivate($invalidusername); + } + + public function testPromoteWithValidUsername() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $username = 'test_username'; + + $user = new TestUser(); + $user->setUsername($username); + $user->setSuperAdmin(false); + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $userManagerMock->expects($this->once()) + ->method('updateUser') + ->will($this->returnValue($user)) + ->with($this->isInstanceOf('FOS\UserBundle\Tests\TestUser')); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_PROMOTED, true); + + $requestStackMock = $this->getRequestStackMock(true); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->promote($username); + + $this->assertSame($username, $user->getUsername()); + $this->assertTrue($user->isSuperAdmin()); + } + + public function testPromoteWithInvalidUsername() + { + $this->expectException(\InvalidArgumentException::class); + + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $invalidusername = 'invalid_username'; + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue(null)) + ->with($this->equalTo($invalidusername)); + + $userManagerMock->expects($this->never()) + ->method('updateUser'); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_PROMOTED, false); + + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->promote($invalidusername); + } + + public function testDemoteWithValidUsername() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $username = 'test_username'; + + $user = new TestUser(); + $user->setUsername($username); + $user->setSuperAdmin(true); + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $userManagerMock->expects($this->once()) + ->method('updateUser') + ->will($this->returnValue($user)) + ->with($this->isInstanceOf('FOS\UserBundle\Tests\TestUser')); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_DEMOTED, true); + + $requestStackMock = $this->getRequestStackMock(true); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->demote($username); + + $this->assertSame($username, $user->getUsername()); + $this->assertFalse($user->isSuperAdmin()); + } + + public function testDemoteWithInvalidUsername() + { + $this->expectException(\InvalidArgumentException::class); + + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $invalidusername = 'invalid_username'; + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue(null)) + ->with($this->equalTo($invalidusername)); + + $userManagerMock->expects($this->never()) + ->method('updateUser'); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_DEMOTED, false); + + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->demote($invalidusername); + } + + public function testChangePasswordWithValidUsername() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + + $user = new TestUser(); + $username = 'test_username'; + $password = 'test_password'; + $oldpassword = 'old_password'; + + $user->setUsername($username); + $user->setPlainPassword($oldpassword); + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $userManagerMock->expects($this->once()) + ->method('updateUser') + ->will($this->returnValue($user)) + ->with($this->isInstanceOf('FOS\UserBundle\Tests\TestUser')); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_PASSWORD_CHANGED, true); + + $requestStackMock = $this->getRequestStackMock(true); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->changePassword($username, $password); + + $this->assertSame($username, $user->getUsername()); + $this->assertSame($password, $user->getPlainPassword()); + } + + public function testChangePasswordWithInvalidUsername() + { + $this->expectException(\InvalidArgumentException::class); + + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + + $invalidusername = 'invalid_username'; + $password = 'test_password'; + + $userManagerMock->expects($this->once()) + ->method('findUserByUsername') + ->will($this->returnValue(null)) + ->with($this->equalTo($invalidusername)); + + $userManagerMock->expects($this->never()) + ->method('updateUser'); + + $eventDispatcherMock = $this->getEventDispatcherMock(FOSUserEvents::USER_PASSWORD_CHANGED, false); + + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + $manipulator->changePassword($invalidusername, $password); + } + + public function testAddRole() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $username = 'test_username'; + $userRole = 'test_role'; + $user = new TestUser(); + + $userManagerMock->expects($this->exactly(2)) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $eventDispatcherMock = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + + $this->assertTrue($manipulator->addRole($username, $userRole)); + $this->assertFalse($manipulator->addRole($username, $userRole)); + $this->assertTrue($user->hasRole($userRole)); + } + + public function testRemoveRole() + { + $userManagerMock = $this->getMockBuilder('FOS\UserBundle\Model\UserManagerInterface')->getMock(); + $username = 'test_username'; + $userRole = 'test_role'; + $user = new TestUser(); + $user->addRole($userRole); + + $userManagerMock->expects($this->exactly(2)) + ->method('findUserByUsername') + ->will($this->returnValue($user)) + ->with($this->equalTo($username)); + + $eventDispatcherMock = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $requestStackMock = $this->getRequestStackMock(false); + + $manipulator = new UserManipulator($userManagerMock, $eventDispatcherMock, $requestStackMock); + + $this->assertTrue($manipulator->removeRole($username, $userRole)); + $this->assertFalse($user->hasRole($userRole)); + $this->assertFalse($manipulator->removeRole($username, $userRole)); + } + + /** + * @param string $event + * @param bool $once + */ + protected function getEventDispatcherMock($event, $once = true): MockObject&EventDispatcherInterface + { + $eventDispatcherMock = $this->getMockBuilder(EventDispatcherInterface::class)->getMock(); + + $eventDispatcherMock->expects($once ? $this->once() : $this->never()) + ->method('dispatch') + ->with($this->anything(), $event); + + return $eventDispatcherMock; + } + + /** + * @param bool $once + */ + protected function getRequestStackMock($once = true): MockObject&RequestStack + { + $requestStackMock = $this->getMockBuilder(RequestStack::class)->getMock(); + + $requestStackMock->expects($once ? $this->once() : $this->never()) + ->method('getCurrentRequest') + ->willReturn(null); + + return $requestStackMock; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000000..ddc92ccd89 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!($loader = @include __DIR__.'/../vendor/autoload.php')) { + echo <<<'EOT' +You need to install the project dependencies using Composer: +$ wget http://getcomposer.org/composer.phar +OR +$ curl -s https://getcomposer.org/installer | php +$ php composer.phar install --dev +$ phpunit +EOT; + exit(1); +}