1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.security;
16
17 import java.io.IOException;
18 import java.security.Principal;
19 import java.util.Map;
20
21 import javax.servlet.ServletException;
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpServletResponse;
24
25 import org.mortbay.jetty.Connector;
26 import org.mortbay.jetty.HttpConnection;
27 import org.mortbay.jetty.Request;
28 import org.mortbay.jetty.Response;
29 import org.mortbay.jetty.handler.HandlerWrapper;
30 import org.mortbay.jetty.servlet.PathMap;
31 import org.mortbay.log.Log;
32 import org.mortbay.util.LazyList;
33 import org.mortbay.util.StringUtil;
34
35
36
37
38
39
40
41 public class SecurityHandler extends HandlerWrapper
42 {
43
44 private String _authMethod=Constraint.__BASIC_AUTH;
45 private UserRealm _userRealm;
46 private ConstraintMapping[] _constraintMappings;
47 private PathMap _constraintMap=new PathMap();
48 private Authenticator _authenticator;
49 private NotChecked _notChecked=new NotChecked();
50 private boolean _checkWelcomeFiles=false;
51
52
53
54
55
56
57 public Authenticator getAuthenticator()
58 {
59 return _authenticator;
60 }
61
62
63
64
65
66 public void setAuthenticator(Authenticator authenticator)
67 {
68 _authenticator = authenticator;
69 }
70
71
72
73
74
75 public UserRealm getUserRealm()
76 {
77 return _userRealm;
78 }
79
80
81
82
83
84 public void setUserRealm(UserRealm userRealm)
85 {
86 _userRealm = userRealm;
87 }
88
89
90
91
92
93 public ConstraintMapping[] getConstraintMappings()
94 {
95 return _constraintMappings;
96 }
97
98
99
100
101
102 public void setConstraintMappings(ConstraintMapping[] constraintMappings)
103 {
104 _constraintMappings=constraintMappings;
105 if (_constraintMappings!=null)
106 {
107 this._constraintMappings = constraintMappings;
108 _constraintMap.clear();
109
110 for (int i=0;i<_constraintMappings.length;i++)
111 {
112 Object mappings = _constraintMap.get(_constraintMappings[i].getPathSpec());
113 mappings=LazyList.add(mappings, _constraintMappings[i]);
114 _constraintMap.put(_constraintMappings[i].getPathSpec(),mappings);
115 }
116 }
117 }
118
119
120 public String getAuthMethod()
121 {
122 return _authMethod;
123 }
124
125
126 public void setAuthMethod(String method)
127 {
128 if (isStarted() && _authMethod!=null && !_authMethod.equals(method))
129 throw new IllegalStateException("Handler started");
130 _authMethod = method;
131 }
132
133
134 public boolean hasConstraints()
135 {
136 return _constraintMappings != null && _constraintMappings.length > 0;
137 }
138
139
140
141
142
143 public boolean isCheckWelcomeFiles()
144 {
145 return _checkWelcomeFiles;
146 }
147
148
149
150
151
152 public void setCheckWelcomeFiles(boolean authenticateWelcomeFiles)
153 {
154 _checkWelcomeFiles=authenticateWelcomeFiles;
155 }
156
157 public void doStart()
158 throws Exception
159 {
160 if (_authenticator==null)
161 {
162
163 if (Constraint.__BASIC_AUTH.equalsIgnoreCase(_authMethod))
164 _authenticator=new BasicAuthenticator();
165 else if (Constraint.__DIGEST_AUTH.equalsIgnoreCase(_authMethod))
166 _authenticator=new DigestAuthenticator();
167 else if (Constraint.__CERT_AUTH.equalsIgnoreCase(_authMethod))
168 _authenticator=new ClientCertAuthenticator();
169 else if (Constraint.__FORM_AUTH.equalsIgnoreCase(_authMethod))
170 _authenticator=new FormAuthenticator();
171 else
172 Log.warn("Unknown Authentication method:"+_authMethod);
173 }
174
175 super.doStart();
176 }
177
178
179
180
181
182
183 public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
184 {
185 Request base_request = (request instanceof Request) ? (Request)request:HttpConnection.getCurrentConnection().getRequest();
186 Response base_response = (response instanceof Response) ? (Response)response:HttpConnection.getCurrentConnection().getResponse();
187 UserRealm old_realm = base_request.getUserRealm();
188 try
189 {
190 base_request.setUserRealm(getUserRealm());
191 if (dispatch==REQUEST && !checkSecurityConstraints(target,base_request,base_response))
192 {
193 base_request.setHandled(true);
194 return;
195 }
196
197 if (dispatch==FORWARD && _checkWelcomeFiles && request.getAttribute("org.mortbay.jetty.welcome")!=null)
198 {
199 request.removeAttribute("org.mortbay.jetty.welcome");
200 if (!checkSecurityConstraints(target,base_request,base_response))
201 {
202 base_request.setHandled(true);
203 return;
204 }
205 }
206
207
208 if (_authenticator instanceof FormAuthenticator && target.endsWith(FormAuthenticator.__J_SECURITY_CHECK))
209 {
210 _authenticator.authenticate(getUserRealm(),target,base_request,base_response);
211 base_request.setHandled(true);
212 return;
213 }
214
215 if (getHandler()!=null)
216 getHandler().handle(target, request, response, dispatch);
217 }
218 finally
219 {
220 if (_userRealm!=null)
221 {
222 if (dispatch==REQUEST)
223 {
224 _userRealm.disassociate(base_request.getUserPrincipal());
225 }
226 }
227 base_request.setUserRealm(old_realm);
228 }
229 }
230
231
232
233 public boolean checkSecurityConstraints(
234 String pathInContext,
235 Request request,
236 Response response)
237 throws IOException
238 {
239 Object mapping_entries= _constraintMap.getLazyMatches(pathInContext);
240 String pattern=null;
241 Object constraints= null;
242
243
244
245
246
247 if (mapping_entries!=null)
248 {
249 loop: for (int m=0;m<LazyList.size(mapping_entries); m++)
250 {
251 Map.Entry entry= (Map.Entry)LazyList.get(mapping_entries,m);
252 Object mappings= entry.getValue();
253 String path_spec=(String)entry.getKey();
254
255 for (int c=0;c<LazyList.size(mappings);c++)
256 {
257 ConstraintMapping mapping=(ConstraintMapping)LazyList.get(mappings,c);
258 if (mapping.getMethod()!=null && !mapping.getMethod().equalsIgnoreCase(request.getMethod()))
259 continue;
260
261 if (pattern!=null && !pattern.equals(path_spec))
262 break loop;
263
264 pattern=path_spec;
265 constraints= LazyList.add(constraints, mapping.getConstraint());
266 }
267 }
268
269 return check(constraints,_authenticator,_userRealm,pathInContext,request,response);
270 }
271
272 request.setUserPrincipal(_notChecked);
273 return true;
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288 private boolean check(
289 Object constraints,
290 Authenticator authenticator,
291 UserRealm realm,
292 String pathInContext,
293 Request request,
294 Response response)
295 throws IOException
296 {
297
298 int dataConstraint= Constraint.DC_NONE;
299 Object roles= null;
300 boolean unauthenticated= false;
301 boolean forbidden= false;
302
303 for (int c= 0; c < LazyList.size(constraints); c++)
304 {
305 Constraint sc= (Constraint)LazyList.get(constraints,c);
306
307
308 if (dataConstraint > Constraint.DC_UNSET && sc.hasDataConstraint())
309 {
310 if (sc.getDataConstraint() > dataConstraint)
311 dataConstraint= sc.getDataConstraint();
312 }
313 else
314 dataConstraint= Constraint.DC_UNSET;
315
316
317 if (!unauthenticated && !forbidden)
318 {
319 if (sc.getAuthenticate())
320 {
321 if (sc.isAnyRole())
322 {
323 roles= Constraint.ANY_ROLE;
324 }
325 else
326 {
327 String[] scr= sc.getRoles();
328 if (scr == null || scr.length == 0)
329 {
330 forbidden= true;
331 break;
332 }
333 else
334 {
335
336 if (roles != Constraint.ANY_ROLE)
337 {
338 for (int r=scr.length;r-->0;)
339 roles= LazyList.add(roles, scr[r]);
340 }
341 }
342 }
343 }
344 else
345 unauthenticated= true;
346 }
347 }
348
349
350 if (forbidden &&
351 (!(authenticator instanceof FormAuthenticator) ||
352 !((FormAuthenticator)authenticator).isLoginOrErrorPage(pathInContext)))
353 {
354
355 response.sendError(HttpServletResponse.SC_FORBIDDEN);
356 return false;
357 }
358
359
360 if (dataConstraint > Constraint.DC_NONE)
361 {
362 HttpConnection connection = HttpConnection.getCurrentConnection();
363 Connector connector = connection.getConnector();
364
365 switch (dataConstraint)
366 {
367 case Constraint.DC_INTEGRAL :
368 if (connector.isIntegral(request))
369 break;
370 if (connector.getConfidentialPort() > 0)
371 {
372 String url=
373 connector.getIntegralScheme()
374 + "://"
375 + request.getServerName()
376 + ":"
377 + connector.getIntegralPort()
378 + request.getRequestURI();
379 if (request.getQueryString() != null)
380 url += "?" + request.getQueryString();
381 response.setContentLength(0);
382 response.sendRedirect(response.encodeRedirectURL(url));
383 }
384 else
385 response.sendError(Response.SC_FORBIDDEN,null);
386 return false;
387 case Constraint.DC_CONFIDENTIAL :
388 if (connector.isConfidential(request))
389 break;
390
391 if (connector.getConfidentialPort() > 0)
392 {
393 String url=
394 connector.getConfidentialScheme()
395 + "://"
396 + request.getServerName()
397 + ":"
398 + connector.getConfidentialPort()
399 + request.getRequestURI();
400 if (request.getQueryString() != null)
401 url += "?" + request.getQueryString();
402
403 response.setContentLength(0);
404 response.sendRedirect(response.encodeRedirectURL(url));
405 }
406 else
407 response.sendError(Response.SC_FORBIDDEN,null);
408 return false;
409
410 default :
411 response.sendError(Response.SC_FORBIDDEN,null);
412 return false;
413 }
414 }
415
416
417 if (!unauthenticated && roles != null)
418 {
419 if (realm == null)
420 {
421 Log.warn("Request "+request.getRequestURI()+" failed - no realm");
422 response.sendError(Response.SC_INTERNAL_SERVER_ERROR,"No realm");
423 return false;
424 }
425
426 Principal user= null;
427
428
429 if (request.getAuthType() != null && request.getRemoteUser() != null)
430 {
431
432 user= request.getUserPrincipal();
433 if (user == null)
434 user= realm.authenticate(request.getRemoteUser(), null, request);
435 if (user == null && authenticator != null)
436 user= authenticator.authenticate(realm, pathInContext, request, response);
437 }
438 else if (authenticator != null)
439 {
440
441 user= authenticator.authenticate(realm, pathInContext, request, response);
442 }
443 else
444 {
445
446 Log.warn("Mis-configured Authenticator for " + request.getRequestURI());
447 response.sendError(Response.SC_INTERNAL_SERVER_ERROR,"Configuration error");
448 }
449
450
451 if (user == null)
452 return false;
453 else if (user == __NOBODY)
454 return true;
455
456 if (roles != Constraint.ANY_ROLE)
457 {
458 boolean inRole= false;
459 for (int r= LazyList.size(roles); r-- > 0;)
460 {
461 if (realm.isUserInRole(user, (String)LazyList.get(roles, r)))
462 {
463 inRole= true;
464 break;
465 }
466 }
467
468 if (!inRole)
469 {
470 Log.warn("AUTH FAILURE: incorrect role for " + StringUtil.printable(user.getName()));
471
472
473
474 response.sendError(Response.SC_FORBIDDEN,"User not in required role");
475 return false;
476 }
477 }
478 }
479 else
480 {
481 request.setUserPrincipal(_notChecked);
482 }
483
484 return true;
485 }
486
487 public static Principal __NO_USER = new Principal()
488 {
489 public String getName()
490 {
491 return null;
492 }
493 public String toString()
494 {
495 return "No User";
496 }
497 };
498
499 public class NotChecked implements Principal
500 {
501 public String getName()
502 {
503 return null;
504 }
505 public String toString()
506 {
507 return "NOT CHECKED";
508 }
509 public SecurityHandler getSecurityHandler()
510 {
511 return SecurityHandler.this;
512 }
513 };
514
515
516
517
518
519
520
521
522
523
524
525
526 public static Principal __NOBODY = new Principal()
527 {
528 public String getName()
529 {
530 return "Nobody";
531 }
532
533 public String toString()
534 {
535 return getName();
536 }
537 };
538 }
539