Buy

Configuring the Encoder in security.yml

Let's set some plain passwords! Where? In our fixtures! Open up fixtures.yml and scroll down. In theory, all we need to do is set the plainPassword property. The rest should happen auto-magically.

Add plainPassword: and we'll keep with iliketurtles, because I've gotten good at typing that. And turtles are cool:

27 lines src/AppBundle/DataFixtures/ORM/fixtures.yml
... lines 1 - 22
AppBundle\Entity\User:
user_{1..10}:
... line 25
plainPassword: iliketurtles

Change over to your terminal and load your fixtures:

./bin/console doctrine:fixtures:load

Explosion!

No encoder has been configured for AppBundle\Entity\User

This is basically Symfony's way of saying:

Ryan, you didn't tell me how you want to encode the passwords. I can't read your mind - I'm just a PHP framework.

Remember how I kept saying we would encrypt the passwords with bcrypt? Do you remember actually configuring that anywhere? Nope! We need to do that.

Open security.yml. Add an encoders key, then AppBundle\Entity\User: bcrypt:

33 lines app/config/security.yml
... lines 1 - 2
security:
encoders:
AppBundle\Entity\User: bcrypt
... lines 6 - 33

If you want, you can configure a few other options, but this is good enough.

Now Symfony knows how to encrypt our passwords. Try the fixtures again:

./bin/console doctrine:fixtures:load

No errors! So, that probably worked. Let's use it!

Checking the Encoded Password

In LoginFormAuthenticator, we can finally add some real password checking! How? By using that same UserPasswordEncoder object.

Head back up to __construct and add a new UserPasswordEncoder argument. I'll use my Option+Enter shortcut to setup that property for me:

80 lines src/AppBundle/Security/LoginFormAuthenticator.php
... lines 1 - 10
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
... lines 12 - 15
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 18 - 20
private $passwordEncoder;
public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router, UserPasswordEncoder $passwordEncoder)
{
... lines 25 - 27
$this->passwordEncoder = $passwordEncoder;
}
... lines 30 - 78
}

And because we're using autowiring for this service, we don't need to change anything in services.yml.

Now, in checkCredentials(), replace the if statement with if ($this->passwordEncoder->isPasswordValid()) and pass that the User object and the plain-text password:

80 lines src/AppBundle/Security/LoginFormAuthenticator.php
... lines 1 - 15
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 18 - 58
public function checkCredentials($credentials, UserInterface $user)
{
... lines 61 - 62
if ($this->passwordEncoder->isPasswordValid($user, $password)) {
return true;
}
... lines 66 - 67
}
... lines 69 - 78
}

That'll take care of securely checking things.

Let's try it out: head to /login. Use weaverryan+1@gmail.com and iliketurtles. We're in! Password system check.

Ok team, that's it for authentication. You can build your authenticator to behave however you want, and you can even have multiple authenticators. Oh, and if you do want to use any of the built-in authentication systems, like the form_login key I mentioned earlier - that's totally fine. Guard authentication takes more work, but has more flexibility. If you want another example, we created a cool JSON web token authenticator in our Symfony REST series.

Now, let's start locking down the system.

