]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java
Localis(z)ation support
[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.AccessControlContext;
19 import java.security.AccessController;
20 import java.security.PrivilegedAction;
21
22 import javax.security.auth.Subject;
23 import javax.security.auth.callback.CallbackHandler;
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;
30
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.widgets.auth.DefaultLoginDialog;
36 import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
37 import org.eclipse.jface.dialogs.MessageDialog;
38 import org.eclipse.rap.rwt.RWT;
39 import org.eclipse.rap.rwt.application.EntryPoint;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.ui.PlatformUI;
42
43 /**
44 * RAP entry point with login capabilities. Once the user has been
45 * authenticated, the workbench is run as a privileged action by the related
46 * subject.
47 */
48 public class SecureEntryPoint implements EntryPoint {
49 final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext";
50 private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
51
52 /**
53 * How many seconds to wait before invalidating the session if the user has
54 * not yet logged in.
55 */
56 private Integer loginTimeout = 1 * 60;
57 // TODO make it configurable
58 /** Default session timeout is 8 hours (European working day length) */
59 private Integer sessionTimeout = 8 * 60 * 60;
60
61 /** Override to provide an application specific workbench advisor */
62 protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
63 return new RapWorkbenchAdvisor(username);
64 }
65
66 @Override
67 public final int createUI() {
68 // Short login timeout so that the modal dialog login doesn't hang
69 // around too long
70 RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
71
72 final Display display = PlatformUI.createDisplay();
73
74 // load context from session
75 HttpServletRequest httpRequest = RWT.getRequest();
76 final HttpSession httpSession = httpRequest.getSession();
77 AccessControlContext acc = (AccessControlContext) httpSession
78 .getAttribute(ACCESS_CONTROL_CONTEXT);
79
80 final Subject subject;
81 if (acc != null
82 && Subject.getSubject(acc).getPrincipals(X500Principal.class)
83 .size() == 1) {
84 subject = Subject.getSubject(acc);
85 } else {
86 subject = new Subject();
87
88 final LoginContext loginContext;
89 try {
90 CallbackHandler callbackHandler = new DefaultLoginDialog(
91 display.getActiveShell());
92 loginContext = new LoginContext(
93 AuthConstants.LOGIN_CONTEXT_USER, subject,
94 callbackHandler);
95 } catch (LoginException e1) {
96 throw new ArgeoException("Cannot initialize login context", e1);
97 }
98
99 tryLogin: while (subject.getPrincipals(X500Principal.class).size() == 0) {
100 try {
101 loginContext.login();
102 if (subject.getPrincipals(X500Principal.class).size() == 0)
103 throw new ArgeoException("Login succeeded but no auth");// fatal
104
105 // add thread locale to RWT session
106 // if (log.isTraceEnabled())
107 // log.trace("Locale " + LocaleUtils.threadLocale.get());
108 // RWT.setLocale(LocaleUtils.threadLocale.get());
109
110 // once the user is logged in, longer session timeout
111 RWT.getRequest().getSession()
112 .setMaxInactiveInterval(sessionTimeout);
113
114 if (log.isDebugEnabled())
115 log.debug("Authenticated " + subject);
116 } catch (FailedLoginException e) {
117 MessageDialog.openInformation(display.getActiveShell(),
118 "Bad Credentials", e.getMessage());
119 // retry login
120 continue tryLogin;
121 } catch (LoginException e) {
122 return processLoginDeath(display, e);
123 }
124 }
125 }
126 final String username = subject.getPrincipals(X500Principal.class)
127 .iterator().next().getName();
128 // Logout callback when the display is disposed
129 display.disposeExec(new Runnable() {
130 public void run() {
131 if (log.isTraceEnabled())
132 log.trace("Display disposed");
133 try {
134 LoginContext loginContext = new LoginContext(
135 AuthConstants.LOGIN_CONTEXT_USER, subject);
136 loginContext.logout();
137 } catch (LoginException e) {
138 log.error("Error when logging out", e);
139 }
140 }
141 });
142
143 //
144 // RUN THE WORKBENCH
145 //
146 Integer returnCode = null;
147 try {
148 returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
149 public Integer run() {
150 // add security context to session
151 httpSession.setAttribute(ACCESS_CONTROL_CONTEXT,
152 AccessController.getContext());
153
154 // start workbench
155 RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
156 int result = PlatformUI.createAndRunWorkbench(display,
157 workbenchAdvisor);
158 return new Integer(result);
159 }
160 });
161 // Explicit exit from workbench
162 fullLogout(subject, username);
163 } finally {
164 display.dispose();
165 }
166 return returnCode;
167 }
168
169 private Integer processLoginDeath(Display display, LoginException e) {
170 // check thread death
171 ThreadDeath td = wasCausedByThreadDeath(e);
172 if (td != null) {
173 display.dispose();
174 throw td;
175 }
176 if (!display.isDisposed()) {
177 ErrorFeedback.show("Unexpected exception during authentication", e);
178 // this was not just bad credentials or death thread
179 RWT.getRequest().getSession().setMaxInactiveInterval(1);
180 display.dispose();
181 return -1;
182 } else {
183 throw new ArgeoException(
184 "Unexpected exception during authentication", e);
185 }
186
187 }
188
189 /**
190 * If there is a {@link ThreadDeath} in the root causes, rethrow it
191 * (important for RAP cleaning mechanism)
192 */
193 protected ThreadDeath wasCausedByThreadDeath(Throwable t) {
194 if (t instanceof ThreadDeath)
195 return (ThreadDeath) t;
196
197 if (t.getCause() != null)
198 return wasCausedByThreadDeath(t.getCause());
199 else
200 return null;
201 }
202
203 private void fullLogout(Subject subject, String username) {
204 try {
205 LoginContext loginContext = new LoginContext(
206 AuthConstants.LOGIN_CONTEXT_USER, subject);
207 loginContext.logout();
208 HttpServletRequest httpRequest = RWT.getRequest();
209 HttpSession httpSession = httpRequest.getSession();
210 httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null);
211 RWT.getRequest().getSession().setMaxInactiveInterval(1);
212 log.info("Logged out " + (username != null ? username : "")
213 + " (THREAD=" + Thread.currentThread().getId() + ")");
214 } catch (LoginException e) {
215 log.error("Error when logging out", e);
216 }
217 }
218 }