1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.security.authentication;
20
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.Enumeration;
24 import java.util.Locale;
25
26 import javax.servlet.RequestDispatcher;
27 import javax.servlet.ServletException;
28 import javax.servlet.ServletRequest;
29 import javax.servlet.ServletResponse;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletRequestWrapper;
32 import javax.servlet.http.HttpServletResponse;
33 import javax.servlet.http.HttpServletResponseWrapper;
34 import javax.servlet.http.HttpSession;
35
36 import org.eclipse.jetty.http.HttpHeaders;
37 import org.eclipse.jetty.http.HttpMethods;
38 import org.eclipse.jetty.http.MimeTypes;
39 import org.eclipse.jetty.security.ServerAuthException;
40 import org.eclipse.jetty.security.UserAuthentication;
41 import org.eclipse.jetty.server.AbstractHttpConnection;
42 import org.eclipse.jetty.server.Authentication;
43 import org.eclipse.jetty.server.Authentication.User;
44 import org.eclipse.jetty.server.Request;
45 import org.eclipse.jetty.server.UserIdentity;
46 import org.eclipse.jetty.util.MultiMap;
47 import org.eclipse.jetty.util.StringUtil;
48 import org.eclipse.jetty.util.URIUtil;
49 import org.eclipse.jetty.util.log.Log;
50 import org.eclipse.jetty.util.log.Logger;
51 import org.eclipse.jetty.util.security.Constraint;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class FormAuthenticator extends LoginAuthenticator
69 {
70 private static final Logger LOG = Log.getLogger(FormAuthenticator.class);
71
72 public final static String __FORM_LOGIN_PAGE="org.eclipse.jetty.security.form_login_page";
73 public final static String __FORM_ERROR_PAGE="org.eclipse.jetty.security.form_error_page";
74 public final static String __FORM_DISPATCH="org.eclipse.jetty.security.dispatch";
75 public final static String __J_URI = "org.eclipse.jetty.security.form_URI";
76 public final static String __J_POST = "org.eclipse.jetty.security.form_POST";
77 public final static String __J_SECURITY_CHECK = "/j_security_check";
78 public final static String __J_USERNAME = "j_username";
79 public final static String __J_PASSWORD = "j_password";
80
81 private String _formErrorPage;
82 private String _formErrorPath;
83 private String _formLoginPage;
84 private String _formLoginPath;
85 private boolean _dispatch;
86 private boolean _alwaysSaveUri;
87
88 public FormAuthenticator()
89 {
90 }
91
92
93 public FormAuthenticator(String login,String error,boolean dispatch)
94 {
95 this();
96 if (login!=null)
97 setLoginPage(login);
98 if (error!=null)
99 setErrorPage(error);
100 _dispatch=dispatch;
101 }
102
103
104
105
106
107
108
109
110
111 public void setAlwaysSaveUri (boolean alwaysSave)
112 {
113 _alwaysSaveUri = alwaysSave;
114 }
115
116
117
118 public boolean getAlwaysSaveUri ()
119 {
120 return _alwaysSaveUri;
121 }
122
123
124
125
126
127 @Override
128 public void setConfiguration(AuthConfiguration configuration)
129 {
130 super.setConfiguration(configuration);
131 String login=configuration.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE);
132 if (login!=null)
133 setLoginPage(login);
134 String error=configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE);
135 if (error!=null)
136 setErrorPage(error);
137 String dispatch=configuration.getInitParameter(FormAuthenticator.__FORM_DISPATCH);
138 _dispatch = dispatch==null?_dispatch:Boolean.valueOf(dispatch);
139 }
140
141
142 public String getAuthMethod()
143 {
144 return Constraint.__FORM_AUTH;
145 }
146
147
148 private void setLoginPage(String path)
149 {
150 if (!path.startsWith("/"))
151 {
152 LOG.warn("form-login-page must start with /");
153 path = "/" + path;
154 }
155 _formLoginPage = path;
156 _formLoginPath = path;
157 if (_formLoginPath.indexOf('?') > 0)
158 _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
159 }
160
161
162 private void setErrorPage(String path)
163 {
164 if (path == null || path.trim().length() == 0)
165 {
166 _formErrorPath = null;
167 _formErrorPage = null;
168 }
169 else
170 {
171 if (!path.startsWith("/"))
172 {
173 LOG.warn("form-error-page must start with /");
174 path = "/" + path;
175 }
176 _formErrorPage = path;
177 _formErrorPath = path;
178
179 if (_formErrorPath.indexOf('?') > 0)
180 _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
181 }
182 }
183
184
185 public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
186 {
187 HttpServletRequest request = (HttpServletRequest)req;
188 HttpServletResponse response = (HttpServletResponse)res;
189 String uri = request.getRequestURI();
190 if (uri==null)
191 uri=URIUtil.SLASH;
192
193 mandatory|=isJSecurityCheck(uri);
194 if (!mandatory)
195 return new DeferredAuthentication(this);
196
197 if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())) &&!DeferredAuthentication.isDeferred(response))
198 return new DeferredAuthentication(this);
199
200 HttpSession session = request.getSession(true);
201
202 try
203 {
204
205 if (isJSecurityCheck(uri))
206 {
207 final String username = request.getParameter(__J_USERNAME);
208 final String password = request.getParameter(__J_PASSWORD);
209
210 UserIdentity user = _loginService.login(username,password);
211 if (user!=null)
212 {
213 session=renewSession(request,response);
214
215
216 String nuri;
217 synchronized(session)
218 {
219 nuri = (String) session.getAttribute(__J_URI);
220
221 if (nuri == null || nuri.length() == 0)
222 {
223 nuri = request.getContextPath();
224 if (nuri.length() == 0)
225 nuri = URIUtil.SLASH;
226 }
227
228 Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
229 session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
230 }
231 response.setContentLength(0);
232 response.sendRedirect(response.encodeRedirectURL(nuri));
233
234 return new FormAuthentication(getAuthMethod(),user);
235 }
236
237
238 if (LOG.isDebugEnabled())
239 LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
240 if (_formErrorPage == null)
241 {
242 if (response != null)
243 response.sendError(HttpServletResponse.SC_FORBIDDEN);
244 }
245 else if (_dispatch)
246 {
247 RequestDispatcher dispatcher = request.getRequestDispatcher(_formErrorPage);
248 response.setHeader(HttpHeaders.CACHE_CONTROL,"No-cache");
249 response.setDateHeader(HttpHeaders.EXPIRES,1);
250 dispatcher.forward(new FormRequest(request), new FormResponse(response));
251 }
252 else
253 {
254 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
255 }
256
257 return Authentication.SEND_FAILURE;
258 }
259
260
261 Authentication authentication = (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
262 if (authentication != null)
263 {
264
265 if (authentication instanceof Authentication.User &&
266 _loginService!=null &&
267 !_loginService.validate(((Authentication.User)authentication).getUserIdentity()))
268 {
269
270 session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
271 }
272 else
273 {
274 String j_uri=(String)session.getAttribute(__J_URI);
275 if (j_uri!=null)
276 {
277 MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
278 if (j_post!=null)
279 {
280 StringBuffer buf = request.getRequestURL();
281 if (request.getQueryString() != null)
282 buf.append("?").append(request.getQueryString());
283
284 if (j_uri.equals(buf.toString()))
285 {
286
287
288
289 session.removeAttribute(__J_POST);
290 Request base_request = (req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
291 base_request.setMethod(HttpMethods.POST);
292 base_request.setParameters(j_post);
293 }
294 }
295 else
296 session.removeAttribute(__J_URI);
297
298 }
299 return authentication;
300 }
301 }
302
303
304 if (DeferredAuthentication.isDeferred(response))
305 {
306 LOG.debug("auth deferred {}",session.getId());
307 return Authentication.UNAUTHENTICATED;
308 }
309
310
311 synchronized (session)
312 {
313
314 if (session.getAttribute(__J_URI)==null || _alwaysSaveUri)
315 {
316 StringBuffer buf = request.getRequestURL();
317 if (request.getQueryString() != null)
318 buf.append("?").append(request.getQueryString());
319 session.setAttribute(__J_URI, buf.toString());
320
321 if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(req.getContentType()) && HttpMethods.POST.equals(request.getMethod()))
322 {
323 Request base_request = (req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
324 base_request.extractParameters();
325 session.setAttribute(__J_POST, new MultiMap<String>(base_request.getParameters()));
326 }
327 }
328 }
329
330
331 if (_dispatch)
332 {
333 RequestDispatcher dispatcher = request.getRequestDispatcher(_formLoginPage);
334 response.setHeader(HttpHeaders.CACHE_CONTROL,"No-cache");
335 response.setDateHeader(HttpHeaders.EXPIRES,1);
336 dispatcher.forward(new FormRequest(request), new FormResponse(response));
337 }
338 else
339 {
340 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
341 }
342 return Authentication.SEND_CONTINUE;
343
344
345 }
346 catch (IOException e)
347 {
348 throw new ServerAuthException(e);
349 }
350 catch (ServletException e)
351 {
352 throw new ServerAuthException(e);
353 }
354 }
355
356
357 public boolean isJSecurityCheck(String uri)
358 {
359 int jsc = uri.indexOf(__J_SECURITY_CHECK);
360
361 if (jsc<0)
362 return false;
363 int e=jsc+__J_SECURITY_CHECK.length();
364 if (e==uri.length())
365 return true;
366 char c = uri.charAt(e);
367 return c==';'||c=='#'||c=='/'||c=='?';
368 }
369
370
371 public boolean isLoginOrErrorPage(String pathInContext)
372 {
373 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
374 }
375
376
377 public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
378 {
379 return true;
380 }
381
382
383
384 protected static class FormRequest extends HttpServletRequestWrapper
385 {
386 public FormRequest(HttpServletRequest request)
387 {
388 super(request);
389 }
390
391 @Override
392 public long getDateHeader(String name)
393 {
394 if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
395 return -1;
396 return super.getDateHeader(name);
397 }
398
399 @Override
400 public String getHeader(String name)
401 {
402 if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
403 return null;
404 return super.getHeader(name);
405 }
406
407 @Override
408 public Enumeration getHeaderNames()
409 {
410 return Collections.enumeration(Collections.list(super.getHeaderNames()));
411 }
412
413 @Override
414 public Enumeration getHeaders(String name)
415 {
416 if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
417 return Collections.enumeration(Collections.EMPTY_LIST);
418 return super.getHeaders(name);
419 }
420 }
421
422
423
424 protected static class FormResponse extends HttpServletResponseWrapper
425 {
426 public FormResponse(HttpServletResponse response)
427 {
428 super(response);
429 }
430
431 @Override
432 public void addDateHeader(String name, long date)
433 {
434 if (notIgnored(name))
435 super.addDateHeader(name,date);
436 }
437
438 @Override
439 public void addHeader(String name, String value)
440 {
441 if (notIgnored(name))
442 super.addHeader(name,value);
443 }
444
445 @Override
446 public void setDateHeader(String name, long date)
447 {
448 if (notIgnored(name))
449 super.setDateHeader(name,date);
450 }
451
452 @Override
453 public void setHeader(String name, String value)
454 {
455 if (notIgnored(name))
456 super.setHeader(name,value);
457 }
458
459 private boolean notIgnored(String name)
460 {
461 if (HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(name) ||
462 HttpHeaders.PRAGMA.equalsIgnoreCase(name) ||
463 HttpHeaders.ETAG.equalsIgnoreCase(name) ||
464 HttpHeaders.EXPIRES.equalsIgnoreCase(name) ||
465 HttpHeaders.LAST_MODIFIED.equalsIgnoreCase(name) ||
466 HttpHeaders.AGE.equalsIgnoreCase(name))
467 return false;
468 return true;
469 }
470 }
471
472
473
474
475
476
477 public static class FormAuthentication extends UserAuthentication implements Authentication.ResponseSent
478 {
479 public FormAuthentication(String method, UserIdentity userIdentity)
480 {
481 super(method,userIdentity);
482 }
483
484 @Override
485 public String toString()
486 {
487 return "Form"+super.toString();
488 }
489 }
490 }