Skip to content

Commit

Permalink
Added backup/restore of attributes to be cross-context aware. Removed…
Browse files Browse the repository at this point in the history
… SessionInvalidatorInterceptor from PC stack. Allow impersonation for disabled users.

Conflicts:

	pom.xml
	webui/portal/src/main/java/org/exoplatform/portal/application/PortalLogoutLifecycle.java
  • Loading branch information
mposolda committed Feb 24, 2014
1 parent ef8e2c5 commit c3b06d8
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.gatein.pc.portlet.aspects.ProducerCacheInterceptor;
import org.gatein.pc.portlet.aspects.RequestAttributeConversationInterceptor;
import org.gatein.pc.portlet.aspects.SecureTransportInterceptor;
import org.gatein.pc.portlet.aspects.SessionInvalidatorInterceptor;
import org.gatein.pc.portlet.aspects.ValveInterceptor;
import org.gatein.pc.portlet.container.ContainerPortletDispatcher;
import org.gatein.pc.portlet.container.ContainerPortletInvoker;
Expand Down Expand Up @@ -121,10 +120,12 @@ public void start() {
bridgepInterceptor.setNext(ccppInterceptor);
ProducerCacheInterceptor producerCacheInterceptor = new ProducerCacheInterceptor();
producerCacheInterceptor.setNext(bridgepInterceptor);
SessionInvalidatorInterceptor sessionInvalidatorInterceptor = new SessionInvalidatorInterceptor();
sessionInvalidatorInterceptor.setNext(producerCacheInterceptor);

// SessionInvalidatorInterceptor is not needed as we have cross-context logout at WCI level
// SessionInvalidatorInterceptor sessionInvalidatorInterceptor = new SessionInvalidatorInterceptor();
// sessionInvalidatorInterceptor.setNext(producerCacheInterceptor);
ContextDispatcherInterceptor contextDispatcherInterceptor = new ContextDispatcherInterceptor();
contextDispatcherInterceptor.setNext(sessionInvalidatorInterceptor);
contextDispatcherInterceptor.setNext(producerCacheInterceptor);
PortletLifecyclePhaseInterceptor portletLifecyclePhaseInterceptor = new PortletLifecyclePhaseInterceptor();
portletLifecyclePhaseInterceptor.setNext(contextDispatcherInterceptor);
SecureTransportInterceptor secureTransportInterceptor = new SecureTransportInterceptor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,8 @@ public boolean importFile(String parentDirRelativePath, String name, InputStream
throw new NotYetImplemented();
}

public boolean invalidateSession(String sessId) {
throw new NotYetImplemented();
}

@Override
public HttpSession getHttpSession(String s) {
public HttpSession getHttpSession(String sessId) {
throw new NotYetImplemented();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.organization.UserStatus;
import org.exoplatform.services.security.Authenticator;
import org.exoplatform.services.security.ConversationRegistry;
import org.exoplatform.services.security.ConversationState;
Expand All @@ -36,13 +37,21 @@
import org.exoplatform.services.security.web.HttpSessionStateKey;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.wci.ServletContainerFactory;
import org.gatein.wci.session.SessionTask;
import org.gatein.wci.session.SessionTaskVisitor;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* Servlet, which handles impersonation and impersonalization (de-impersonation) of users
Expand Down Expand Up @@ -71,13 +80,20 @@ public class ImpersonationServlet extends AbstractHttpServlet {
/** Impersonation suffix (Actually path of this servlet) */
public static final String IMPERSONATE_URL_SUFIX = "/impersonate";

/** Prefix of session attributes, which will be used to backup existing session of root user */
private static final String BACKUP_ATTR_PREFIX = "_bck.";
/** Session attribute, which will be used to backup existing session of root user */
private static final String BACKUP_ATTR = "_impersonation.bck";

private static final Logger log = LoggerFactory.getLogger(ImpersonationServlet.class);

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// We set the character encoding now to UTF-8 before obtaining parameters
req.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
log.error("Encoding not supported", e);
}

String action = req.getParameter(PARAM_ACTION);
if (action == null) {
log.error("Parameter '" + PARAM_ACTION + "' not provided");
Expand Down Expand Up @@ -106,7 +122,7 @@ protected void startImpersonation(HttpServletRequest req, HttpServletResponse re
OrganizationService orgService = (OrganizationService)getContainer().getComponentInstanceOfType(OrganizationService.class);
User userToImpersonate;
try {
userToImpersonate = orgService.getUserHandler().findUserByName(usernameToImpersonate);
userToImpersonate = orgService.getUserHandler().findUserByName(usernameToImpersonate, UserStatus.BOTH);
} catch (Exception e) {
throw new ServletException(e);
}
Expand Down Expand Up @@ -178,20 +194,37 @@ protected boolean checkPermission(User userToImpersonate) {
protected void backupAndClearCurrentSession(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if (session != null) {
Enumeration attrNames = session.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String)attrNames.nextElement();
Object attrValue = session.getAttribute(attrName);

// Backup attribute and clear old
String backupAttrName = BACKUP_ATTR_PREFIX + attrName;
session.setAttribute(backupAttrName, attrValue);
session.removeAttribute(attrName);

if (log.isTraceEnabled()) {
log.trace("Finished backup of attribute: " + attrName);
String sessionId = session.getId();

// Backup attributes in sessions of portal and all portlet applications
ServletContainerFactory.getServletContainer().visit(new SessionTaskVisitor(sessionId, new SessionTask(){

@Override
public boolean executeTask(HttpSession session) {
if (log.isTraceEnabled()) {
log.trace("Starting with backup attributes for context: " + session.getServletContext().getContextPath());
}

// Create a copy just to make sure that attrNames is transient
List<String> attrNames = offlineCopy(session.getAttributeNames());
Map<String, Object> backup = new HashMap<String, Object>();

for (String attrName : attrNames) {
Object attrValue = session.getAttribute(attrName);

session.removeAttribute(attrName);
backup.put(attrName, attrValue);

if (log.isTraceEnabled()) {
log.trace("Finished backup of attribute: " + attrName);
}
}

session.setAttribute(BACKUP_ATTR, backup);
return true;
}
}

}));
}
}

Expand Down Expand Up @@ -276,42 +309,56 @@ protected void restoreConversationState(HttpServletRequest req, ImpersonatedIden
protected void restoreOldSessionAttributes(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if (session != null) {
int prefixLength = BACKUP_ATTR_PREFIX.length();
String sessionId = session.getId();

// Remove all session attributes of current (impersonated) user. Restore attributes of admin user
Enumeration attrNames = session.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String)attrNames.nextElement();
// Restore attributes in sessions of portal and all portlet applications
ServletContainerFactory.getServletContainer().visit(new SessionTaskVisitor(sessionId, new SessionTask(){

// Remove attribute of impersonated user
if (!attrName.startsWith(BACKUP_ATTR_PREFIX)) {
session.removeAttribute(attrName);
@Override
public boolean executeTask(HttpSession session) {
if (log.isTraceEnabled()) {
log.trace("Removed attribute: " + attrName);
log.trace("Starting with restoring attributes for context: " + session.getServletContext().getContextPath());
}
} else {
// Restore attribute as it's one of the attributes of admin user, which was backup-ed
Object attrValue = session.getAttribute(attrName);

String restoredAttributeName = attrName.substring(prefixLength);
session.setAttribute(restoredAttributeName, attrValue);
session.removeAttribute(attrName);
// Retrieve backup of previous attributes
Map<String, Object> backup = (Map<String, Object>)session.getAttribute(BACKUP_ATTR);

if (log.isTraceEnabled()) {
log.trace("Finished restore of attribute: " + attrName);
// Iteration 1 -- Remove all session attributes of current (impersonated) user.
List<String> attrNames = offlineCopy(session.getAttributeNames());
for (String attrName : attrNames) {
session.removeAttribute(attrName);
if (log.isTraceEnabled()) {
log.trace("Removed attribute: " + attrName);
}
}

// Iteration 2 -- Restore all session attributes of admin user
if (backup == null) {
if (log.isTraceEnabled()) {
log.trace("No session attributes found in previous impersonated session. Ignoring");
}
} else {
for (Map.Entry<String, Object> attr : backup.entrySet()) {
session.setAttribute(attr.getKey(), attr.getValue());

if (log.isTraceEnabled()) {
log.trace("Finished restore of attribute: " + attr.getKey());
}
}
}

return true;
}
}

}));
}
}

// Register given conversationState into ConversationRegistry. Key will be current Http session
private void registerConversationState(HttpServletRequest req, ConversationState conversationState) {
// Obtain stateKey of current HttpSession
HttpSession httpSession = req.getSession();
StateKey stateKey = new HttpSessionStateKey(httpSession);

// Update conversationRegistry
ConversationRegistry conversationRegistry = (ConversationRegistry)getContainer().getComponentInstanceOfType(ConversationRegistry.class);
conversationRegistry.register(stateKey, conversationState);
}
Expand Down Expand Up @@ -339,4 +386,12 @@ private String getReturnURI(HttpServletRequest req) {

return returnURI;
}

private List<String> offlineCopy(Enumeration<String> e) {
List<String> list = new LinkedList<String>();
while (e.hasMoreElements()) {
list.add(e.nextElement());
}
return list;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* JBoss, a division of Red Hat
* Copyright 2014, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. See the
* copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.gatein.web.security.impersonation;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
* @author <a href="mailto:[email protected]">Marek Posolda</a>
*/
public class ImpersonationUtils {

/**
* Create URL for redirection to impersonationServlet to start impersonation session
*
* @param portalContextPath context-path of portal (assumption is that ImpersonationServlet is available on this path too)
* @param usernameToImpersonate username to impersonate
* @param returnImpersonationUri URI, where should be request redirected from ImpersonationServlet after finish of Impersonation workflow
* @return uri to send to impersonationServlet including all parameters
*/
public static String createStartImpersonationURL(String portalContextPath, String usernameToImpersonate, String returnImpersonationUri) {
String impersonationServletUri = portalContextPath + ImpersonationServlet.IMPERSONATE_URL_SUFIX;

try {
return new StringBuilder(impersonationServletUri)
.append("?")
.append(ImpersonationServlet.PARAM_ACTION)
.append("=")
.append(ImpersonationServlet.PARAM_ACTION_START_IMPERSONATION)
.append("&")
.append(ImpersonationServlet.PARAM_USERNAME)
.append("=")
.append(URLEncoder.encode(usernameToImpersonate, "UTF-8"))
.append("&")
.append(ImpersonationServlet.PARAM_RETURN_IMPERSONATION_URI)
.append("=")
.append(URLEncoder.encode(returnImpersonationUri, "UTF-8"))
.toString();
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException(uee);
}
}

/**
* Create URL for redirection to impersonationServlet to stop impersonation session
*
* @param portalContextPath context-path of portal (assumption is that ImpersonationServlet is available on this path too)
* @return uri to send to impersonationServlet including all parameters
*/
public static String createFinishImpersonationURL(String portalContextPath) {
// Redirect to ImpersonationServlet and trigger stop of Impersonation session
String impersonationServletUri = portalContextPath + ImpersonationServlet.IMPERSONATE_URL_SUFIX;

return new StringBuilder(impersonationServletUri)
.append("?")
.append(ImpersonationServlet.PARAM_ACTION)
.append("=")
.append(ImpersonationServlet.PARAM_ACTION_STOP_IMPERSONATION)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package org.exoplatform.organization.webui.component;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -56,7 +55,7 @@
import org.exoplatform.webui.form.UIFormInputSet;
import org.exoplatform.webui.form.UIFormSelectBox;
import org.exoplatform.webui.form.UIFormStringInput;
import org.gatein.web.security.impersonation.ImpersonationServlet;
import org.gatein.web.security.impersonation.ImpersonationUtils;

/**
* Created by The eXo Platform SARL Author : chungnv [email protected] Jun 23, 2006 10:07:15 AM
Expand Down Expand Up @@ -310,7 +309,7 @@ public void execute(Event<UIListUsers> event) throws Exception {
String userName = event.getRequestContext().getRequestParameter(OBJECTID);

OrganizationService service = uiListUsers.getApplicationComponent(OrganizationService.class);
User userToImpersonate = service.getUserHandler().findUserByName(userName);
User userToImpersonate = service.getUserHandler().findUserByName(userName, UserStatus.BOTH);
if (userToImpersonate == null) {
UIApplication uiApplication = event.getRequestContext().getUIApplication();
uiApplication.addMessage(new ApplicationMessage("UIListUsers.msg.user-is-deleted", null,
Expand All @@ -327,32 +326,15 @@ public void execute(Event<UIListUsers> event) throws Exception {
return;
}

// Redirect to finish login with new user
// Redirect to impersonation servlet
PortalRequestContext portalRequestContext = Util.getPortalRequestContext();
SiteKey siteKey = portalRequestContext.getSiteKey();
NodeURL currentNodeURL = portalRequestContext.createURL(NodeURL.TYPE);
currentNodeURL.setResource(new NavigationResource(siteKey, portalRequestContext.getNodePath()));

String redirectURL = portalRequestContext.getRequestContextPath() + ImpersonationServlet.IMPERSONATE_URL_SUFIX;

// Attach params
redirectURL = new StringBuilder(redirectURL)
.append("?")
.append(ImpersonationServlet.PARAM_ACTION)
.append("=")
.append(ImpersonationServlet.PARAM_ACTION_START_IMPERSONATION)
.append("&")
.append(ImpersonationServlet.PARAM_USERNAME)
.append("=")
.append(URLEncoder.encode(userName, "UTF-8"))
.append("&")
.append(ImpersonationServlet.PARAM_RETURN_IMPERSONATION_URI)
.append("=")
.append(URLEncoder.encode(currentNodeURL.toString(), "UTF-8"))
.toString();
String impersonationRedirectURL = ImpersonationUtils.createStartImpersonationURL(portalRequestContext.getRequestContextPath(), userName, currentNodeURL.toString());

// Redirect to impersonation servlet
portalRequestContext.getJavascriptManager().addJavascript("window.location = '" + redirectURL + "';");
portalRequestContext.getJavascriptManager().addJavascript("window.location = '" + impersonationRedirectURL + "';");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public String getUserDisplayName() {
Identity identity = state.getIdentity();
if (identity instanceof ImpersonatedIdentity) {
String adminUsername = ((ImpersonatedIdentity) identity).getParentConversationState().getIdentity().getUserId();
return user.getFullName() + " (" + adminUsername + ")";
return user.getDisplayName() + " (" + adminUsername + ")";
} else {
return user.getFullName();
return user.getDisplayName();
}
}
}
Loading

0 comments on commit c3b06d8

Please sign in to comment.