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