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