--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.CredentialNotFoundException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.x500.X500Principal;
+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.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.ThreadDeathLoginException;
+import org.argeo.cms.widgets.auth.DefaultLoginDialog;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * RAP entry point with login capabilities. Once the user has been
+ * authenticated, the workbench is run as a privileged action by the related
+ * subject.
+ */
+@Deprecated
+public class SecureEntryPoint implements EntryPoint {
+ final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext";
+ private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
+
+ /**
+ * How many seconds to wait before invalidating the session if the user has
+ * not yet logged in.
+ */
+ private Integer loginTimeout = 1 * 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 final int createUI() {
+ // Short login timeout so that the modal dialog login doesn't hang
+ // around too long
+ RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
+
+ final Display display = PlatformUI.createDisplay();
+
+ // load context from session
+ HttpServletRequest httpRequest = RWT.getRequest();
+ final HttpSession httpSession = httpRequest.getSession();
+ AccessControlContext acc = (AccessControlContext) httpSession
+ .getAttribute(ACCESS_CONTROL_CONTEXT);
+
+ final Subject subject;
+ if (acc != null
+ && Subject.getSubject(acc).getPrincipals(X500Principal.class)
+ .size() == 1) {
+ subject = Subject.getSubject(acc);
+ } else {
+ subject = new Subject();
+
+ final LoginContext loginContext;
+ DefaultLoginDialog callbackHandler;
+ try {
+ callbackHandler = new DefaultLoginDialog(
+ display.getActiveShell());
+ loginContext = new LoginContext(
+ AuthConstants.LOGIN_CONTEXT_USER, subject,
+ callbackHandler);
+ } catch (LoginException e1) {
+ throw new CmsException("Cannot initialize login context", e1);
+ }
+
+ tryLogin: while (subject.getPrincipals(X500Principal.class).size() == 0) {
+ try {
+ loginContext.login();
+ if (subject.getPrincipals(X500Principal.class).size() == 0)
+ throw new CmsException("Login succeeded but no auth");// fatal
+
+ // add thread locale to RWT session
+ // if (log.isTraceEnabled())
+ // log.trace("Locale " + LocaleUtils.threadLocale.get());
+ // RWT.setLocale(LocaleUtils.threadLocale.get());
+
+ // once the user is logged in, longer session timeout
+ RWT.getRequest().getSession()
+ .setMaxInactiveInterval(sessionTimeout);
+
+ if (log.isDebugEnabled())
+ log.debug("Authenticated " + subject);
+ } catch (FailedLoginException e) {
+ MessageDialog.openInformation(display.getActiveShell(),
+ "Bad Credentials", e.getMessage());
+ // retry login
+ continue tryLogin;
+ } catch (CredentialNotFoundException e) {
+ MessageDialog.openInformation(display.getActiveShell(),
+ "No Credentials", e.getMessage());
+ // retry login
+ continue tryLogin;
+ } catch (LoginException e) {
+ callbackHandler.getShell().dispose();
+ return processLoginDeath(display, e);
+ }
+ }
+ }
+ final String username = subject.getPrincipals(X500Principal.class)
+ .iterator().next().getName();
+ // Logout callback when the display is disposed
+ display.disposeExec(new Runnable() {
+ public void run() {
+ if (log.isTraceEnabled())
+ log.trace("Display disposed");
+ try {
+ LoginContext loginContext = new LoginContext(
+ AuthConstants.LOGIN_CONTEXT_USER, subject);
+ loginContext.logout();
+ } catch (LoginException e) {
+ log.error("Error when logging out", e);
+ }
+ }
+ });
+
+ //
+ // RUN THE WORKBENCH
+ //
+ Integer returnCode = null;
+ try {
+ returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
+ public Integer run() {
+ // add security context to session
+ httpSession.setAttribute(ACCESS_CONTROL_CONTEXT,
+ AccessController.getContext());
+
+ // start workbench
+ RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
+ int result = PlatformUI.createAndRunWorkbench(display,
+ workbenchAdvisor);
+ return new Integer(result);
+ }
+ });
+ // Explicit exit from workbench
+ fullLogout(subject, username);
+ } finally {
+ display.dispose();
+ }
+ return returnCode;
+ }
+
+ private Integer processLoginDeath(Display display, Throwable 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 CmsException(
+ "Unexpected exception during authentication", e);
+ }
+
+ }
+
+ /**
+ * 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 instanceof ThreadDeathLoginException)
+ return ((ThreadDeathLoginException) t).getThreadDeath();
+ if (t.getCause() != null)
+ return wasCausedByThreadDeath(t.getCause());
+ else
+ return null;
+ }
+
+ private void fullLogout(Subject subject, String username) {
+ try {
+ LoginContext loginContext = new LoginContext(
+ AuthConstants.LOGIN_CONTEXT_USER, subject);
+ loginContext.logout();
+ HttpServletRequest httpRequest = RWT.getRequest();
+ HttpSession httpSession = httpRequest.getSession();
+ httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null);
+ RWT.getRequest().getSession().setMaxInactiveInterval(1);
+ log.info("Logged out " + (username != null ? username : "")
+ + " (THREAD=" + Thread.currentThread().getId() + ")");
+ } catch (LoginException e) {
+ log.error("Error when logging out", e);
+ }
+ }
+}