security - Symfony2 User roles vanish between Listener and UserProvider -
i'm having trouble (again) security component of symfony2.
first thing first, there severals objects involved in process of authentication :
- securityfactoryinterface
- abstractauthenticationlistener
- authenticationproviderinterface
- custom token (inherits abstracttoken)
- userproviderinterface
flow
if understand well, aim of security factory configure custom authentication.
the listener conductor of authentication. throught attemptauthentication method captures form submission (containing user's credentials) , tries authenticate user. inside method, listener create abstracttoken , pass token authenticate method of authenticationprovider.
after that, authenticationprovider calls userprovider retrieve users data webservice or database etc...
once userprovider did magic, returns user object authenticationprovider.
the authenticationprovider creates new token filled user retrieved userprovider , retunrs listener.
after getting new token, listener unknown magic (i think sets token security context i'm not sure).
problem
at each steps did var_dump of user object. roles set in every steps except in "final steps": in listener.
when listener retrieve authenticated token userprovider, user's roles empty. can't figure out why...
securityfactory
class companyfactory implements securityfactoryinterface { public function create(containerbuilder $container, $id, $config, $userprovider, $defaultentrypoint) { $providerid = 'security.authentication.provider.company.'.$id; $container ->setdefinition($providerid, new definitiondecorator('company.security.authentication.provider')) ->replaceargument(0, new reference($userprovider)); $listenerid = 'security.authentication.listener.company.'.$id; $listener = $container->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) { } } listener
class companylistener extends abstractauthenticationlistener { // custructor stuff removed protected function requiresauthentication(request $request) { if ($this->options['post_only'] && !$request->ismethod('post')) { return false; } return parent::requiresauthentication($request); } protected function attemptauthentication(request $request) { $username = trim($request->get($this->options['username_parameter'], null, true)); $password = $request->get($this->options['password_parameter'], null, true); $ip = $request->getclientip(); $request->getsession()->set(securitycontextinterface::last_username, $username); $authtoken = $this->authenticationmanager->authenticate(new companyusertoken($username, $password, $ip)); return $authtoken; } } authenticationprovider
class companyprovider implements authenticationproviderinterface { private $userprovider; public function __construct(userproviderinterface $userprovider) { $this->userprovider = $userprovider; } public function authenticate(tokeninterface $token) { $user = $this->userprovider->loaduserbyusernamepassword($token->user, $token->getpassword(), $token->getip()); $authenticatedtoken = new companyusertoken($user->getusername(), $user->getpassword(), $user->getip(), $user->getroles()); $authenticatedtoken->setuser($user); return $authenticatedtoken; } public function supports(tokeninterface $token) { return $token instanceof companyusertoken; } } custom token
class companyusertoken extends abstracttoken { private $password; private $ip; public function __construct($username, $password, $ip, array $roles = array()) { parent::__construct($roles); $this->password = $password; $this->user = $username; $this->ip = $ip; // if user has roles, consider authenticated $this->setauthenticated(count($roles) > 0); } public function getcredentials() { return ''; } public function getpassword() { return $this->password; } public function getip() { return $this->ip; } } user provider
class companyuserprovider implements userproviderinterface { private $documentmanager; public function __construct($doctrinemongodb) { $this->doctrinemongodb = $doctrinemongodb; } public function loaduserbyusername($username) { // not used needed interface } public function loaduserbyusernamepassword($username, $password, $ip) { // magic, retrieve user datas db. return $user; } public function refreshuser(userinterface $user) { // same thing above method return $refresheduser; } public function supportsclass($class) { return $class === 'company\userbundle\document\user'; } } service.yml
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] security.yml
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 } update
here var_dumps explain problem :
userprovider
var_dump($user);
object(company\userbundle\document\user)[142] protected 'username' => string 'supacoco' (length=13) protected 'roles' => array (size=1) 0 => string 'role_admin' (length=10) authenticationprovider
var_dump($authenticatedtoken->getuser());
object(company\userbundle\document\user)[142] protected 'username' => string 'supacoco' (length=13) protected 'roles' => array (size=1) 0 => string 'role_admin' (length=10) listener
var_dump($authtoken->getuser());
object(company\userbundle\document\user)[142] protected 'username' => string 'supacoco' (length=13) protected 'roles' => array (size=0) empty
after digging lot, discovered "authenticationprovidermanager", after authenticating token, calls default user's method "erasecredentials".
the code below explains why user loses his/her roles after logging in.
public function erasecredentials() { $this->roles = array(); }
Comments
Post a Comment