eZ Platform Discussions

External user providers : credentials error on first page load

version2

#1

Hello !

I’m in a bit of a weird spot here. We use external user managers to log users into the admin part of eZ. We already managed that with CAS and another custom solution, but now we are trying to do it with Shibboleth. Two weird things here:

  • instead of having a similar behaviour, on first arriving on the admin interface once logged, we get an error page with this message “User ‘random@user.mail’ doesn’t have user/login permission to SiteAccess ‘admin’”, despite the user having the said right.
  • if we reload manually the page, then everything is working perfectly, no more error message. And nothing else is different on the profiler on the user part. We have a working EventListener on the interactive login event, and it does do the same than with the others authentication providers, and then is not called after the first try (as it should).

The only difference is that when we reload the page, we go through a lot more listeners :
First call (bug) :
"Symfony\Component\Security\Http\Firewall\ChannelListener" "Symfony\Component\Security\Http\Firewall\ContextListener"
Second & following calls (ok) :
"Symfony\Component\Security\Http\Firewall\ChannelListener" "Symfony\Component\Security\Http\Firewall\ContextListener" "Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener" "Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener" "eZ\Publish\Core\REST\Server\Security\RestAuthenticator" "Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener" "Symfony\Component\Security\Http\Firewall\LogoutListener" "Symfony\Component\Security\Http\Firewall\AccessListener"

What is happening here? It feels like there is a priority problem on the authentication part that tests the eZ credentials before the login, but what happens?

Has anyone had the same problem?


#2

For some reason, dynamic/interactive login works with CAS and the other custom way, but with Shibboleth, the user used to authenticate was Anonymous instead of our designated user for this operation (I still can’t figure out why, though !). As the anonymous user had not the rights to the user/login role on the admin part, we got the problem at the first landing on the page.

After that, the user was logged by our authentication process and the session refreshed with the next page load, and everything was then fine.

Don’t know if the fact that the user can still be logged interactively even when it has no user/login right is a bug or if it has no consequence, might investigate later…

At least the problem is solved in our side.


#3

I think this happens because you use an user provider which is not really compatible with eZ. The refreshUser() method of the provider needs some tricks, this is how it looks for my Ldap provider:

public function refreshUser(UserInterface $user)
{
    if (!$user instanceof UserWrapped) {
        throw new UnsupportedUserException();
    }

    /** @var LdapUser $ldapUser */
    $ldapUser = parent::refreshUser($user);
    $user->setWrappedUser($ldapUser);
    $user->setAPIUser($ldapUser->getAPIUser());

    $this->repository->setCurrentUser($user->getAPIUser());

    return $user;
}

The setCurrentUser() call is really important here. The problem is that, the doc says that you should call this method in the login listener, which is true, but after the first redirect, eZ tries to refresh (load) your user from the session and you get the no permission error.

Normal user providers usually don’t handle the eZ UserWrapped class (why would they?) and at login they use their own user object (the security token will hold eg LdapUser instead of UserWrapped). So after the login redirects to the first page (eg the admin dashboard), your provider’s refreshUser() will run (because the token has a user which is supported), which will fail because it’s not really compatible (this is where you get the error). Then eZ converts the user object to UserWrapped. So from the next request eZ takes over and it will work, but your user provider will be skipped, so it won’t check if your ldap user is still valid or whatever you would check in your refreshUser().