]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java
Starts workbench thanks to a fragment
[lgpl/argeo-commons.git] / org.argeo.security.ui.rap / src / org / argeo / security / ui / rap / SecureEntryPoint.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16 package org.argeo.security.ui.rap;
17
18 import java.security.PrivilegedAction;
19
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;
25
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;
40
41 /**
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
44 * subject.
45 */
46 public class SecureEntryPoint implements EntryPoint {
47 private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
48
49 /**
50 * From org.springframework.security.context.
51 * HttpSessionContextIntegrationFilter
52 */
53 protected static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
54
55 /**
56 * How many seconds to wait before invalidating the session if the user has
57 * not yet logged in.
58 */
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;
63
64 /** Override to provide an application specific workbench advisor */
65 protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
66 return new RapWorkbenchAdvisor(username);
67 }
68
69 @Override
70 public final int createUI() {
71 // Short login timeout so that the modal dialog login doesn't hang
72 // around too long
73 RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
74
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)
81 SecurityContextHolder
82 .setContext((SecurityContext) contextFromSessionObject);
83
84 // if (log.isDebugEnabled())
85 // log.debug("THREAD=" + Thread.currentThread().getId()
86 // + ", sessionStore=" + RWT.getSessionStore().getId()
87 // + ", remote user=" + httpRequest.getRemoteUser());
88
89 // create display
90 final Display display = PlatformUI.createDisplay();
91 Subject subject = new Subject();
92
93 // log in
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);
99 try {
100 if (!loginModule.login()) {
101 throw new ArgeoException("Login failed");
102 }
103 } catch (LoginException e1) {
104 throw new ArgeoException("Login failed", e1);
105 }
106
107 // final ILoginContext loginContext = SecureRapActivator
108 // .createLoginContext(SecureRapActivator.CONTEXT_SPRING);
109 // tryLogin: while (subject == null && !display.isDisposed()) {
110 // try {
111 // loginContext.login();
112 // subject = loginContext.getSubject();
113 //
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());
121 //
122 // // Once the user is logged in, she can have a longer session
123 // // timeout
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());
133 // // retry login
134 // continue tryLogin;
135 // }
136 // return processLoginDeath(display, e);
137 // }
138 // }
139
140 final String username = subject.getPrincipals().iterator().next()
141 .getName();
142 // Logout callback when the display is disposed
143 display.disposeExec(new Runnable() {
144 public void run() {
145 log.debug("Display disposed");
146 // logout(loginContext, username);
147 try {
148 loginModule.logout();
149 } catch (LoginException e) {
150 log.error("Error when logging out", e);
151 }
152 }
153 });
154
155 //
156 // RUN THE WORKBENCH
157 //
158 Integer returnCode = null;
159 try {
160 returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
161 public Integer run() {
162 RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
163 int result = PlatformUI.createAndRunWorkbench(display,
164 workbenchAdvisor);
165 return new Integer(result);
166 }
167 });
168 // logout(loginContext, username);
169 } finally {
170 display.dispose();
171 }
172 return returnCode;
173 }
174
175 private Integer processLoginDeath(Display display, LoginException e) {
176 // check thread death
177 ThreadDeath td = wasCausedByThreadDeath(e);
178 if (td != null) {
179 display.dispose();
180 throw td;
181 }
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);
186 display.dispose();
187 return -1;
188 } else {
189 throw new ArgeoException(
190 "Unexpected exception during authentication", e);
191 }
192
193 }
194
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;
199
200 if (t.getCause() != null)
201 return wasCausedByBadCredentials(t.getCause());
202 else
203 return null;
204 }
205
206 /**
207 * If there is a {@link ThreadDeath} in the root causes, rethrow it
208 * (important for RAP cleaning mechanism)
209 */
210 protected ThreadDeath wasCausedByThreadDeath(Throwable t) {
211 if (t instanceof ThreadDeath)
212 return (ThreadDeath) t;
213
214 if (t.getCause() != null)
215 return wasCausedByThreadDeath(t.getCause());
216 else
217 return null;
218 }
219
220 protected void logout(ILoginContext secureContext, String username) {
221 try {
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);
232 }
233 }
234 }