1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.security.jaspi.modules;
15
16 import java.io.IOException;
17 import java.io.Serializable;
18 import java.security.Principal;
19 import java.util.Arrays;
20 import java.util.Map;
21 import java.util.Set;
22
23 import javax.security.auth.Subject;
24 import javax.security.auth.callback.CallbackHandler;
25 import javax.security.auth.callback.UnsupportedCallbackException;
26 import javax.security.auth.message.AuthException;
27 import javax.security.auth.message.AuthStatus;
28 import javax.security.auth.message.MessageInfo;
29 import javax.security.auth.message.MessagePolicy;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32 import javax.servlet.http.HttpSession;
33 import javax.servlet.http.HttpSessionBindingEvent;
34 import javax.servlet.http.HttpSessionBindingListener;
35
36 import org.eclipse.jetty.http.security.Constraint;
37 import org.eclipse.jetty.http.security.Password;
38 import org.eclipse.jetty.security.CrossContextPsuedoSession;
39 import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
40 import org.eclipse.jetty.util.StringUtil;
41 import org.eclipse.jetty.util.URIUtil;
42 import org.eclipse.jetty.util.log.Log;
43 import org.eclipse.jetty.util.log.Logger;
44
45
46
47
48
49 public class FormAuthModule extends BaseAuthModule
50 {
51 private static final Logger LOG = Log.getLogger(FormAuthModule.class);
52
53
54 public final static String __J_URI = "org.eclipse.jetty.util.URI";
55
56 public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth";
57
58 public final static String __J_SECURITY_CHECK = "/j_security_check";
59
60 public final static String __J_USERNAME = "j_username";
61
62 public final static String __J_PASSWORD = "j_password";
63
64
65 public static final String LOGIN_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.LoginPage";
66
67 public static final String ERROR_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.ErrorPage";
68
69 public static final String SSO_SOURCE_KEY = "org.eclipse.jetty.security.jaspi.modules.SsoSource";
70
71 private String _formErrorPage;
72
73 private String _formErrorPath;
74
75 private String _formLoginPage;
76
77 private String _formLoginPath;
78
79 private CrossContextPsuedoSession<UserInfo> ssoSource;
80
81 public FormAuthModule()
82 {
83 }
84
85 public FormAuthModule(CallbackHandler callbackHandler, String loginPage, String errorPage)
86 {
87 super(callbackHandler);
88 setLoginPage(loginPage);
89 setErrorPage(errorPage);
90 }
91
92 public FormAuthModule(CallbackHandler callbackHandler, CrossContextPsuedoSession<UserInfo> ssoSource,
93 String loginPage, String errorPage)
94 {
95 super(callbackHandler);
96 this.ssoSource = ssoSource;
97 setLoginPage(loginPage);
98 setErrorPage(errorPage);
99 }
100
101 @Override
102 public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
103 CallbackHandler handler, Map options)
104 throws AuthException
105 {
106 super.initialize(requestPolicy, responsePolicy, handler, options);
107 setLoginPage((String) options.get(LOGIN_PAGE_KEY));
108 setErrorPage((String) options.get(ERROR_PAGE_KEY));
109 ssoSource = (CrossContextPsuedoSession<UserInfo>) options.get(SSO_SOURCE_KEY);
110 }
111
112 private void setLoginPage(String path)
113 {
114 if (!path.startsWith("/"))
115 {
116 LOG.warn("form-login-page must start with /");
117 path = "/" + path;
118 }
119 _formLoginPage = path;
120 _formLoginPath = path;
121 if (_formLoginPath.indexOf('?') > 0) _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
122 }
123
124
125 private void setErrorPage(String path)
126 {
127 if (path == null || path.trim().length() == 0)
128 {
129 _formErrorPath = null;
130 _formErrorPage = null;
131 }
132 else
133 {
134 if (!path.startsWith("/"))
135 {
136 LOG.warn("form-error-page must start with /");
137 path = "/" + path;
138 }
139 _formErrorPage = path;
140 _formErrorPath = path;
141
142 if (_formErrorPath.indexOf('?') > 0) _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
143 }
144 }
145
146 @Override
147 public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
148 {
149 HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
150 HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
151 HttpSession session = request.getSession(isMandatory(messageInfo));
152 String uri = request.getPathInfo();
153
154 if (session == null || isLoginOrErrorPage(uri)) return AuthStatus.SUCCESS;
155
156 try
157 {
158
159
160 if (uri.endsWith(__J_SECURITY_CHECK))
161 {
162
163 final String username = request.getParameter(__J_USERNAME);
164 final String password = request.getParameter(__J_PASSWORD);
165 boolean success = tryLogin(messageInfo, clientSubject, response, session, username, new Password(password));
166 if (success)
167 {
168
169 String nuri = (String) session.getAttribute(__J_URI);
170 if (nuri == null || nuri.length() == 0)
171 {
172 nuri = request.getContextPath();
173 if (nuri.length() == 0) nuri = URIUtil.SLASH;
174 }
175 session.removeAttribute(__J_URI);
176
177 response.setContentLength(0);
178 response.sendRedirect(response.encodeRedirectURL(nuri));
179
180 return AuthStatus.SEND_CONTINUE;
181 }
182
183 if (LOG.isDebugEnabled()) LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
184 if (_formErrorPage == null)
185 {
186 if (response != null) response.sendError(HttpServletResponse.SC_FORBIDDEN);
187 }
188 else
189 {
190 response.setContentLength(0);
191 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formErrorPage)));
192 }
193
194
195 return AuthStatus.SEND_FAILURE;
196 }
197
198 FormCredential form_cred = (FormCredential) session.getAttribute(__J_AUTHENTICATED);
199
200 if (form_cred != null)
201 {
202 boolean success = tryLogin(messageInfo, clientSubject, response, session, form_cred._jUserName, new Password(new String(form_cred._jPassword)));
203 if (success) { return AuthStatus.SUCCESS; }
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 }
287 else if (ssoSource != null)
288 {
289 UserInfo userInfo = ssoSource.fetch(request);
290 if (userInfo != null)
291 {
292 boolean success = tryLogin(messageInfo, clientSubject, response, session, userInfo.getUserName(), new Password(new String(userInfo.getPassword())));
293 if (success) { return AuthStatus.SUCCESS; }
294 }
295 }
296
297
298 if (!isMandatory(messageInfo) || isLoginOrErrorPage(uri))
299
300 return AuthStatus.SUCCESS;
301
302
303 if (request.getQueryString() != null) uri += "?" + request.getQueryString();
304 session.setAttribute(__J_URI, request.getScheme() + "://"
305 + request.getServerName()
306 + ":"
307 + request.getServerPort()
308 + URIUtil.addPaths(request.getContextPath(), uri));
309 response.setContentLength(0);
310 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formLoginPage)));
311 return AuthStatus.SEND_CONTINUE;
312 }
313 catch (IOException e)
314 {
315 throw new AuthException(e.getMessage());
316 }
317 catch (UnsupportedCallbackException e)
318 {
319 throw new AuthException(e.getMessage());
320 }
321
322 }
323
324 private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject,
325 HttpServletResponse response, HttpSession session,
326 String username, Password password)
327 throws AuthException, IOException, UnsupportedCallbackException
328 {
329 if (login(clientSubject, username, password, Constraint.__FORM_AUTH, messageInfo))
330 {
331 char[] pwdChars = password.toString().toCharArray();
332 Set<LoginCallbackImpl> loginCallbacks = clientSubject.getPrivateCredentials(LoginCallbackImpl.class);
333 if (!loginCallbacks.isEmpty())
334 {
335 LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
336 FormCredential form_cred = new FormCredential(username, pwdChars, loginCallback.getUserPrincipal());
337
338 session.setAttribute(__J_AUTHENTICATED, form_cred);
339 }
340
341
342 if (ssoSource != null)
343 {
344 UserInfo userInfo = new UserInfo(username, pwdChars);
345 ssoSource.store(userInfo, response);
346 }
347 return true;
348 }
349 return false;
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378 }
379
380 public boolean isLoginOrErrorPage(String pathInContext)
381 {
382 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
383 }
384
385
386
387
388
389 private static class FormCredential implements Serializable, HttpSessionBindingListener
390 {
391 String _jUserName;
392
393 char[] _jPassword;
394
395 transient Principal _userPrincipal;
396
397 private FormCredential(String _jUserName, char[] _jPassword, Principal _userPrincipal)
398 {
399 this._jUserName = _jUserName;
400 this._jPassword = _jPassword;
401 this._userPrincipal = _userPrincipal;
402 }
403
404 public void valueBound(HttpSessionBindingEvent event)
405 {
406 }
407
408 public void valueUnbound(HttpSessionBindingEvent event)
409 {
410 if (LOG.isDebugEnabled()) LOG.debug("Logout " + _jUserName);
411
412
413
414
415
416
417
418 }
419
420 public int hashCode()
421 {
422 return _jUserName.hashCode() + _jPassword.hashCode();
423 }
424
425 public boolean equals(Object o)
426 {
427 if (!(o instanceof FormCredential)) return false;
428 FormCredential fc = (FormCredential) o;
429 return _jUserName.equals(fc._jUserName) && Arrays.equals(_jPassword, fc._jPassword);
430 }
431
432 public String toString()
433 {
434 return "Cred[" + _jUserName + "]";
435 }
436
437 }
438
439 }