It may be useful for portal administrator to have possibility to temporary login as another user without knowing his password. For example: User root wants to verify that user mary really doesn't have permission to see page X or portlet Y on page Z.
The idea is that:
- In OrganizationPortlet, we will have another action in user's list. So in addition to current actions for Edit User Info and Delete User, we will have another action Impersonate User. After click to this, administrator will be directly impersonated as particular user (for example user mary)
- During impersonation session, everything in UI will be treated similarly like in normal session of particular user (for example mary) . Only differences will be:
- In right top corner: There will be name of impersonated user followed by username of admin user in braces. For example: Mary Kelly (root)
- In top left drop-down menu of the page (UIStarToolbarPortlet), there will be message Finish impersonation instead of default Sign out. After click to this, impersonation session will be finished and user root will be returned to his "own" previous session before he had started impersonation session. So normally he will be returned back to page with OrganizationPortlet. The idea is, that his whole UI state will be "recovered" to previous state before he started impersonation.
Impersonation is quite powerfull feature, so it needs to be properly checked who have permission to impersonate as another user. We may have option in UserACL to control which groups will have permissions to impersonate as another user. With group manager:/platform/administrators as default:
<value-param> <name>user.impersonate.groups</name> <description>groups with membership type have permission to impersonate as other users</description> <value>manager:/platform/administrators</value> </value-param>
Actually we will have new method in UserACL with signature:
/** * Decide if current user has permission to impersonate as another user * * @param userToImpersonate user, which we want to impersonate (Actually not used for this impl) * @return true if current user has permission to impersonate as userToImpersonate */ public boolean hasImpersonateUserPermission(User userToImpersonate)
Just a note that parameter userToImpersonate represents user, who will be target of impersonation. Actually it may be unused for default UserACL implementation, which just allow all admins from manager:/platform/administrators to impersonate as everyone. But possibly people may override if they have some custom requirements (For example: if they want to allow user john to impersonate as normal users, but not allow impersonation if target user is administrator etc)
After click to impersonation icon would be checked permissions and user will be redirected to ImpersonationServlet, which will handle requests for impersonation and also for de-impersonation. The reason to do the real "impersonation" in separate servlet instead of WebUI action listener would be to not interfere current HttpServletRequest and properly finish it as root user (including save of WebUI state and all other actions).
- check permissions via UserACL (described in previous section)
- Retrieve all attributes of current HttpSession and backup them in HttpSession with adding some proper prefix like _bck. . The reason is, that after finish of Impersonation, we want to restore previous state of HttpSession of root user, which will actually also restore WebUI state of portal . This change is done also in all HTTP sessions of all portlet applications, which will allow to restore state of portlet components as well.
- Real "impersonation" will be performed by creating object of ImpersonatedIdentity (subclass of org.exoplatform.services.security.Identity), which represents Identity of impersonated user. In addition, it will encapsulate original ConversationState object of root user, which will be later restored. New ConversationState object, which will encapsulate ImpersonatedIdentity, will be registered to ConversationRegistry and will replace previous state. Note that ID of HttpSession will remain the same.
Each Http request of impersonated session will be handled by Http filter ImpersonationFilter, which will encapsulate original HttpServletRequest and override methods getRemoteUser(), getUserPrincipal() and isUserInRole(String role), so GateIn Portal will see the owner of the request as the impersonated user. Note that web container (JBoss AS or Tomcat) won't see a difference and will still treat the request as request of the original owner (root user), just GateIn Portal will se impersonated identity.
Finish of impersonation session will be also handled by ImpersonationServlet, which will:
- Restore previously saved attributes of "original" HttpSession and clear attributes of impersonated user. Again, this should ensure that WebUI state and also all portlet sessions will be returned to state before impersonation had been started.
- Restore previous ConversationState wrapped in ImpersonatedIdentity and restore it back into ConversationRegistry
- Redirect user back to previous URL, from which impersonation session was started (typically URL with Organization Portlet).
Impersonation already implemented at GateIn portal 3.8 and available in documentation Impersonation - GateIn Portal 3.8 - Project Documentation Editor
Currently it's allowed to impersonate as user "mary" even if "mary" is disabled. I think it's correct to behave this way. For example, admin may have doubt that user "mary" has some malicious content on her dashboard and is doing some bad things... So he wants to disable her first and then impersonate as "mary" and quickly check what she really has on her dashboard.