2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.security
.ui
.rap
;
18 import java
.security
.AccessControlContext
;
19 import java
.security
.AccessController
;
20 import java
.security
.PrivilegedAction
;
22 import javax
.security
.auth
.Subject
;
23 import javax
.security
.auth
.login
.CredentialNotFoundException
;
24 import javax
.security
.auth
.login
.FailedLoginException
;
25 import javax
.security
.auth
.login
.LoginContext
;
26 import javax
.security
.auth
.login
.LoginException
;
27 import javax
.security
.auth
.x500
.X500Principal
;
28 import javax
.servlet
.http
.HttpServletRequest
;
29 import javax
.servlet
.http
.HttpSession
;
31 import org
.apache
.commons
.logging
.Log
;
32 import org
.apache
.commons
.logging
.LogFactory
;
33 import org
.argeo
.ArgeoException
;
34 import org
.argeo
.cms
.auth
.AuthConstants
;
35 import org
.argeo
.cms
.auth
.ThreadDeathLoginException
;
36 import org
.argeo
.cms
.widgets
.auth
.DefaultLoginDialog
;
37 import org
.argeo
.eclipse
.ui
.dialogs
.ErrorFeedback
;
38 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
39 import org
.eclipse
.rap
.rwt
.RWT
;
40 import org
.eclipse
.rap
.rwt
.application
.EntryPoint
;
41 import org
.eclipse
.swt
.widgets
.Display
;
42 import org
.eclipse
.ui
.PlatformUI
;
45 * RAP entry point with login capabilities. Once the user has been
46 * authenticated, the workbench is run as a privileged action by the related
49 public class SecureEntryPoint
implements EntryPoint
{
50 final static String ACCESS_CONTROL_CONTEXT
= "org.argeo.node.accessControlContext";
51 private final static Log log
= LogFactory
.getLog(SecureEntryPoint
.class);
54 * How many seconds to wait before invalidating the session if the user has
57 private Integer loginTimeout
= 1 * 60;
58 // TODO make it configurable
59 /** Default session timeout is 8 hours (European working day length) */
60 private Integer sessionTimeout
= 8 * 60 * 60;
62 /** Override to provide an application specific workbench advisor */
63 protected RapWorkbenchAdvisor
createRapWorkbenchAdvisor(String username
) {
64 return new RapWorkbenchAdvisor(username
);
68 public final int createUI() {
69 // Short login timeout so that the modal dialog login doesn't hang
71 RWT
.getRequest().getSession().setMaxInactiveInterval(loginTimeout
);
73 final Display display
= PlatformUI
.createDisplay();
75 // load context from session
76 HttpServletRequest httpRequest
= RWT
.getRequest();
77 final HttpSession httpSession
= httpRequest
.getSession();
78 AccessControlContext acc
= (AccessControlContext
) httpSession
79 .getAttribute(ACCESS_CONTROL_CONTEXT
);
81 final Subject subject
;
83 && Subject
.getSubject(acc
).getPrincipals(X500Principal
.class)
85 subject
= Subject
.getSubject(acc
);
87 subject
= new Subject();
89 final LoginContext loginContext
;
90 DefaultLoginDialog callbackHandler
;
92 callbackHandler
= new DefaultLoginDialog(
93 display
.getActiveShell());
94 loginContext
= new LoginContext(
95 AuthConstants
.LOGIN_CONTEXT_USER
, subject
,
97 } catch (LoginException e1
) {
98 throw new ArgeoException("Cannot initialize login context", e1
);
101 tryLogin
: while (subject
.getPrincipals(X500Principal
.class).size() == 0) {
103 loginContext
.login();
104 if (subject
.getPrincipals(X500Principal
.class).size() == 0)
105 throw new ArgeoException("Login succeeded but no auth");// fatal
107 // add thread locale to RWT session
108 // if (log.isTraceEnabled())
109 // log.trace("Locale " + LocaleUtils.threadLocale.get());
110 // RWT.setLocale(LocaleUtils.threadLocale.get());
112 // once the user is logged in, longer session timeout
113 RWT
.getRequest().getSession()
114 .setMaxInactiveInterval(sessionTimeout
);
116 if (log
.isDebugEnabled())
117 log
.debug("Authenticated " + subject
);
118 } catch (FailedLoginException e
) {
119 MessageDialog
.openInformation(display
.getActiveShell(),
120 "Bad Credentials", e
.getMessage());
123 } catch (CredentialNotFoundException e
) {
124 MessageDialog
.openInformation(display
.getActiveShell(),
125 "No Credentials", e
.getMessage());
128 } catch (LoginException e
) {
129 callbackHandler
.getShell().dispose();
130 return processLoginDeath(display
, e
);
134 final String username
= subject
.getPrincipals(X500Principal
.class)
135 .iterator().next().getName();
136 // Logout callback when the display is disposed
137 display
.disposeExec(new Runnable() {
139 if (log
.isTraceEnabled())
140 log
.trace("Display disposed");
142 LoginContext loginContext
= new LoginContext(
143 AuthConstants
.LOGIN_CONTEXT_USER
, subject
);
144 loginContext
.logout();
145 } catch (LoginException e
) {
146 log
.error("Error when logging out", e
);
154 Integer returnCode
= null;
156 returnCode
= Subject
.doAs(subject
, new PrivilegedAction
<Integer
>() {
157 public Integer
run() {
158 // add security context to session
159 httpSession
.setAttribute(ACCESS_CONTROL_CONTEXT
,
160 AccessController
.getContext());
163 RapWorkbenchAdvisor workbenchAdvisor
= createRapWorkbenchAdvisor(username
);
164 int result
= PlatformUI
.createAndRunWorkbench(display
,
166 return new Integer(result
);
169 // Explicit exit from workbench
170 fullLogout(subject
, username
);
177 private Integer
processLoginDeath(Display display
, Throwable e
) {
178 // check thread death
179 ThreadDeath td
= wasCausedByThreadDeath(e
);
184 if (!display
.isDisposed()) {
185 ErrorFeedback
.show("Unexpected exception during authentication", e
);
186 // this was not just bad credentials or death thread
187 RWT
.getRequest().getSession().setMaxInactiveInterval(1);
191 throw new ArgeoException(
192 "Unexpected exception during authentication", e
);
198 * If there is a {@link ThreadDeath} in the root causes, rethrow it
199 * (important for RAP cleaning mechanism)
201 protected ThreadDeath
wasCausedByThreadDeath(Throwable t
) {
202 if (t
instanceof ThreadDeath
)
203 return (ThreadDeath
) t
;
204 if (t
instanceof ThreadDeathLoginException
)
205 return ((ThreadDeathLoginException
) t
).getThreadDeath();
206 if (t
.getCause() != null)
207 return wasCausedByThreadDeath(t
.getCause());
212 private void fullLogout(Subject subject
, String username
) {
214 LoginContext loginContext
= new LoginContext(
215 AuthConstants
.LOGIN_CONTEXT_USER
, subject
);
216 loginContext
.logout();
217 HttpServletRequest httpRequest
= RWT
.getRequest();
218 HttpSession httpSession
= httpRequest
.getSession();
219 httpSession
.setAttribute(ACCESS_CONTROL_CONTEXT
, null);
220 RWT
.getRequest().getSession().setMaxInactiveInterval(1);
221 log
.info("Logged out " + (username
!= null ? username
: "")
222 + " (THREAD=" + Thread
.currentThread().getId() + ")");
223 } catch (LoginException e
) {
224 log
.error("Error when logging out", e
);