Leave a comment!

  • 2017-08-02 Victor Bocharsky

    Hey Nina,

    I think you just forgot to implement "Doctrine\Common\EventSubscriber" interface in your HashPasswordListener... or, maybe you just forget to use its namespace. Please, make sure you implement that interface.

    Cheers!

  • 2017-08-01 Nina

    Thanks for explanation!
    Now I have another error when
    write in the console php bin/console doctrine:fixtures:load

    Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Type error: Argument 1 passed to Doctrine\Common\EventManager::addEventSubscriber() must implement interface Doctrine\Common\EventSubscriber, instance of AppBundle\Doctrine\HashPasswordListener given, called in C:\OpenServer\domains\startsecurity\var\cache\dev\appDevDebugProjectContainer.php on line 695 in C:\OpenServer\domains\startsecurity\vendor\doctrine\common\lib\Doctrine\Common\EventManager.php:137
    Stack trace:
    #0 C:\OpenServer\domains\startsecurity\var\cache\dev\appDevDebugProjectContainer.php(695): Doctrine\Common\EventManager->addEventSubscriber(Object(AppBundle\Doctrine\HashPasswordListener))
    #1 C:\OpenServer\domains\startsecurity\vendor\symfony\symfony\src\Symfony\Component\DependencyInjection\Container.php(274): appDevDebugProjectContainer->getDoctrine_Dbal_DefaultConnectionService()
    #2 C:\OpenServer\domains\startsecurity\var\cache\dev\appDevDebugProjectContainer.php(757): Symfony\Component\DependencyInjection\Container->get('doctrine in C:\OpenServer\domains\startsecurity\vendor\doctrine\common\lib\Doctrine\Common\EventManager.php on line 137

  • 2017-08-01 weaverryan

    Hi Nina!

    Ah, YAML :). Here are the most important things to understand with YAML:

    1) Indentation is *important*. When you indent, you're creating a key under the other key. For example, see how "encoders" is indented 4 spaces? That means it is a *child* of "security". And the AppBundle\Entity\User line is indented for *more* spaces. So it is a child of "encoders". Watch your indentation carefully: make sure you're always indenting exactly 4 spaces. This is what Victor Bocharsky was talking about in his message :).

    2) YAML is also about creating key value pairs, which are separated by a ":" and a space. For example, above, we're setting some key "AppBundle\Entity\User" to the value "bcrypt"

    There are many other weird syntaxes with YAML, which you'll learn little-by-little :). Until you get more comfortable, just be very careful with your spaces - they're important! We have code blocks on each page - you can always copy our code if you're having issues. Ultimately, YAML is read and a big array is created from it. Then Symfony uses that array.

    About your error:

    > Unrecognized option "0" under "security.firewalls.access_control"

    Compare your security.yml with the one on the next page - look at the first code block: https://knpuniversity.com/s.... You can click the arrow icon in the upper left to view the *entire* file :).

    If you expand the entire security.yml file in that code block, you'll see that the "firewalls" key and the "access_control" key are both indented 4 spaces. So, they are both children under "security" and they are siblings of each other. Your error tells me that you have extra indentation on your "access_control" line, so YAML *thinks* that access_control lives *under* firewalls... instead of on the same level. So, check your indentation!

    Cheers!

  • 2017-08-01 Nina

    Thanks for help.
    I don't really understand how .yml file works?
    How they uses in the symfony?
    And how the correct markup should looks like? What rules for correct .yml file?

    And I have another error when I write in the console
    php bin/console doctrine:fixtures:load

    Unrecognized option "0" under "security.firewalls.access_control"

  • 2017-08-01 Victor Bocharsky

    Yep, here it is:


    security:
    encoders:
    AppBundle\Entity\User: bcrypt
    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    providers:
    our_users:
    entity: { class: AppBundle\Entity\User, property: email }

    Notice 1 extra space before "encoders:" and "providers" - there should be 4 spaces, not 5. Indentation is very important in YAML syntax, be careful ;)

    P.S. "path:" has incorrect indentation too :) Remove 1 extra space before it.

    Cheers!

  • 2017-08-01 Nina

    security.yml


    # http://symfony.com/doc/current/book/security.html
    security:
    encoders:
    AppBundle\Entity\User: bcrypt
    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    providers:
    our_users:
    entity: { class: AppBundle\Entity\User, property: email }

    firewalls:
    # disables authentication for assets and the profiler, adapt it according to your needs
    dev:
    pattern: ^/(_(profiler|wdt)|css|images|js)/
    security: false

    main:
    anonymous: ~
    guard:
    authenticators:
    - app.security.login_form_authenticator
    logout:
    path: /logout
  • 2017-08-01 Victor Bocharsky

    Hey Nina,

    As you can see from the error message, your "app/config/security.yml" file does not contain valid YAML syntax. I suppose you have indentation problems in it (Indentation problem at line 8 (near " providers:")), but to say more we need to see your security.yml content. Could you share it with us?

    Cheers!

  • 2017-08-01 Nina

    Hello
    I have problem when
    type php bin/console doctrine:fixtures:load

    [Symfony\Component\Config\Exception\FileLoaderLoadException]
    The file "C:\OpenServer\domains\startsecurity\app/config\security.yml" does not contain
    valid YAML in C:\OpenServer\domains\startsecurity\app/config\security.yml (which is bein
    g imported from "C:\OpenServer\domains\startsecurity\app/config\config.yml").

    [Symfony\Component\DependencyInjection\Exception\InvalidArgumentException]
    The file "C:\OpenServer\domains\startsecurity\app/config\security.yml" does not contain
    valid YAML.

    [Symfony\Component\Yaml\Exception\ParseException]
    Indentation problem at line 8 (near " providers:").

    Why I have this error and how to fix this?

  • 2017-06-15 Diego Aguiar

    Hey Hesham,

    Thanks for sharing your problem and solution, definitely it will help someone :)

    Cheers!

  • 2017-06-15 Hesham

    Hello,

    As many of you, i had Column 'password' cannot be null as error when i tried to load fixtures.

    After hours of lookign for the source of the problem and comparing all files, i found out that i did not right the tag name correctly in services.yml.

    So if you are having the same problem you need to check this twice. i hope it will help someone in the future.

    Nice Tutorial Ryan i like the way you explain your work. keep going

    Cheers from France!

  • 2017-05-18 weaverryan

    Dude, so awesome! Thanks for the nice words - made my afternoon :)

  • 2017-05-17 Dennis

    Hi weaverryan

    Thanks! I really love how you explain things. Not just this has to be done like this, you really explain it WHY you have to do it, and what is actually going on when you do it. I used to think it was all *magic* what was going on, but you convinced me otherwise. And I can totally follow you (I'm Dutch).

    So thanks man! I really like it.

    Cheers!

  • 2017-05-17 weaverryan

    Yo Dennis!

    Nice hack :). If you're not using fixtures... then there needs to be *some* way of adding users... like a registration form! We add that later - https://knpuniversity.com/s... - but basically, the code to encode the user's password is what you have here :).

    Now, keep rolling!!!

    Cheers!

  • 2017-05-17 Dennis

    I've made something already. Just did a little "hack". On my indexAction I wrote:

    $userRepository = $this->getDoctrine()->getRepository('AppBundle:User');
    $user = $userRepository->find(1);
    $user->setPlainPassword('mySuperSecurePassword');

    $em = $this->getDoctrine()->getManager();
    $em->persist($user);
    $em->flush();

    and removed it again. :)

  • 2017-05-17 Dennis

    Hi again Ryan.

    I do not use fixtures. How can I set my password then?

    Hope to hear from you soon, because I'm on a roll here! xD

    Greetz Dennis

  • 2017-05-02 sokphea chea (ជា សុខភា)

    thank you, now it's fixed :D

  • 2017-05-02 sokphea chea (ជា សុខភា)

    It's fix now, Victor Bocharsky. :D I can continue my learning journey now. thank you <3 Knp

  • 2017-05-01 Victor Bocharsky

    Hey Chea,

    I suppose it's due to this problem: https://github.com/sokphea-... :)
    Try to merge it and the problem will be fixed I think.

    Cheers!

  • 2017-05-01 Victor Bocharsky

    Hey Chea,

    You've missed a return statement in getPassword() getter, see my PR: https://github.com/sokphea-...

    Cheers!

  • 2017-05-01 sokphea chea (ជា សុខភា)

    Victor Bocharsky $this->passwordEncoder->isPasswordValid($user, $password) always return false even it's the correct password from fixture.

  • 2017-05-01 sokphea chea (ជា សុខភា)

    weaverryan Victor Bocharsky I got the same error as somecallmetim27 mention 'Invalid credentials.''. I can't figure out what wrong. I double check on the code, but it didn't help. Could you check on this? I don't want to replace the code, and keeping going without solving this. Here the code: https://github.com/sokphea-...

  • 2017-03-29 Diego Aguiar

    Hey Luc Hamers,
    Can you show me how your user class looks like ?
    Make sure that it implements the Serializable interface
    Maybe clearing the cache can help you, sometimes weird things happens because of the cache

    Have a nice day!

  • 2017-03-28 Luc Hamers

    I have a problem with the serialization of my user object. At some point, my user object is serialized when I call a page via ajax after I have logged in. I then get the error message "Serialization of 'SimpleXMLElement' is not allowed". Here is a part of the stack trace:

    1. in vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php at line 154 
    2. at SplObjectStorage->serialize()
    3. at SplObjectStorage->serialize()
    4. at serialize (array(object(MemberEntry), true, array(object(Role)), array())) in vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php at line 154
    5. at AbstractToken->serialize() in vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Token/PostAuthenticationGuardToken.php at line 79
    6. at PostAuthenticationGuardToken->serialize()
    7. at serialize (object(PostAuthenticationGuardToken)) in vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ContextListener.php at line 129
    8. at ContextListener->onKernelResponse (object(FilterResponseEvent), 'kernel.response', object(TraceableEventDispatcher))
    9. at call_user_func (array(object(ContextListener), 'onKernelResponse'), object(FilterResponseEvent), 'kernel.response', object(TraceableEventDispatcher))

    Calling the same page without logging in works fine.

    Now when I add __sleep() to the user object and put in some of the user properties, then this error disappears, but also the login stops working: password is checked -> redirection to the default page is called -> the user object is not found in the session (I think) -> redirection back to the login form.

    I currently have no idea, why this fails, where the XML comes from and/or which parts of the user entity I have to serialize with the __sleep function to get both ajax and login working.

  • 2017-03-22 weaverryan

    Hey!

    Awesome! Yea, that's one of the easiest things to do - if you have a little typo in the right place, you won't get an error, but your listener will never be called!

    Now, about those 3 mysterious lines :). For some reason (there may be a good reason, but I don't know what it is), when you make a change to your entity in a preUpdate listener... Doctrine ignores it. Basically, it's already decided what has changed on the entity (the changeset) and will use that for the query. These three lines force it to look again and notice the new change. There's actually a simpler (but more limited), way of doing this. The $args argument is actually a PreUpdateEventArgs instance (this is a sub-class of the LifecycleEventArgs). This has a setNewValue() method, which is the *intended* way for you to change new values. But, it doesn't allow you to set a value on a field that wasn't previously changed. That's the bummer. Check out this answer (and comment below it): http://stackoverflow.com/qu...

    Cheers!

  • 2017-03-20 karolos

    Hey @weaverryan ,

    Thank for your response ! It was a long journey 'till I figure out that I had a little mistake and the listener was never fired up.

    Can you please explain a little bit more the 3 lines you paste inside the listener ?


    $em = $args->getEntityManager();
    $meta = $em->getClassMetadata(get_class($entity));
    $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
  • 2017-03-04 weaverryan

    Hey @karolos!

    Hmm! As long as you're setting plainPassword in your fixtures file, and have the Doctrine listener code setup correctly, the password *should* be encoded automatically. But, this does seem to be an area where it's easy to make a small, but difficult-to-debug mistake!

    Can you create a gist (https://gist.github.com/) that shows your fixtures.yml, User.php, HashPasswordListener.php and services.yml files? You're right that you shouldn't pass password directly in the fixtures - we need to encode that guy!

    Cheers!

  • 2017-03-03 karolos

    Should i pass password to fixtures.yml ?but then the password isnt hashed

  • 2017-03-03 karolos

    I still getting the same error. I used plainPassword on the fixtures.yml

    also

    public function setPlainPassword($plainPassword)
    {
    $this->plainPassword = $plainPassword;
    // guarantees that the entity looks "dirty" to doctrine
    // when changing the plainPassword
    $this->password = null;
    }

  • 2017-02-20 Victor Bocharsky

    Hey Matt,

    Great! Thanks for sharing it ;)

    Cheers!

  • 2017-02-17 Matt Johnson

    Had the same issue. Turns out I forgot the Entity use statment in AppBundle\Doctrine\HashPasswordListener

    I added the use statement for the User entity and the error cleared up:
    use AppBundle\Entity\User;

  • 2017-01-02 Laurence Tremblay

    I am having the exact same problem. Eveything has worked fine except when I tried login in from the loaded Fixtures... then I always get the Invalid credidential error. Weird...

    Very nice tutorial BTW !

  • 2016-12-23 Victor Bocharsky

    Hey Nash,

    The problem actually isn't in the $user variable but in variable from which you call isPasswordValid() method, i.e. it's the $this->passwordEncoder in your case. I bet if you dump $this->passwordEncoder property - you will get null. So you need to double check that you really injecting UserPasswordEncoder service and setting it properly to the $this->passwordEncoder somewhere in the __construct() method. Let me know if it doesn't help you.

    Cheers!

  • 2016-12-23 Nash Delos Santos

    I've got this error Error: Call to a member function isPasswordValid() on a non-object. I dumped the $user and password variables and can confirm that the $user is an object, $password also holds the value typed in from the form, so not sure what I did wrong.

  • 2016-12-09 weaverryan

    Well, a big thumbs up for fixing it :). But I hate when things are mysterious... and that is *really* strange. But, unless you see something else weird, ignore it and keep going ;). Glad it worked out!

  • 2016-12-09 Victor Bocharsky

    Haha, sometimes simple mistakes are very difficult to reveal. But they give us a good experience ;)

    Cheers!

  • 2016-12-09 somecallmetim27

    I actually had cleared the cache several times. I reloaded the database, manually went into the database to check that things looked the way they should, etc. Maybe someday when I die I can get God to show me where the error was. Perhaps some mischievous spirit was just screwing with me? :-/

  • 2016-12-08 Victor Bocharsky

    Wow, you have done a huge work restarting the tutorial from scratch :) Anyway, great that it works now! Well, probably the problem just was in cache, I always clear the cache first in any weird behavior - in most cases it helps ;)

    Cheers!

  • 2016-12-08 somecallmetim27

    https://goo.gl/images/pNq5Sh (no, not spam or hacked account, etc)

  • 2016-12-08 somecallmetim27

    I never found the error, but I did fix it. I started the tutorial from scratch in a completely new project. I downloaded and installed the 'start' code and worked from there. This time, everything worked. Then I systematically went through and replaced every file from this tutorial in the non-working project with the corresponding file from new one. I still ran into the same problem. Finally, after double checking that I hadn't missed any random files, I just copied the src folder from the working project and pasted it into the non-working project. Wala, perfection! I checked Git to see if I could tell the difference, but other than some minor differences in previous non-related code (ie things like use statements in a different order in GenusAdminController), nothing jumped out at me.

    Sooooo yeah... I don't know :P

  • 2016-12-07 weaverryan

    You can also - for now (to rule it out as a problem) - completely remove the $this->password = null line from your User class - this is only a "fix" for an edge case (the entire User not saving) that only affects updates, and only when the password is the only field that changed. Since we're inserting new users, this line doesn't help us (so removing it temporarily might clear things up).

    Feel free to also post some code! We might be able to spot an issue if there is one!

  • 2016-12-07 Victor Bocharsky

    Hey somecallmetim27,

    Let's just try to add "die('HashPasswordListener debugging')" statement in HashPasswordListener class, I mean somewhere in HashPasswordListener::prePersist() since we create new users, not update them. And let me know do you see this debug text in console?

    Cheers!

  • 2016-12-07 Victor Bocharsky

    Hey somecallmetim27,

    Good debugging, now we know that authentication works, so you just have problem with password validation.

    Cheers!

  • 2016-12-07 Victor Bocharsky

    Hey somecallmetim27,

    No, this warning relates to the translations and can't cause your problem with authentication.

    Cheers!

  • 2016-12-06 somecallmetim27

    The frustrating thing is, since load fixtures is something that is done from the console, I can't see how to tell if our HashPasswordListener class is working. I can't see anyway to debug that.

  • 2016-12-06 somecallmetim27

    Ok, this goes back to the problem that several other people were having before. When you have this

    // forces the object to look "dirty" to Doctrine. Avoids
    // Doctrine *not* saving this entity, if only plainPassword changes
    $this->password = null;

    in setPlainPassword, the fixtures won't load and you get that 'password' cannot be null error. If you put those lines at the end of the setPassword method instead, the fixtures will load, but then I get 'Invalid credentials' when I put in what should be the correct password in.

    My take on this is that the 'iliketurtles' plainPassword from fixtures.yml isn't getting encrypted and presented to the DB for persisting. The only reason it works when you move the above bit of code around is because now the password is set to 'null' and, therefore, is no longer blank.

  • 2016-12-06 somecallmetim27

    Just noticed I'm getting this from the debug toolbar. Could this be the problem?

    These messages are not available for the given locale and cannot be found in the fallback locales. Add them to the translation catalogue to avoid Symfony outputting untranslated contents.

    Locale Domain Times used Message ID Message Preview
    en messages 1 Username Username
    en messages 1 Password Password

  • 2016-12-06 somecallmetim27

    That's fine, but the code still doesn't work. Just to see what would happen, I changed this

    if ($this->passwordEncoder->isPasswordValid($user, $password)) {
    return true;
    }

    in the checkCredentials function to this

    if (!$this->passwordEncoder->isPasswordValid($user, $password)) {
    return true;
    }

    and suddenly I was allowed to login. Which means isPasswordValid is returning a false value when it should be returning true. And I have no idea why.

  • 2016-12-06 Victor Bocharsky

    Hey somecallmetim27 ,

    No, this value is returned as encoded from the DB and keeps to be encoded all time in your application. Actually, you can't decoded this value if you encoded it with one-way algorithm like md5, sha1, bcrypt, etc.. Instead of decoding it, which is impossible, Symfony just encode a password, which user inputted and then compare the result hash of inputted password with one from the DB. If hashes is matched - then user inputted a valid password. And Symfony has a service which helps you to check if inputted user password is valid, i.e. matches with the one from DB - it's instance of UserPasswordEncoder class. We require this object in LoginFormAuthenticator::__construct() method and then call "$this->passwordEncoder->isPasswordValid(...)" on it. So you need to pass a user object to this method, and unencoded password from user input. Then the password encoder will do the remaining work for you: encode input password with the same algorithm and check if it matches with the one in DB as I said in the beginning. If input password is valid - isPasswordValid () method will return true.
    Does it make sense for you?

    Cheers!

  • 2016-12-06 somecallmetim27

    I keep getting "Invalid credentials." no matter what I do. I keep going over the code for this section, I've gone through my code looking for errors, I can't find anything I've missed in this lesson and I don't know how to troubleshoot further. That being said, this method,

    public function getPassword()
    {
    return $this->password;
    }

    looks funny to me. Shouldn't there be something decoding what was stored? Did I miss something I a previous lesson?

  • 2016-12-06 somecallmetim27

    I had same issue. Your fix saved me who knows how much time. :)

  • 2016-10-31 Victor Bocharsky

    Ah, very nice catch! Actually, it's the most common and tricky error - do not mark object as "dirty" for Doctrine.

    No problem, I was glad to give you some tips ;)

  • 2016-10-31 Daan Hage

    OMG!!! I added this line:
    // forces the object to look "dirty" to Doctrine. Avoids
    // Doctrine *not* saving this entity, if only plainPassword changes
    $this->password = null;

    to setPassword instead of setPlainPassword!
    Very stupid, disadvantage of going to quickly through the tutorials.
    Sorry for the trouble and thanks for your time and effort Victor!

  • 2016-10-31 Victor Bocharsky

    So when you set a password in fixtures.yml - does it help you?

    Yeah, I agree with you, sometimes its difficult to debug it. But Symfony has a nice web debug toolbar, where you can see triggered services and so many other useful things. Also Blackfire.io and Xdebug can help with debugging more difficult things. But in the most cases I just use dump()/var_dump()/die() functions for debugging.

    Cheers!

  • 2016-10-31 Daan Hage

    If I look at the fictures.yml files, password is never set. Although we've created a doctrine listener which should transform the plainpassword into the encoded password. However, it looks like it never gets triggered.

    So far this hits exactly a reason why I don't like frameworks (although it has a lot of plus-sides and is awesome!), once you have a error thrown and it's traced through a lot of classes, I find it very hard to debug.

    How do you solve it? Were do you start? Dop I have to right all kinds of classes so I override the defaults stuff?

  • 2016-10-31 Victor Bocharsky

    Hey Daan,

    So the question is why does your password empty? If you answer it - you'll probably solve this problem. Could you debug it?

    Cheers!

  • 2016-10-31 Daan Hage

    I have the same error:
    SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null

    I also use symfony 3. Please help :(

  • 2016-10-16 weaverryan

    Cool! Really happy the entity-no-logic thing makes sense :). Honestly, this is one of the times when that "limitation" is the ugliest - in 99% of the use-cases it really makes for much more predictable and well-organized code. I really think this is one of the exceptional cases.

    Cheers!

  • 2016-10-16 Johan

    Ok cool, at first I was a bit disappointed about not being able to use the services inside my entity but after doing some research (got to this extremely elaborate stackoverflow answer: http://stackoverflow.com/a/... I guess it makes sense. It prevents me from doing things that I shouldn't be doing inside my entity which would probably include anything that is not related to storing or retrieving data (or encoding passwords).

    I think I am convinced that the listener is a pretty good solution :) The entity listener sounds cool, I'll definitely have a look at that later.

    P.S. I agree that I shouldn't worry too much about performance in the beginning, but if I can grab some performance for free, I'll take it :)

    Thanks for your help!

  • 2016-10-16 weaverryan

    Hi Johan!

    I actually agree in principle, but what you're proposing isn't quite possible: you don't have access to the service container (or any of your services) from within the proposed setPlainPassword(). In other words: there's no way for us to do the encoding login inside of this method (or inside of the User class at all). So, the encoding *must* be done external to User, which really leaves us with 2 options:

    1) Use a listener pattern like we are, which definitely has complexity, but has the advantage that it just "works" after it's setup
    2) Don't have a plainPassword property (or setter) at all. And instead, whenever you want to set the password, you encode it outside of the class and then call setPassword() with the encoded password (this could be centralized in a service of course, to avoid code duplication). This is simpler, but not as automatic - e.g. you would need to do a bit more work to get this to work with Alice fixtures (which, without any work, just call the setters and save).

    I've had a similar conversation about this feature with a colleague of mine, which also thinks that this is too complex. But, there's not really a super easy way to do it. This is also modeled off of FOSUserBundle, which uses this listener approach (the advantage there, is that you don't need to actually do the work of setting up the listener).

    P.S. On the performance note, I don't often worry about performance until later, and I doubt that this would be your bottleneck. BUT, there is an alternative thing called an "entity listener" - they're a bit newer, but effectively allow you to have a listener on only one entity. http://docs.doctrine-projec... and https://symfony.com/doc/cur...

    Cheers!

  • 2016-10-16 Johan

    I feel like this is more complicated than it needs to be. Why not do this:

    1. Create a "password" field and persist it with Doctrine.
    2. Create a getter (getPassword()) for this field. Don't create a (public) setter, it doesn't make any sense in my opinion.
    3. Create a setter for plain password (setPlainPassword()) which takes the passed plain text, encodes it and stores it in the "password" field. So, NO "plainPassword" field. The argument directly gets encoded and stored in the "password" field. And of course, there is no getPlainPassword method.

    I think it has two advantages:
    - it's simpler. There are only a few lines of extra code in the User class.
    - it is probably faster. There is no event listener that gets called on EVERY entity update. In a big application I can imagine millions of updates per second of which only a small proportion consists of password sets/updates. So, this event listener gets called millions of times, for no reason.

  • 2016-08-29 Lee Ravenberg

    Thanks, I had the same issue.

  • 2016-08-22 James Hogan

    I am not able to get past ...:fixtures:load

    NOTE...using Symfony 3.1.3

    [Doctrine\DBAL\Exception\NotNullConstraintViolationException]
    An exception occurred while executing 'INSERT INTO user (email, password, roles) VALUES (?, ?, ?)' with params ["weaverryan+1@gmail.com", null, "[\"ROLE_ADMIN\"]"]:
    SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null

    [Doctrine\DBAL\Driver\PDOException]
    SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null

    [PDOException]
    SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null

  • 2016-07-27 weaverryan

    Cheers - thanks for sharing Andjii! No better way to learn than debugging ;)

  • 2016-07-27 Andjii

    it caused because, I implemented EventSubscriberInterface instead of EventSubscriber, so that broke my app. I hope, this information may be useful for someone else who will have this course=))

  • 2016-07-27 Andjii

    I got the error after all acts which were described in video...

    ContextErrorException in EventManager.php line 137:Catchable Fatal Error: Argument 1 passed to Doctrine\Common\EventManager::addEventSubscriber() must be an instance of Doctrine\Common\EventSubscriber, instance of AppBundle\Doctrine\HashPassListener given, called in \var\cache\dev\appDevDebugProjectContainer.php on line 665 and defined

    What have I done wrong?

    (I use Symfony 3.1)