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