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
44
45
46
47
48 public class FormAuthModule extends BaseAuthModule
49 {
50
51 public final static String __J_URI = "org.eclipse.jetty.util.URI";
52
53 public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth";
54
55 public final static String __J_SECURITY_CHECK = "/j_security_check";
56
57 public final static String __J_USERNAME = "j_username";
58
59 public final static String __J_PASSWORD = "j_password";
60
61
62 public static final String LOGIN_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.LoginPage";
63
64 public static final String ERROR_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.ErrorPage";
65
66 public static final String SSO_SOURCE_KEY = "org.eclipse.jetty.security.jaspi.modules.SsoSource";
67
68 private String _formErrorPage;
69
70 private String _formErrorPath;
71
72 private String _formLoginPage;
73
74 private String _formLoginPath;
75
76 private CrossContextPsuedoSession<UserInfo> ssoSource;
77
78 public FormAuthModule()
79 {
80 }
81
82 public FormAuthModule(CallbackHandler callbackHandler, String loginPage, String errorPage)
83 {
84 super(callbackHandler);
85 setLoginPage(loginPage);
86 setErrorPage(errorPage);
87 }
88
89 public FormAuthModule(CallbackHandler callbackHandler, CrossContextPsuedoSession<UserInfo> ssoSource,
90 String loginPage, String errorPage)
91 {
92 super(callbackHandler);
93 this.ssoSource = ssoSource;
94 setLoginPage(loginPage);
95 setErrorPage(errorPage);
96 }
97
98 @Override
99 public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
100 CallbackHandler handler, Map options)
101 throws AuthException
102 {
103 super.initialize(requestPolicy, responsePolicy, handler, options);
104 setLoginPage((String) options.get(LOGIN_PAGE_KEY));
105 setErrorPage((String) options.get(ERROR_PAGE_KEY));
106 ssoSource = (CrossContextPsuedoSession<UserInfo>) options.get(SSO_SOURCE_KEY);
107 }
108
109 private void setLoginPage(String path)
110 {
111 if (!path.startsWith("/"))
112 {
113 Log.warn("form-login-page must start with /");
114 path = "/" + path;
115 }
116 _formLoginPage = path;
117 _formLoginPath = path;
118 if (_formLoginPath.indexOf('?') > 0) _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
119 }
120
121
122 private void setErrorPage(String path)
123 {
124 if (path == null || path.trim().length() == 0)
125 {
126 _formErrorPath = null;
127 _formErrorPage = null;
128 }
129 else
130 {
131 if (!path.startsWith("/"))
132 {
133 Log.warn("form-error-page must start with /");
134 path = "/" + path;
135 }
136 _formErrorPage = path;
137 _formErrorPath = path;
138
139 if (_formErrorPath.indexOf('?') > 0) _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
140 }
141 }
142
143 @Override
144 public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
145 {
146 HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
147 HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
148 HttpSession session = request.getSession(isMandatory(messageInfo));
149 String uri = request.getPathInfo();
150
151 if (session == null || isLoginOrErrorPage(uri)) return AuthStatus.SUCCESS;
152
153 try
154 {
155
156
157 if (uri.endsWith(__J_SECURITY_CHECK))
158 {
159
160 final String username = request.getParameter(__J_USERNAME);
161 final String password = request.getParameter(__J_PASSWORD);
162 boolean success = tryLogin(messageInfo, clientSubject, response, session, username, new Password(password));
163 if (success)
164 {
165
166 String nuri = (String) session.getAttribute(__J_URI);
167 if (nuri == null || nuri.length() == 0)
168 {
169 nuri = request.getContextPath();
170 if (nuri.length() == 0) nuri = URIUtil.SLASH;
171 }
172 session.removeAttribute(__J_URI);
173
174 response.setContentLength(0);
175 response.sendRedirect(response.encodeRedirectURL(nuri));
176
177 return AuthStatus.SEND_CONTINUE;
178 }
179
180 if (Log.isDebugEnabled()) Log.debug("Form authentication FAILED for " + StringUtil.printable(username));
181 if (_formErrorPage == null)
182 {
183 if (response != null) response.sendError(HttpServletResponse.SC_FORBIDDEN);
184 }
185 else
186 {
187 response.setContentLength(0);
188 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formErrorPage)));
189 }
190
191
192 return AuthStatus.SEND_FAILURE;
193 }
194
195 FormCredential form_cred = (FormCredential) session.getAttribute(__J_AUTHENTICATED);
196
197 if (form_cred != null)
198 {
199 boolean success = tryLogin(messageInfo, clientSubject, response, session, form_cred._jUserName, new Password(new String(form_cred._jPassword)));
200 if (success) { return AuthStatus.SUCCESS; }
201
202
203
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 else if (ssoSource != null)
285 {
286 UserInfo userInfo = ssoSource.fetch(request);
287 if (userInfo != null)
288 {
289 boolean success = tryLogin(messageInfo, clientSubject, response, session, userInfo.getUserName(), new Password(new String(userInfo.getPassword())));
290 if (success) { return AuthStatus.SUCCESS; }
291 }
292 }
293
294
295 if (!isMandatory(messageInfo) || isLoginOrErrorPage(uri))
296
297 return AuthStatus.SUCCESS;
298
299
300 if (request.getQueryString() != null) uri += "?" + request.getQueryString();
301 session.setAttribute(__J_URI, request.getScheme() + "://"
302 + request.getServerName()
303 + ":"
304 + request.getServerPort()
305 + URIUtil.addPaths(request.getContextPath(), uri));
306 response.setContentLength(0);
307 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formLoginPage)));
308 return AuthStatus.SEND_CONTINUE;
309 }
310 catch (IOException e)
311 {
312 throw new AuthException(e.getMessage());
313 }
314 catch (UnsupportedCallbackException e)
315 {
316 throw new AuthException(e.getMessage());
317 }
318
319 }
320
321 private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject,
322 HttpServletResponse response, HttpSession session,
323 String username, Password password)
324 throws AuthException, IOException, UnsupportedCallbackException
325 {
326 if (login(clientSubject, username, password, Constraint.__FORM_AUTH, messageInfo))
327 {
328 char[] pwdChars = password.toString().toCharArray();
329 Set<LoginCallbackImpl> loginCallbacks = clientSubject.getPrivateCredentials(LoginCallbackImpl.class);
330 if (!loginCallbacks.isEmpty())
331 {
332 LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
333 FormCredential form_cred = new FormCredential(username, pwdChars, loginCallback.getUserPrincipal());
334
335 session.setAttribute(__J_AUTHENTICATED, form_cred);
336 }
337
338
339 if (ssoSource != null)
340 {
341 UserInfo userInfo = new UserInfo(username, pwdChars);
342 ssoSource.store(userInfo, response);
343 }
344 return true;
345 }
346 return false;
347
348
349
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 public boolean isLoginOrErrorPage(String pathInContext)
378 {
379 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
380 }
381
382
383
384
385
386 private static class FormCredential implements Serializable, HttpSessionBindingListener
387 {
388 String _jUserName;
389
390 char[] _jPassword;
391
392 transient Principal _userPrincipal;
393
394 private FormCredential(String _jUserName, char[] _jPassword, Principal _userPrincipal)
395 {
396 this._jUserName = _jUserName;
397 this._jPassword = _jPassword;
398 this._userPrincipal = _userPrincipal;
399 }
400
401 public void valueBound(HttpSessionBindingEvent event)
402 {
403 }
404
405 public void valueUnbound(HttpSessionBindingEvent event)
406 {
407 if (Log.isDebugEnabled()) Log.debug("Logout " + _jUserName);
408
409
410
411
412
413
414
415 }
416
417 public int hashCode()
418 {
419 return _jUserName.hashCode() + _jPassword.hashCode();
420 }
421
422 public boolean equals(Object o)
423 {
424 if (!(o instanceof FormCredential)) return false;
425 FormCredential fc = (FormCredential) o;
426 return _jUserName.equals(fc._jUserName) && Arrays.equals(_jPassword, fc._jPassword);
427 }
428
429 public String toString()
430 {
431 return "Cred[" + _jUserName + "]";
432 }
433
434 }
435
436 }