Forums › Forums › OroPlatform › OroPlatform – Programming Questions › How to switch user (ImpersonationToken)
This topic contains 5 replies, has 2 voices, and was last updated by cardiac 7 years, 8 months ago.
Starting from March 1, 2020 the forum has been switched to the read-only mode. Please head to StackOverflow for support.
- CreatorTopic
- July 6, 2016 at 2:12 am #34221
Hi,
I wanted to activate the symfony switch user implementation.
security.yml
YAML1234main:switch_user:role: ROLE_ADMINISTRATORparameter: _want_to_be_this_userBut if I try to switch it I see an error:
Fatal error: Call to undefined method
Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken::getOrganizationContext() in /var/www/vhosts/orocrm/vendor/oro/platform/src/Oro/Bundle/DashboardBundle/Model/Manager.php on line 232
I then found the ImpersonationToken which implements the OrganizationContextTokenInterface and should therefor work fine. But I haven’t found how to make use of it.
Can you help me with the switch user case?
Thanks and
best regards
- CreatorTopic
- AuthorReplies
- July 8, 2016 at 6:43 am #34222
Hi,
Unfortunately native orocrm doesn’t support this feature, because it uses Symfony SwitchUserListener, which uses UserPasswordToken, which doesn’t know about getOrganizationContext() function. Quite complicated. But i have created custom code, which allows use user switch. I didn’t test it enough, so use it as good example.
July 8, 2016 at 6:45 am #34223Here is my security.yml
YAML12345678910security:encoders:Symfony\Component\Security\Core\User\User: plaintextfirewalls:dev:pattern: ^/(_(profiler|wdt)|css|images|js)/security: falsemain:switch_user: { role: ROLE_ADMINISTRATOR }July 8, 2016 at 6:49 am #34224I had to copy the entire file because original SwitchUserListener.php too closed (many private properties)
PHP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170<?phpnamespace Custom\Bundle\OroBundle\EventListener;use Psr\Log\LoggerInterface;use Symfony\Component\Security\Core\Exception\AccessDeniedException;use Symfony\Component\Security\Core\User\UserProviderInterface;use Symfony\Component\Security\Core\User\UserCheckerInterface;use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;use Symfony\Component\HttpKernel\Event\GetResponseEvent;use Symfony\Component\Security\Core\Exception\AuthenticationException;use Symfony\Component\HttpFoundation\RedirectResponse;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\Security\Core\Role\SwitchUserRole;use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;use Symfony\Component\Security\Http\Event\SwitchUserEvent;use Symfony\Component\Security\Http\SecurityEvents;use Symfony\Component\EventDispatcher\EventDispatcherInterface;use Symfony\Component\Security\Http\Firewall\ListenerInterface;use Custom\Bundle\OroBundle\Authentication\Token\UsernamePasswordOrganizationToken;class SwitchUserListener implements ListenerInterface{private $tokenStorage;private $provider;private $userChecker;private $providerKey;private $accessDecisionManager;private $usernameParameter;private $role;private $logger;private $dispatcher;public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null){if (empty($providerKey)) {throw new \InvalidArgumentException('$providerKey must not be empty.');}$this->tokenStorage = $tokenStorage;$this->provider = $provider;$this->userChecker = $userChecker;$this->providerKey = $providerKey;$this->accessDecisionManager = $accessDecisionManager;$this->usernameParameter = $usernameParameter;$this->role = $role;$this->logger = $logger;$this->dispatcher = $dispatcher;}/*** @param GetResponseEvent $event A GetResponseEvent instance* @throws \LogicException if switching to a user failed*/public function handle(GetResponseEvent $event){$request = $event->getRequest();if (!$request->get($this->usernameParameter)) {return;}if ('_exit' === $request->get($this->usernameParameter)) {$this->tokenStorage->setToken($this->attemptExitUser($request));} else {try {$this->tokenStorage->setToken($this->attemptSwitchUser($request));} catch (AuthenticationException $e) {throw new \LogicException(sprintf('Switch User failed: "%s"', $e->getMessage()));}}$request->query->remove($this->usernameParameter);$request->server->set('QUERY_STRING', http_build_query($request->query->all()));$response = new RedirectResponse($request->getUri(), 302);$event->setResponse($response);}/*** @param Request $request A Request instance* @return TokenInterface|null The new TokenInterface if successfully switched, null otherwise* @throws \LogicException* @throws AccessDeniedException*/private function attemptSwitchUser(Request $request){$token = $this->tokenStorage->getToken();$originalToken = $this->getOriginalToken($token);if (false !== $originalToken) {if ($token->getUsername() === $request->get($this->usernameParameter)) {return $token;}throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));}if (false === $this->accessDecisionManager->decide($token, array($this->role))) {throw new AccessDeniedException();}$username = $request->get($this->usernameParameter);if (null !== $this->logger) {$this->logger->info('Attempting to switch to user.', array('username' => $username));}$user = $this->provider->loadUserByUsername($username);$this->userChecker->checkPostAuth($user);$roles = $user->getRoles();$roles[] = new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $this->tokenStorage->getToken());$token = new UsernamePasswordOrganizationToken($user,$user->getPassword(),$this->providerKey,$user->getOrganization(),$roles);if (null !== $this->dispatcher) {$switchEvent = new SwitchUserEvent($request, $token->getUser());$this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);}return $token;}/*** @param Request $request A Request instance* @return TokenInterface The original TokenInterface instance* @throws AuthenticationCredentialsNotFoundException*/private function attemptExitUser(Request $request){if (false === $original = $this->getOriginalToken($this->tokenStorage->getToken())) {throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');}if (null !== $this->dispatcher) {$user = $this->provider->refreshUser($original->getUser());$switchEvent = new SwitchUserEvent($request, $user);$this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);}return $original;}/*** @param TokenInterface $token A switched TokenInterface instance* @return TokenInterface|false The original TokenInterface instance, false if the current TokenInterface is not switched*/private function getOriginalToken(TokenInterface $token){foreach ($token->getRoles() as $role) {if ($role instanceof SwitchUserRole) {return $role->getSource();}}return false;}}July 8, 2016 at 7:08 am #34225This is my custom UsernamePasswordOrganizationToken.php which is using by listener. I was thinking how to make it better and wrapped serialized data into base64, because i had some problems with || gluing in parent class.
PHP123456789101112131415161718192021222324<?phpnamespace Custom\Bundle\OroBundle\Authentication\Token;use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationToken as OriginToken;class UsernamePasswordOrganizationToken extends OriginToken{/*** {@inheritdoc}*/public function serialize(){return base64_encode(parent::serialize());}/*** {@inheritdoc}*/public function unserialize($serialized){parent::unserialize(base64_decode($serialized));}}And last php file is token factory.
PHP12345678910111213141516171819202122232425<?phpnamespace Custom\Bundle\OroBundle\Authentication\Token;use Oro\Bundle\OrganizationBundle\Entity\Organization;use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface;class UsernamePasswordOrganizationTokenFactory implements UsernamePasswordOrganizationTokenFactoryInterface{/*** {@inheritdoc}*/public function create($user, $credentials, $providerKey, Organization $organizationContext, array $roles = []){$authenticatedToken = new UsernamePasswordOrganizationToken($user,$credentials,$providerKey,$organizationContext,$roles);return $authenticatedToken;}}service.yml
YAML12345678910111213141516171819202122parameters:security.authentication.switchuser_listener.class: Custom\Bundle\OroBundle\EventListener\SwitchUserListeneroro_security.token.factory.username_password_organization.class: Custom\Bundle\OroBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryservices:security.authentication.switchuser_listener:class: %security.authentication.switchuser_listener.class%public: falseabstract: truearguments:- @security.token_storage-- @security.user_checker-- @security.access.decision_manager- @logger- "_switch_user"- "ROLE_ALLOWED_TO_SWITCH"- @event_dispatcheroro_security.token.factory.username_password_organization:class: %oro_security.token.factory.username_password_organization.class%July 10, 2016 at 11:28 pm #34226Thank you Mike for the example. I will update here if I could use it.
- AuthorReplies
The forum ‘OroPlatform – Programming Questions’ is closed to new topics and replies.