<h1 dir="auto">Security - tagging UserCheckerInterface</h1>
<p dir="auto">When working with custom authentications, you most likely want to implement a custom UserChecker. The standard "form" supports this but doesn't really allow customization. "simple_form" does not have a standard implementation for this, sadly.</p>
<h1 dir="auto">Proposal</h1>
<p dir="auto">I came to the conclusion that it might be a lot easier to add some custom account/user validation. I was creating my own Authentication and user provider and stumbled against the choice to implement the Simple variant or the normal (older) variant.</p>
<p dir="auto">One of the main reasons I couldn't implement the existing variant, was due to the lack of customization of the UserChecker. Before loading the user, I had to know if the user was supposed to see a captcha or if the IP of the user was blocked. Before the authentication, I wanted to know if the account had any blocks. After the authentication, I wanted to load a secondary Entity that I would check some additional account statuses. I could not work around this with existing code so had to write a custom provider for this.</p>
<p dir="auto">A simple solution would be to allow the firewall options for a certain authentication method to allow passing the ID of a custom service. This would mean you'd get the following option:</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="security:
    firewall:
        secured_area:
            (simple_)form:
                user_checker_service: my.user_checker"><pre class="notranslate"><span class="pl-ent">security</span>:
    <span class="pl-ent">firewall</span>:
        <span class="pl-ent">secured_area</span>:
            <span class="pl-ent">(simple_)form</span>:
                <span class="pl-ent">user_checker_service</span>: <span class="pl-s">my.user_checker</span></pre></div>
<p dir="auto">As long as your service would implement the UserChecker Interface, this would all be possible. However, this isn't really flexible... You could do the following:</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="security:
    firewall:
        secured_area:
            (simple_)form:
                user_checkers: 
                    - my.user_checker.ip_block
                    - my.user_checker.captcha_block
                    - my.user_checker.banned_account
                    - my.user_checker.accepts_latest_eula
                    - vendor.bundle.extra_user_check"><pre class="notranslate"><span class="pl-ent">security</span>:
    <span class="pl-ent">firewall</span>:
        <span class="pl-ent">secured_area</span>:
            <span class="pl-ent">(simple_)form</span>:
                <span class="pl-ent">user_checkers</span>: 
                    - <span class="pl-s">my.user_checker.ip_block</span>
                    - <span class="pl-s">my.user_checker.captcha_block</span>
                    - <span class="pl-s">my.user_checker.banned_account</span>
                    - <span class="pl-s">my.user_checker.accepts_latest_eula</span>
                    - <span class="pl-s">vendor.bundle.extra_user_check</span></pre></div>
<p dir="auto">With this solution, you would be able to add extra checks on demand. It would be a really nice addition to work with the Simple provider as well, but that would introduce BC breaks, but solve a lot of nasty code.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class MyBannedAccountUserChecker implements
    PreUserLoadCheckerInterface,
    PreAuthUserCheckerInterface, 
    PostAuthUserCheckerInterface
{
    public function checkPreUserLoad()
    {
        // additional checks BEFORE the user is being load...
        // good example is to check IP block, captcha's, other requirements
    }

    public function checkPreAuth(UserInterface $user)
    {
        if (!$user instanceof MyUser) {
            return;
        }

        // .. get the entity where ever you store if the user is banned

        if ($banned) {
            throw new AccountBannedException($user); // extends AuthenticationException
        }
    }

    public function checkPostAuth(UserInterface $user) { ... }
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">MyBannedAccountUserChecker</span> <span class="pl-k">implements</span>
    <span class="pl-v">PreUserLoadCheckerInterface</span>,
    <span class="pl-v">PreAuthUserCheckerInterface</span>, 
    <span class="pl-v">PostAuthUserCheckerInterface</span>
{
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">checkPreUserLoad</span>()
    {
        <span class="pl-c">// additional checks BEFORE the user is being load...</span>
        <span class="pl-c">// good example is to check IP block, captcha's, other requirements</span>
    }

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">checkPreAuth</span>(<span class="pl-smi"><span class="pl-smi">UserInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>user</span>)
    {
        <span class="pl-k">if</span> (!<span class="pl-s1"><span class="pl-c1">$</span>user</span> instanceof <span class="pl-v">MyUser</span>) {
            <span class="pl-k">return</span>;
        }

        <span class="pl-c">// .. get the entity where ever you store if the user is banned</span>

        <span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>banned</span>) {
            <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-v">AccountBannedException</span>(<span class="pl-s1"><span class="pl-c1">$</span>user</span>); <span class="pl-c">// extends AuthenticationException</span>
        }
    }

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">checkPostAuth</span>(<span class="pl-smi"><span class="pl-smi">UserInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>user</span>) { <span class="pl-c1">.</span>.<span class="pl-c1">.</span> }
}</pre></div>
<p dir="auto">I think this example should demonstrate exactly what I mean. This will give you flexibility while it will also make it easier to configure the security component user checks. I have noticed examples of people who are totally lost in this component and have no clue where to add the custom checks for this. As far as I know it's only possible to modify the current existing UserChecker by a CompilerPass. It's tightly coupled with the "form" login method and the only available implementation works only with the AdvancedUserInterface (which imo should be rm -rf'd).</p>
<p dir="auto">To prevent backwards incompatibility issues, the current UserCheckerInterface should not be removed, although the splitting of pre and post auth checks can cause a minor inconvenience.</p>
<p dir="auto">Hi everyone, I'm working on a fresh Symfony 2.0.8 project.</p>
<p dir="auto">UniqueEntity constraint seem to being not working. I'm using yml validation file:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="NicaStaff\IQCoreBundle\Entity\User:
    constraints:
        - \Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity: { fields: email, message: This email is already used }
    properties:
        first_name:
            - NotBlank: { groups: [default] }"><pre class="notranslate"><span class="pl-v">NicaStaff</span>\<span class="pl-v">IQCoreBundle</span>\<span class="pl-v">Entity</span>\<span class="pl-v">User</span>:
    constraints:
        - \<span class="pl-v">Symfony</span>\<span class="pl-v">Bridge</span>\<span class="pl-v">Doctrine</span>\<span class="pl-v">Validator</span>\<span class="pl-v">Constraints</span>\<span class="pl-v">UniqueEntity</span>: { fields: email, message: <span class="pl-v">This</span> email is already used }
    properties:
        first_name:
            - <span class="pl-v">NotBlank</span>: { groups: [default] }</pre></div>
<p dir="auto">The assertion is being skipped, so I got a MySQL duplicated entry error.</p>
<p dir="auto">In the doctrine mapping 'email' is unique and string(255) (Hopefully this has nothing to do with the issue).</p>
<p dir="auto">Following the stack and messing up with files and code I found that UniqueEntity Constraint is being executed but the validatedBy() method is not being overrided.</p>
<p dir="auto">In the other hand, UniqueEntityValidator is never being executed.</p>
<p dir="auto">As opposite, a working Symfony 2.0.5 previous project the UniqueEntity assert is working like a charm. If I mess up with the sames files it is clear that UniqueEntityValidator is being executed and the UniqueEntity::validatedBy() is being correctly override.</p>
<p dir="auto">Any workaround for this? Did I miss something?</p>
<p dir="auto">Regards.</p>