+/*
+ * Copyright (C) 2007-2012 Mathieu Baudier
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.argeo.security.ui.rap;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
import org.eclipse.equinox.security.auth.ILoginContext;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.rwt.RWT;
import org.eclipse.rwt.lifecycle.IEntryPoint;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
-import org.eclipse.ui.application.WorkbenchAdvisor;
-import org.eclipse.ui.application.WorkbenchWindowAdvisor;
import org.springframework.security.BadCredentialsException;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
/**
- * RAP entry point with login capabilities. On the user has been authenticated,
- * the workbench is run as a privileged action by the related subject.
+ * RAP entry point with login capabilities. Once the user has been
+ * authenticated, the workbench is run as a privileged action by the related
+ * subject.
*/
public class SecureEntryPoint implements IEntryPoint {
private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
+ /**
+ * From org.springframework.security.context.
+ * HttpSessionContextIntegrationFilter
+ */
+ protected static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
+
/**
* How many seconds to wait before invalidating the session if the user has
* not yet logged in.
*/
private Integer loginTimeout = 1 * 60;
- private Integer sessionTimeout = 15 * 60;
+ // TODO make it configurable
+ /** Default session timeout is 8 hours (European working day length) */
+ private Integer sessionTimeout = 8 * 60 * 60;
+
+ /** Override to provide an application specific workbench advisor */
+ protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
+ return new RapWorkbenchAdvisor(username);
+ }
@Override
- public int createUI() {
+ public final int createUI() {
// Short login timeout so that the modal dialog login doesn't hang
// around too long
RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
+ HttpServletRequest httpRequest = RWT.getRequest();
+ HttpSession httpSession = httpRequest.getSession();
+ Object contextFromSessionObject = httpSession
+ .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
+ if (contextFromSessionObject != null)
+ SecurityContextHolder
+ .setContext((SecurityContext) contextFromSessionObject);
+
if (log.isDebugEnabled())
log.debug("THREAD=" + Thread.currentThread().getId()
- + ", sessionStore=" + RWT.getSessionStore().getId());
-
- Integer returnCode = null;
+ + ", sessionStore=" + RWT.getSessionStore().getId()
+ + ", remote user=" + httpRequest.getRemoteUser());
// create display
- Display display = PlatformUI.createDisplay();
+ final Display display = PlatformUI.createDisplay();
// log in
final ILoginContext loginContext = SecureRapActivator
- .createLoginContext();
+ .createLoginContext(SecureRapActivator.CONTEXT_SPRING);
Subject subject = null;
- tryLogin: while (subject == null) {
+ tryLogin: while (subject == null && !display.isDisposed()) {
try {
loginContext.login();
subject = loginContext.getSubject();
+
+ if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null)
+ httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
+ SecurityContextHolder.getContext());
+
+ // Once the user is logged in, she can have a longer session
+ // timeout
+ RWT.getRequest().getSession()
+ .setMaxInactiveInterval(sessionTimeout);
+ if (log.isDebugEnabled())
+ log.debug("Authenticated " + subject);
} catch (LoginException e) {
- if (e.getCause() != null) {
- Throwable firstCause = e.getCause();
- // log.error("Cause", firstCause);
- if (firstCause instanceof LoginException
- && firstCause.getCause() != null) {
- Throwable secondCause = firstCause.getCause();
- if (secondCause instanceof BadCredentialsException) {
- MessageDialog.openInformation(
- display.getActiveShell(),
- "Bad Credentials",
- "Your credentials are incorrect");
- // retry login
- continue tryLogin;
- } else if (secondCause instanceof ThreadDeath) {
- // rethrow thread death caused by dialog UI timeout
- throw (ThreadDeath) secondCause;
- }
-
- } else if (firstCause instanceof ThreadDeath) {
- throw (ThreadDeath) firstCause;
- }
+ BadCredentialsException bce = wasCausedByBadCredentials(e);
+ if (bce != null) {
+ MessageDialog.openInformation(display.getActiveShell(),
+ "Bad Credentials", bce.getMessage());
+ // retry login
+ continue tryLogin;
}
- // this was not just bad credentials returns
- RWT.getRequest().getSession().setMaxInactiveInterval(1);
- display.dispose();
- return -1;
+ return processLoginDeath(display, e);
}
}
- // identify after successful login
- if (log.isDebugEnabled())
- log.debug("Authenticated " + subject);
final String username = subject.getPrincipals().iterator().next()
.getName();
-
- // Once the user is logged in, she can have a longer session timeout
- RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
-
// Logout callback when the display is disposed
display.disposeExec(new Runnable() {
public void run() {
log.debug("Display disposed");
logout(loginContext, username);
- // invalidate session
- //RWT.getRequest().getSession().setMaxInactiveInterval(1);
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e1) {
- // silent
- }
}
});
//
// RUN THE WORKBENCH
//
+ Integer returnCode = null;
try {
- returnCode = (Integer) Subject.doAs(subject, getRunAction(display));
+ returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
+ public Integer run() {
+ RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
+ int result = PlatformUI.createAndRunWorkbench(display,
+ workbenchAdvisor);
+ return new Integer(result);
+ }
+ });
logout(loginContext, username);
} finally {
display.dispose();
}
- return processReturnCode(returnCode);
+ return returnCode;
}
- protected void logout(ILoginContext secureContext, String username) {
- try {
- secureContext.logout();
- log.info("Logged out " + (username != null ? username : "")
- + " (THREAD=" + Thread.currentThread().getId() + ")");
- } catch (LoginException e) {
- log.error("Erorr when logging out", e);
+ private Integer processLoginDeath(Display display, LoginException e) {
+ // check thread death
+ ThreadDeath td = wasCausedByThreadDeath(e);
+ if (td != null) {
+ display.dispose();
+ throw td;
+ }
+ if (!display.isDisposed()) {
+ ErrorFeedback.show("Unexpected exception during authentication", e);
+ // this was not just bad credentials or death thread
+ RWT.getRequest().getSession().setMaxInactiveInterval(1);
+ display.dispose();
+ return -1;
+ } else {
+ throw new ArgeoException(
+ "Unexpected exception during authentication", e);
}
- }
- @SuppressWarnings("rawtypes")
- private PrivilegedAction getRunAction(final Display display) {
- return new PrivilegedAction() {
- public Object run() {
- int result = createAndRunWorkbench(display);
- return new Integer(result);
- }
- };
}
- /** To be overridden */
- protected Integer createAndRunWorkbench(Display display) {
- return PlatformUI.createAndRunWorkbench(display,
- createWorkbenchAdvisor());
- }
+ /** Recursively look for {@link BadCredentialsException} in the root causes. */
+ private BadCredentialsException wasCausedByBadCredentials(Throwable t) {
+ if (t instanceof BadCredentialsException)
+ return (BadCredentialsException) t;
- /** To be overridden */
- protected Integer processReturnCode(Integer returnCode) {
- return returnCode;
+ if (t.getCause() != null)
+ return wasCausedByBadCredentials(t.getCause());
+ else
+ return null;
}
- /** To be overridden */
- protected WorkbenchAdvisor createWorkbenchAdvisor() {
- return new SecureWorkbenchAdvisor() {
- public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
- IWorkbenchWindowConfigurer configurer) {
- return new RapSecureWorkbenchWindowAdvisor(configurer);
- }
+ /**
+ * If there is a {@link ThreadDeath} in the root causes, rethrow it
+ * (important for RAP cleaning mechanism)
+ */
+ protected ThreadDeath wasCausedByThreadDeath(Throwable t) {
+ if (t instanceof ThreadDeath)
+ return (ThreadDeath) t;
+
+ if (t.getCause() != null)
+ return wasCausedByThreadDeath(t.getCause());
+ else
+ return null;
+ }
- };
+ protected void logout(ILoginContext secureContext, String username) {
+ try {
+ secureContext.logout();
+ log.info("Logged out " + (username != null ? username : "")
+ + " (THREAD=" + Thread.currentThread().getId() + ")");
+ } catch (LoginException e) {
+ log.error("Erorr when logging out", e);
+ }
}
}