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