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