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
.PrivilegedAction
;
20 import javax
.security
.auth
.Subject
;
21 import javax
.security
.auth
.login
.LoginException
;
22 import javax
.security
.auth
.spi
.LoginModule
;
23 import javax
.servlet
.http
.HttpServletRequest
;
24 import javax
.servlet
.http
.HttpSession
;
26 import org
.apache
.commons
.logging
.Log
;
27 import org
.apache
.commons
.logging
.LogFactory
;
28 import org
.argeo
.ArgeoException
;
29 import org
.argeo
.eclipse
.ui
.workbench
.ErrorFeedback
;
30 import org
.argeo
.security
.ui
.dialogs
.DefaultLoginDialog
;
31 import org
.eclipse
.equinox
.security
.auth
.ILoginContext
;
32 import org
.eclipse
.rap
.rwt
.RWT
;
33 import org
.eclipse
.rap
.rwt
.application
.EntryPoint
;
34 import org
.eclipse
.swt
.widgets
.Display
;
35 import org
.eclipse
.ui
.PlatformUI
;
36 import org
.osgi
.framework
.BundleContext
;
37 import org
.springframework
.security
.authentication
.BadCredentialsException
;
38 import org
.springframework
.security
.core
.context
.SecurityContext
;
39 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
42 * RAP entry point with login capabilities. Once the user has been
43 * authenticated, the workbench is run as a privileged action by the related
46 public class SecureEntryPoint
implements EntryPoint
{
47 private final static Log log
= LogFactory
.getLog(SecureEntryPoint
.class);
50 * From org.springframework.security.context.
51 * HttpSessionContextIntegrationFilter
53 protected static final String SPRING_SECURITY_CONTEXT_KEY
= "SPRING_SECURITY_CONTEXT";
56 * How many seconds to wait before invalidating the session if the user has
59 private Integer loginTimeout
= 1 * 60;
60 // TODO make it configurable
61 /** Default session timeout is 8 hours (European working day length) */
62 private Integer sessionTimeout
= 8 * 60 * 60;
64 /** Override to provide an application specific workbench advisor */
65 protected RapWorkbenchAdvisor
createRapWorkbenchAdvisor(String username
) {
66 return new RapWorkbenchAdvisor(username
);
70 public final int createUI() {
71 // Short login timeout so that the modal dialog login doesn't hang
73 RWT
.getRequest().getSession().setMaxInactiveInterval(loginTimeout
);
75 // Try to load security context thanks to the session processing filter
76 HttpServletRequest httpRequest
= RWT
.getRequest();
77 HttpSession httpSession
= httpRequest
.getSession();
78 Object contextFromSessionObject
= httpSession
79 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
);
80 if (contextFromSessionObject
!= null)
82 .setContext((SecurityContext
) contextFromSessionObject
);
84 // if (log.isDebugEnabled())
85 // log.debug("THREAD=" + Thread.currentThread().getId()
86 // + ", sessionStore=" + RWT.getSessionStore().getId()
87 // + ", remote user=" + httpRequest.getRemoteUser());
90 final Display display
= PlatformUI
.createDisplay();
91 Subject subject
= new Subject();
94 BundleContext bc
= SecureRapActivator
.getActivator().getBundleContext();
95 final LoginModule loginModule
= bc
.getService(bc
96 .getServiceReference(LoginModule
.class));
97 loginModule
.initialize(subject
,
98 new DefaultLoginDialog(display
.getActiveShell()), null, null);
100 if (!loginModule
.login()) {
101 throw new ArgeoException("Login failed");
103 } catch (LoginException e1
) {
104 throw new ArgeoException("Login failed", e1
);
107 // final ILoginContext loginContext = SecureRapActivator
108 // .createLoginContext(SecureRapActivator.CONTEXT_SPRING);
109 // tryLogin: while (subject == null && !display.isDisposed()) {
111 // loginContext.login();
112 // subject = loginContext.getSubject();
114 // // add security context to session
115 // if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null)
116 // httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
117 // SecurityContextHolder.getContext());
118 // // add thread locale to RWT session
119 // log.info("Locale " + LocaleUtils.threadLocale.get());
120 // RWT.setLocale(LocaleUtils.threadLocale.get());
122 // // Once the user is logged in, she can have a longer session
124 // RWT.getRequest().getSession()
125 // .setMaxInactiveInterval(sessionTimeout);
126 // if (log.isDebugEnabled())
127 // log.debug("Authenticated " + subject);
128 // } catch (LoginException e) {
129 // BadCredentialsException bce = wasCausedByBadCredentials(e);
130 // if (bce != null) {
131 // MessageDialog.openInformation(display.getActiveShell(),
132 // "Bad Credentials", bce.getMessage());
134 // continue tryLogin;
136 // return processLoginDeath(display, e);
140 final String username
= subject
.getPrincipals().iterator().next()
142 // Logout callback when the display is disposed
143 display
.disposeExec(new Runnable() {
145 log
.debug("Display disposed");
146 // logout(loginContext, username);
148 loginModule
.logout();
149 } catch (LoginException e
) {
150 log
.error("Error when logging out", e
);
158 Integer returnCode
= null;
160 returnCode
= Subject
.doAs(subject
, new PrivilegedAction
<Integer
>() {
161 public Integer
run() {
162 RapWorkbenchAdvisor workbenchAdvisor
= createRapWorkbenchAdvisor(username
);
163 int result
= PlatformUI
.createAndRunWorkbench(display
,
165 return new Integer(result
);
168 // logout(loginContext, username);
175 private Integer
processLoginDeath(Display display
, LoginException e
) {
176 // check thread death
177 ThreadDeath td
= wasCausedByThreadDeath(e
);
182 if (!display
.isDisposed()) {
183 ErrorFeedback
.show("Unexpected exception during authentication", e
);
184 // this was not just bad credentials or death thread
185 RWT
.getRequest().getSession().setMaxInactiveInterval(1);
189 throw new ArgeoException(
190 "Unexpected exception during authentication", e
);
195 /** Recursively look for {@link BadCredentialsException} in the root causes. */
196 private BadCredentialsException
wasCausedByBadCredentials(Throwable t
) {
197 if (t
instanceof BadCredentialsException
)
198 return (BadCredentialsException
) t
;
200 if (t
.getCause() != null)
201 return wasCausedByBadCredentials(t
.getCause());
207 * If there is a {@link ThreadDeath} in the root causes, rethrow it
208 * (important for RAP cleaning mechanism)
210 protected ThreadDeath
wasCausedByThreadDeath(Throwable t
) {
211 if (t
instanceof ThreadDeath
)
212 return (ThreadDeath
) t
;
214 if (t
.getCause() != null)
215 return wasCausedByThreadDeath(t
.getCause());
220 protected void logout(ILoginContext secureContext
, String username
) {
222 HttpServletRequest httpRequest
= RWT
.getRequest();
223 HttpSession httpSession
= httpRequest
.getSession();
224 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
, null);
225 RWT
.getRequest().getSession().setMaxInactiveInterval(1);
226 SecurityContextHolder
.clearContext();
227 secureContext
.logout();
228 log
.info("Logged out " + (username
!= null ? username
: "")
229 + " (THREAD=" + Thread
.currentThread().getId() + ")");
230 } catch (LoginException e
) {
231 log
.error("Erorr when logging out", e
);