Chapters
33 Chapters
|
3:34:13
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
22.
Impersonation (switch_user)
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.10.2
"doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.7.2
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.8.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.0
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.1.4
"symfony/cache": "^3.3|^4.0", // v4.1.4
"symfony/console": "^4.0", // v4.1.4
"symfony/flex": "^1.0", // v1.21.6
"symfony/framework-bundle": "^4.0", // v4.1.4
"symfony/lts": "^4@dev", // dev-master
"symfony/property-access": "^3.3|^4.0", // v4.1.4
"symfony/property-info": "^3.3|^4.0", // v4.1.4
"symfony/security-bundle": "^4.0", // v4.1.4
"symfony/serializer": "^3.3|^4.0", // v4.1.4
"symfony/twig-bundle": "^4.0", // v4.1.4
"symfony/web-server-bundle": "^4.0", // v4.1.4
"symfony/yaml": "^4.0", // v4.1.4
"twig/extensions": "^1.5" // v1.5.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.4
"symfony/dotenv": "^4.0", // v4.1.4
"symfony/maker-bundle": "^1.0", // v1.7.0
"symfony/monolog-bundle": "^3.0", // v3.3.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.4
"symfony/stopwatch": "^3.3|^4.0", // v4.1.4
"symfony/var-dumper": "^3.3|^4.0", // v4.1.4
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.1.4
}
}
15 Comments
Hey SymfonyCast-Team,
I have the same issue as Manoj (see below). I implemented it mainly as described, but I am using the interface
UserLoaderInterfacein my UserRepository (and removedpropery: emailin my yaml-file). Reason for this is that the email-address (to identify a user) is not at the USER-Entity itself, but with a 1:1 relationship at the PERSON-Enity). This is working perfectly until it comes to the user-switch.So it seems to work in general (see my "tests"):
But once I try to switch the user (by a valid email-address), I am redirected to the login-page and I am logged out by the system.
Any tip how to solve this or how to inverstigate the issue?
Best regards!!!
Tim
Hey Tim,
Sorry, it's not clear enough for me what exactly does not work as expected? The 4 use cases you described sound ok to me. The problem is that you're logged out by the system automatically? Well, if you change the user data directly in the DB - it may happen. Could you try to add all required roles in the DB you need for the user you want to test first? Make sure that this user has that ROLE_ALLOWED_TO_SWITCH role too. Then, logout, go to the login page, log in as this user (with ROLE_ALLOWED_TO_SWITCH) using valid credentials, make sure you're successfully logged in. Refresh the page just in case to make sure you're not logged out due to some problems on the next request. And try now to impersonate someone (another user that exist in the DB). Does it work for you now?
Cheers!
Hello Victor, sorry for not being clear enough.
Person A has the role 'ROLE_ALLOWED_TO_SWITCH' and 'ROLE_ADMIN. Cache cleaned. Browser closed. Person A can login as expected and use the web-application on multiple levels and pages as expected. This behavior is implemented since month and no issue.
Now Person A is logged in and tries to impersonate (https://dev.website.local/some_name?_switch_user=person_b@test.com). Now the user is redirected to the login-page and Person A is logged out.
Here is some of my code
class UserRepository extends ServiceEntityRepository implements UserLoaderInterface {
}
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator {
}
Hey Tim K. !
Hmm. When a user is suddenly logged out like this, I always think of one specific cause. Before I explain it (and end up being wrong about the cause... and wasting all or our times ;) ), try an experiment:
A) Make your User class implement EquatableInterface - https://github.com/symfony/symfony/blob/712100924d9dc875b536b8c25f88b6a2d9d40169/src/Symfony/Component/Security/Core/User/EquatableInterface.php
B) And return true from the method:
Let me know if that fixes the issue. If it does, I can explain and we can find the root cause. If it does not, then we'll know to look for something else :).
Cheers!
Hey Ryan,
cool. It worked with these small lines of code.
Curious to know why...
Can I keep it like this or do I need to replace this test by something else?
Cheers
Tim
Hey Tim K.!
Woohoo! Ok, now that we know this fixes things, let's talk about what's *really* going on and the "proper" fix for this. Here is an ancient - but still totally relevant - conversation about this - https://symfonycasts.com/sc...
So basically, you should not keep this isEqualTo() "fix"... it could have a bad security edge case. What we need to do is figure out why - when you switch users - your User seems to suddenly "change". What I think is happening is this:
A) You switch to person_b@test.com. This works and Symfony then redirects
B) When the first page loads after switching, the User object (for person_b) is loaded from the session. Then, this hasUserChanged() method is called: https://github.com/symfony/...
You can see how - if your User implements EquatableInterface - you "short circuit" this method and take full control of it. What we need to do now is remove that method & interface, and try to figure out what part of that rest of that function is causing it to return "true". You could add some debug code right before each "return true" to see which is triggered.
One related question is: does your User class have a custom serialize() method? Usually your User class does *not* need this method... and it is often the cause of the problem (usually you are missing serializing a field that is then checked in this hasUserChanged() method... which means that it is "missing" from the deserialized User object and so it looks "changed" when comparing that field to the object stored in the database).
Phew! Let me know what you find out!
P.S. - I hope you see you also in a few days for Germany Symfony Live ;).
Cheers!
Good Morning Ryan,
I really appreciate your support. Many Thanks!
I have checked the function and it returns indeed "true" at the last IF-statement.It seems that not all required data is loaded in the USER-object, as the SubObject PERSON is sort of empty (it has the ID-value, but all other values are NULL). But this object is delivering the email-address (=getUsername()).
<b>Symfony\Component\Security\Core\Authentication\Token</b>
`
private function hasUserChanged(UserInterface $user): bool {
// ...
if ($this->user->getUsername() !== $user->getUsername()) {
}
//...
}
`
<b>App\Entity\User (Reminder see above)</b>
`
//..
public function getUsername(): string {
return (string) $this->person->getEmail();
}
//..
`
... and no, App\Entity\User-class has no serialize()-methode.
What can I do now?
Cheers!
<b>Addendum</b>
I added "fetch='EAGER'" to to the PERSON-property in the USER-class, which seems to solve the problem.
<b>App\Entity\User</b>
`
// ...
`
Is this a valid solution or are there any disadvantages of this?
Cheers!
Tim
Hey Tim K.!
Sorry for the slow reply - I had a personal issue. And, excellent debugging!
> I added "fetch='EAGER'" to to the PERSON-property in the USER-class, which seems to solve the problem.
This is totally fine. The only disadvantage (which might really be fine) is that whenever you query for your User object, Doctrine will automatically also query for your Person object. But if you're routinely using that data anyways... that will make no difference. We could think of other solutions... but I think this is probably just. fine :).
Cheers!
👍
many thanks!
I have implemented impersonation feature. And it works on the local machine. But at my hosting it doesn't works. When I trying to switch user on real hosting - nothing happens. No errors and no switching. How can I debug this?
Hey Eugem
I believe the user you are using on your production site does not have the role required for impersonating users. Could you double check that?
Cheers!
Hi! It were settings of Apache web server
Hi, This was first time I came to know about impersonation feature in Symfony. I have implemented it. but sadly it doesn't work in my app. Whenever I try, it just redirect me back to login page.
You can see the code here:
https://github.com/napester...
Hey infete
Hmm, everything looks fine to me. If it's redirecting you back to the login page is because you don't have the permission to switch users. Clear the case and try again but with an admin account
Cheers!
Okay, makes sense. Thanks
"Houston: no signs of life"
Start the conversation!