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