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