Note that there are some explanatory texts on larger screens.

plurals
  1. POSymfony2 User roles vanish between Listener and UserProvider
    primarykey
    data
    text
    <p>I'm having trouble (again) with the security component of Symfony2.</p> <p>First thing first, there are severals objects involved in the process of authentication : </p> <ol> <li>SecurityFactoryInterface</li> <li>AbstractAuthenticationListener</li> <li>AuthenticationProviderInterface</li> <li>Custom Token (inherits from AbstractToken)</li> <li>UserProviderInterface</li> </ol> <h1>Flow</h1> <p>If I understand well, the aim of the Security factory is to configure the custom authentication. </p> <p>The Listener is the conductor of the authentication. Throught its <code>attemptAuthentication</code> method it captures the form submission (containing user's credentials) and tries to authenticate the user. Inside this method, the Listener will create an <code>AbstractToken</code> and then pass the token to the <code>authenticate</code> method of the <code>AuthenticationProvider</code>.</p> <p>After that, the <code>AuthenticationProvider</code> calls the <code>UserProvider</code> to retrieve the users data from a webservice or database etc...</p> <p>Once the <code>UserProvider</code> did its magic, it returns an User object to the <code>AuthenticationProvider</code>. </p> <p>The AuthenticationProvider then creates an new Token filled with the user retrieved by the <code>UserProvider</code> and then retunrs it to the <code>Listener</code>.</p> <p>After getting the new Token, the <code>Listener</code> does some unknown magic (I think it sets the token into the security context but I'm not sure).</p> <h1>Problem</h1> <p>At each steps I did a var_dump of my User object. The roles were set in every steps except in the "final steps": in the <code>Listener</code>.</p> <p>When the <code>Listener</code> retrieve the authenticated token from the <code>UserProvider</code>, <strong>user's roles are empty</strong>. I can't figure out why...</p> <h2>SecurityFactory</h2> <pre><code>class CompanyFactory implements SecurityFactoryInterface { public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) { $providerId = 'security.authentication.provider.company.'.$id; $container -&gt;setDefinition($providerId, new DefinitionDecorator('company.security.authentication.provider')) -&gt;replaceArgument(0, new Reference($userProvider)); $listenerId = 'security.authentication.listener.company.'.$id; $listener = $container-&gt;setDefinition($listenerId, new DefinitionDecorator('company.security.authentication.listener')); return array($providerId, $listenerId, $defaultEntryPoint); } public function getPosition() { return 'pre_auth'; } public function getKey() { return 'company'; } public function addConfiguration(NodeDefinition $node) { } } </code></pre> <h2>Listener</h2> <pre><code>class CompanyListener extends AbstractAuthenticationListener { // Custructor stuff removed protected function requiresAuthentication(Request $request) { if ($this-&gt;options['post_only'] &amp;&amp; !$request-&gt;isMethod('post')) { return false; } return parent::requiresAuthentication($request); } protected function attemptAuthentication(Request $request) { $username = trim($request-&gt;get($this-&gt;options['username_parameter'], null, true)); $password = $request-&gt;get($this-&gt;options['password_parameter'], null, true); $ip = $request-&gt;getClientIp(); $request-&gt;getSession()-&gt;set(SecurityContextInterface::LAST_USERNAME, $username); $authToken = $this-&gt;authenticationManager-&gt;authenticate(new CompanyUserToken($username, $password, $ip)); return $authToken; } } </code></pre> <h2>AuthenticationProvider</h2> <pre><code>class CompanyProvider implements AuthenticationProviderInterface { private $userProvider; public function __construct(UserProviderInterface $userProvider) { $this-&gt;userProvider = $userProvider; } public function authenticate(TokenInterface $token) { $user = $this-&gt;userProvider-&gt;loadUserByUsernamePassword($token-&gt;user, $token-&gt;getPassword(), $token-&gt;getIp()); $authenticatedToken = new CompanyUserToken($user-&gt;getUsername(), $user-&gt;getPassword(), $user-&gt;getIp(), $user-&gt;getRoles()); $authenticatedToken-&gt;setUser($user); return $authenticatedToken; } public function supports(TokenInterface $token) { return $token instanceof CompanyUserToken; } } </code></pre> <h2>Custom Token</h2> <pre><code>class CompanyUserToken extends AbstractToken { private $password; private $ip; public function __construct($username, $password, $ip, array $roles = array()) { parent::__construct($roles); $this-&gt;password = $password; $this-&gt;user = $username; $this-&gt;ip = $ip; // If the user has roles, consider it authenticated $this-&gt;setAuthenticated(count($roles) &gt; 0); } public function getCredentials() { return ''; } public function getPassword() { return $this-&gt;password; } public function getIp() { return $this-&gt;ip; } } </code></pre> <h2>User Provider</h2> <pre><code>class CompanyUserProvider implements UserProviderInterface { private $documentManager; public function __construct($doctrineMongoDB) { $this-&gt;doctrineMongoDB = $doctrineMongoDB; } public function loadUserByUsername($username) { // Not used but needed by the interface } public function loadUserByUsernamePassword($username, $password, $ip) { // Does the magic, retrieve user datas from DB. return $user; } public function refreshUser(UserInterface $user) { // Does nearly the same thing that the above method return $refreshedUser; } public function supportsClass($class) { return $class === 'Company\UserBundle\Document\User'; } } </code></pre> <h2>service.yml</h2> <pre><code>parameters: security.authentication.handler.class: Company\UserBundle\Security\Authentication\Handler\CompnayAuthenticationHandler company_user_provider.class: Company\UserBundle\Security\User\CompanyUserProvider services: security.authentication.handler: class: %security.authentication.handler.class% public: false arguments: [@router, @security.http_utils] company.security.authentication.provider: class: Company\UserBundle\Security\Authentication\Provider\CompanyProvider arguments: [@company_user_provider] company.security.authentication.listener: class: Company\UserBundle\Security\Firewall\CompanyListener arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils, "company", @security.authentication.handler, @security.authentication.handler, {}, @logger, @event_dispatcher, @user.service.captcha] company_user_provider: class: %company_user_provider.class% arguments: [@doctrine_mongodb] user.service.captcha: class: Company\UserBundle\Services\CaptchaService arguments: [@form.factory] </code></pre> <h2>security.yml</h2> <pre><code>jms_security_extra: secure_all_services: false expressions: true security: encoders: Company\UserBundle\Document\User: plaintext role_hierarchy: ROLE_VIP_USER: ROLE_USER ROLE_ADMIN: [ROLE_USER, ROLE_VIP_USER] providers: webservice: id: company_user_provider firewalls: company_secured: pattern: ^/ company: true anonymous: true form_login: login_path: login check_path: login_check post_only: true use_referer: false success_handler: security.authentication.handler failure_handler: security.authentication.handler logout: path: /logout target: login access_control: - { path: ^/admin, role: ROLE_ADMIN } </code></pre> <h1>Update</h1> <p>Here are some <code>var_dumps</code> to explain my problem : </p> <h3>UserProvider</h3> <p><code>var_dump($user);</code></p> <pre><code>object(Company\UserBundle\Document\User)[142] protected 'username' =&gt; string 'Supacoco' (length=13) protected 'roles' =&gt; array (size=1) 0 =&gt; string 'ROLE_ADMIN' (length=10) </code></pre> <h3>AuthenticationProvider</h3> <p><code>var_dump($authenticatedToken-&gt;getUser());</code></p> <pre><code>object(Company\UserBundle\Document\User)[142] protected 'username' =&gt; string 'Supacoco' (length=13) protected 'roles' =&gt; array (size=1) 0 =&gt; string 'ROLE_ADMIN' (length=10) </code></pre> <h3>Listener</h3> <p><code>var_dump($authToken-&gt;getUser());</code></p> <pre><code>object(Company\UserBundle\Document\User)[142] protected 'username' =&gt; string 'Supacoco' (length=13) protected 'roles' =&gt; array (size=0) empty </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload