View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.security;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.Set;
32  import java.util.concurrent.CopyOnWriteArrayList;
33  import java.util.concurrent.CopyOnWriteArraySet;
34  
35  import javax.servlet.HttpConstraintElement;
36  import javax.servlet.HttpMethodConstraintElement;
37  import javax.servlet.ServletSecurityElement;
38  import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
39  import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
40  
41  import org.eclipse.jetty.http.HttpStatus;
42  import org.eclipse.jetty.http.PathMap;
43  import org.eclipse.jetty.server.HttpChannel;
44  import org.eclipse.jetty.server.HttpConfiguration;
45  import org.eclipse.jetty.server.Request;
46  import org.eclipse.jetty.server.Response;
47  import org.eclipse.jetty.server.UserIdentity;
48  import org.eclipse.jetty.util.security.Constraint;
49  
50  /* ------------------------------------------------------------ */
51  /**
52   * Handler to enforce SecurityConstraints. This implementation is servlet spec
53   * 3.0 compliant and pre-computes the constraint combinations for runtime
54   * efficiency.
55   *
56   */
57  public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
58  {
59      private static final String OMISSION_SUFFIX = ".omission";
60      private static final String ALL_METHODS = "*";
61      private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<>();
62      private final Set<String> _roles = new CopyOnWriteArraySet<>();
63      private final PathMap<Map<String, RoleInfo>> _constraintMap = new PathMap<>();
64      private boolean _strict = true;
65  
66      /* ------------------------------------------------------------ */
67      /**
68       * @return
69       */
70      public static Constraint createConstraint()
71      {
72          return new Constraint();
73      }
74      
75      /* ------------------------------------------------------------ */
76      /**
77       * @param constraint
78       * @return
79       */
80      public static Constraint createConstraint(Constraint constraint)
81      {
82          try
83          {
84              return (Constraint)constraint.clone();
85          }
86          catch (CloneNotSupportedException e)
87          {
88              throw new IllegalStateException (e);
89          }
90      }
91      
92      /* ------------------------------------------------------------ */
93      /**
94       * Create a security constraint
95       * 
96       * @param name
97       * @param authenticate
98       * @param roles
99       * @param dataConstraint
100      * @return
101      */
102     public static Constraint createConstraint (String name, boolean authenticate, String[] roles, int dataConstraint)
103     {
104         Constraint constraint = createConstraint();
105         if (name != null)
106             constraint.setName(name);
107         constraint.setAuthenticate(authenticate);
108         constraint.setRoles(roles);
109         constraint.setDataConstraint(dataConstraint);
110         return constraint;
111     }
112     
113 
114     /* ------------------------------------------------------------ */
115     /**
116      * @param name
117      * @param element
118      * @return
119      */
120     public static Constraint createConstraint (String name, HttpConstraintElement element)
121     {
122         return createConstraint(name, element.getRolesAllowed(), element.getEmptyRoleSemantic(), element.getTransportGuarantee());     
123     }
124 
125 
126     /* ------------------------------------------------------------ */
127     /**
128      * @param name
129      * @param rolesAllowed
130      * @param permitOrDeny
131      * @param transport
132      * @return
133      */
134     public static Constraint createConstraint (String name, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
135     {
136         Constraint constraint = createConstraint();
137         
138         if (rolesAllowed == null || rolesAllowed.length==0)
139         {           
140             if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
141             {
142                 //Equivalent to <auth-constraint> with no roles
143                 constraint.setName(name+"-Deny");
144                 constraint.setAuthenticate(true);
145             }
146             else
147             {
148                 //Equivalent to no <auth-constraint>
149                 constraint.setName(name+"-Permit");
150                 constraint.setAuthenticate(false);
151             }
152         }
153         else
154         {
155             //Equivalent to <auth-constraint> with list of <security-role-name>s
156             constraint.setAuthenticate(true);
157             constraint.setRoles(rolesAllowed);
158             constraint.setName(name+"-RolesAllowed");           
159         } 
160 
161         //Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
162         constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
163         return constraint; 
164     }
165     
166     
167 
168     /* ------------------------------------------------------------ */
169     /**
170      * @param pathSpec
171      * @param constraintMappings
172      * @return
173      */
174     public static List<ConstraintMapping> getConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
175     {
176         if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
177             return Collections.emptyList();
178         
179         List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
180         for (ConstraintMapping mapping:constraintMappings)
181         {
182             if (pathSpec.equals(mapping.getPathSpec()))
183             {
184                mappings.add(mapping);
185             }
186         }
187         return mappings;
188     }
189     
190     
191     /* ------------------------------------------------------------ */
192     /** Take out of the constraint mappings those that match the 
193      * given path.
194      * 
195      * @param pathSpec
196      * @param constraintMappings a new list minus the matching constraints
197      * @return
198      */
199     public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
200     {
201         if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
202             return Collections.emptyList();
203         
204         List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
205         for (ConstraintMapping mapping:constraintMappings)
206         {
207             //Remove the matching mappings by only copying in non-matching mappings
208             if (!pathSpec.equals(mapping.getPathSpec()))
209             {
210                mappings.add(mapping);
211             }
212         }
213         return mappings;
214     }
215     
216     
217     
218     /* ------------------------------------------------------------ */
219     /** Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
220      * 
221      * @param name
222      * @param pathSpec
223      * @param securityElement
224      * @return
225      */
226     public static List<ConstraintMapping> createConstraintsWithMappingsForPath (String name, String pathSpec, ServletSecurityElement securityElement)
227     {
228         List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
229 
230         //Create a constraint that will describe the default case (ie if not overridden by specific HttpMethodConstraints)
231         Constraint constraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
232 
233         //Create a mapping for the pathSpec for the default case
234         ConstraintMapping defaultMapping = new ConstraintMapping();
235         defaultMapping.setPathSpec(pathSpec);
236         defaultMapping.setConstraint(constraint);  
237         mappings.add(defaultMapping);
238 
239 
240         //See Spec 13.4.1.2 p127
241         List<String> methodOmissions = new ArrayList<String>();
242         
243         //make constraint mappings for this url for each of the HttpMethodConstraintElements
244         Collection<HttpMethodConstraintElement> methodConstraints = securityElement.getHttpMethodConstraints();
245         if (methodConstraints != null)
246         {
247             for (HttpMethodConstraintElement methodConstraint:methodConstraints)
248             {
249                 //Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements supplied for the HttpMethodConstraintElement
250                 Constraint mconstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraint);
251                 ConstraintMapping mapping = new ConstraintMapping();
252                 mapping.setConstraint(mconstraint);
253                 mapping.setPathSpec(pathSpec);
254                 if (methodConstraint.getMethodName() != null)
255                 {
256                     mapping.setMethod(methodConstraint.getMethodName());
257                     //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
258                     methodOmissions.add(methodConstraint.getMethodName());
259                 }
260                 mappings.add(mapping);
261             }
262         }
263         //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
264         if (methodOmissions.size() > 0)
265             defaultMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
266      
267         return mappings;
268     }
269     
270     
271     
272     /* ------------------------------------------------------------ */
273     /** Get the strict mode.
274      * @return true if the security handler is running in strict mode.
275      */
276     public boolean isStrict()
277     {
278         return _strict;
279     }
280 
281     /* ------------------------------------------------------------ */
282     /** Set the strict mode of the security handler.
283      * <p>
284      * When in strict mode (the default), the full servlet specification
285      * will be implemented.
286      * If not in strict mode, some additional flexibility in configuration
287      * is allowed:<ul>
288      * <li>All users do not need to have a role defined in the deployment descriptor
289      * <li>The * role in a constraint applies to ANY role rather than all roles defined in
290      * the deployment descriptor.
291      * </ul>
292      *
293      * @param strict the strict to set
294      * @see #setRoles(Set)
295      * @see #setConstraintMappings(List, Set)
296      */
297     public void setStrict(boolean strict)
298     {
299         _strict = strict;
300     }
301 
302     /* ------------------------------------------------------------ */
303     /**
304      * @return Returns the constraintMappings.
305      */
306     @Override
307     public List<ConstraintMapping> getConstraintMappings()
308     {
309         return _constraintMappings;
310     }
311 
312     /* ------------------------------------------------------------ */
313     @Override
314     public Set<String> getRoles()
315     {
316         return _roles;
317     }
318 
319     /* ------------------------------------------------------------ */
320     /**
321      * Process the constraints following the combining rules in Servlet 3.0 EA
322      * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
323      *
324      * @param constraintMappings
325      *            The constraintMappings to set, from which the set of known roles
326      *            is determined.
327      */
328     public void setConstraintMappings(List<ConstraintMapping> constraintMappings)
329     {
330         setConstraintMappings(constraintMappings,null);
331     }
332 
333     /**
334      * Process the constraints following the combining rules in Servlet 3.0 EA
335      * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
336      *
337      * @param constraintMappings
338      *            The constraintMappings to set as array, from which the set of known roles
339      *            is determined.  Needed to retain API compatibility for 7.x
340      */
341     public void setConstraintMappings( ConstraintMapping[] constraintMappings )
342     {
343         setConstraintMappings( Arrays.asList(constraintMappings), null);
344     }
345 
346     /* ------------------------------------------------------------ */
347     /**
348      * Process the constraints following the combining rules in Servlet 3.0 EA
349      * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
350      *
351      * @param constraintMappings
352      *            The constraintMappings to set.
353      * @param roles The known roles (or null to determine them from the mappings)
354      */
355     @Override
356     public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
357     {
358         _constraintMappings.clear();
359         _constraintMappings.addAll(constraintMappings);
360 
361         if (roles==null)
362         {
363             roles = new HashSet<>();
364             for (ConstraintMapping cm : constraintMappings)
365             {
366                 String[] cmr = cm.getConstraint().getRoles();
367                 if (cmr!=null)
368                 {
369                     for (String r : cmr)
370                         if (!ALL_METHODS.equals(r))
371                             roles.add(r);
372                 }
373             }
374         }
375         setRoles(roles);
376         
377         if (isStarted())
378         {
379             for (ConstraintMapping mapping : _constraintMappings)
380             {
381                 processConstraintMapping(mapping);
382             }
383         }
384     }
385 
386     /* ------------------------------------------------------------ */
387     /**
388      * Set the known roles.
389      * This may be overridden by a subsequent call to {@link #setConstraintMappings(ConstraintMapping[])} or
390      * {@link #setConstraintMappings(List, Set)}.
391      * @see #setStrict(boolean)
392      * @param roles The known roles (or null to determine them from the mappings)
393      */
394     public void setRoles(Set<String> roles)
395     {
396         _roles.clear();
397         _roles.addAll(roles);
398     }
399 
400 
401 
402     /* ------------------------------------------------------------ */
403     /**
404      * @see org.eclipse.jetty.security.ConstraintAware#addConstraintMapping(org.eclipse.jetty.security.ConstraintMapping)
405      */
406     @Override
407     public void addConstraintMapping(ConstraintMapping mapping)
408     {
409         _constraintMappings.add(mapping);
410         if (mapping.getConstraint()!=null && mapping.getConstraint().getRoles()!=null)
411             for (String role :  mapping.getConstraint().getRoles())
412                 addRole(role);
413 
414         if (isStarted())
415         {
416             processConstraintMapping(mapping);
417         }
418     }
419 
420     /* ------------------------------------------------------------ */
421     /**
422      * @see org.eclipse.jetty.security.ConstraintAware#addRole(java.lang.String)
423      */
424     @Override
425     public void addRole(String role)
426     {
427         boolean modified = _roles.add(role);
428         if (isStarted() && modified && isStrict())
429         {
430             // Add the new role to currently defined any role role infos
431             for (Map<String,RoleInfo> map : _constraintMap.values())
432             {
433                 for (RoleInfo info : map.values())
434                 {
435                     if (info.isAnyRole())
436                         info.addRole(role);
437                 }
438             }
439         }
440     }
441 
442     /* ------------------------------------------------------------ */
443     /**
444      * @see org.eclipse.jetty.security.SecurityHandler#doStart()
445      */
446     @Override
447     protected void doStart() throws Exception
448     {
449         _constraintMap.clear();
450         if (_constraintMappings!=null)
451         {
452             for (ConstraintMapping mapping : _constraintMappings)
453             {
454                 processConstraintMapping(mapping);
455             }
456         }
457         super.doStart();
458     }
459     
460     
461     /* ------------------------------------------------------------ */
462     @Override
463     protected void doStop() throws Exception
464     {
465         super.doStop();
466         _constraintMap.clear();
467     }
468     
469     
470     /* ------------------------------------------------------------ */
471     /**
472      * Create and combine the constraint with the existing processed
473      * constraints.
474      * 
475      * @param mapping
476      */
477     protected void processConstraintMapping(ConstraintMapping mapping)
478     {
479         Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec());
480         if (mappings == null)
481         {
482             mappings = new HashMap<String,RoleInfo>();
483             _constraintMap.put(mapping.getPathSpec(),mappings);
484         }
485         RoleInfo allMethodsRoleInfo = mappings.get(ALL_METHODS);
486         if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
487             return;
488 
489         if (mapping.getMethodOmissions() != null && mapping.getMethodOmissions().length > 0)
490         {
491             processConstraintMappingWithMethodOmissions(mapping, mappings);
492             return;
493         }
494 
495         String httpMethod = mapping.getMethod();
496         if (httpMethod==null)
497             httpMethod=ALL_METHODS;
498         RoleInfo roleInfo = mappings.get(httpMethod);
499         if (roleInfo == null)
500         {
501             roleInfo = new RoleInfo();
502             mappings.put(httpMethod,roleInfo);
503             if (allMethodsRoleInfo != null)
504             {
505                 roleInfo.combine(allMethodsRoleInfo);
506             }
507         }
508         if (roleInfo.isForbidden())
509             return;
510 
511         //add in info from the constraint
512         configureRoleInfo(roleInfo, mapping);
513         
514         if (roleInfo.isForbidden())
515         {
516             if (httpMethod.equals(ALL_METHODS))
517             {
518                 mappings.clear();
519                 mappings.put(ALL_METHODS,roleInfo);
520             }
521         }
522         else
523         {
524             //combine with any entry that covers all methods
525             if (httpMethod == null)
526             {
527                 for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
528                 {
529                     if (entry.getKey() != null)
530                     {
531                         RoleInfo specific = entry.getValue();
532                         specific.combine(roleInfo);
533                     }
534                 }
535             }
536         }
537     }
538 
539     /* ------------------------------------------------------------ */
540     /** Constraints that name method omissions are dealt with differently.
541      * We create an entry in the mappings with key "method.omission". This entry
542      * is only ever combined with other omissions for the same method to produce a
543      * consolidated RoleInfo. Then, when we wish to find the relevant constraints for
544      *  a given Request (in prepareConstraintInfo()), we consult 3 types of entries in 
545      * the mappings: an entry that names the method of the Request specifically, an
546      * entry that names constraints that apply to all methods, entries of the form
547      * method.omission, where the method of the Request is not named in the omission.
548      * @param mapping
549      * @param mappings
550      */
551     protected void processConstraintMappingWithMethodOmissions (ConstraintMapping mapping, Map<String, RoleInfo> mappings)
552     {
553         String[] omissions = mapping.getMethodOmissions();
554         StringBuilder sb = new StringBuilder();
555         for (int i=0; i<omissions.length; i++)
556         {
557             if (i > 0)
558                 sb.append(".");
559             sb.append(omissions[i]);
560         }
561         sb.append(OMISSION_SUFFIX);
562 
563         RoleInfo ri = new RoleInfo();
564         mappings.put(sb.toString(), ri);
565         configureRoleInfo(ri, mapping);
566     }
567 
568     
569     /* ------------------------------------------------------------ */
570     /**
571      * Initialize or update the RoleInfo from the constraint
572      * @param ri
573      * @param mapping
574      */
575     protected void configureRoleInfo (RoleInfo ri, ConstraintMapping mapping)
576     {
577         Constraint constraint = mapping.getConstraint();
578         boolean forbidden = constraint.isForbidden();
579         ri.setForbidden(forbidden);
580         
581         //set up the data constraint (NOTE: must be done after setForbidden, as it nulls out the data constraint
582         //which we need in order to do combining of omissions in prepareConstraintInfo
583         UserDataConstraint userDataConstraint = UserDataConstraint.get(mapping.getConstraint().getDataConstraint());
584         ri.setUserDataConstraint(userDataConstraint);
585         
586 
587         //if forbidden, no point setting up roles
588         if (!ri.isForbidden())
589         {
590             //add in the roles
591             boolean checked = mapping.getConstraint().getAuthenticate();
592             ri.setChecked(checked);
593             if (ri.isChecked())
594             {
595                 if (mapping.getConstraint().isAnyRole())
596                  {
597                      if (_strict)
598                      {
599                          // * means "all defined roles"
600                          for (String role : _roles)
601                             ri.addRole(role);
602                      }
603                      else
604                          // * means any role
605                         ri.setAnyRole(true);
606                  }
607                  else
608                  {
609                     String[] newRoles = mapping.getConstraint().getRoles();
610                      for (String role : newRoles)
611                      {
612                          if (_strict &&!_roles.contains(role))
613                              throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
614                         ri.addRole(role);
615                      }
616                  }
617              }
618          }
619      }
620 
621    
622     /* ------------------------------------------------------------ */
623     /** 
624      * Find constraints that apply to the given path.
625      * In order to do this, we consult 3 different types of information stored in the mappings for each path - each mapping
626      * represents a merged set of user data constraints, roles etc -:
627      * <ol>
628      * <li>A mapping of an exact method name </li>
629      * <li>A mapping will null key that matches every method name</li>
630      * <li>Mappings with keys of the form "&lt;method&gt;.&lt;method&gt;.&lt;method&gt;.omission" that indicates it will match every method name EXCEPT those given</li>
631      * </ol>
632      * 
633      * @see org.eclipse.jetty.security.SecurityHandler#prepareConstraintInfo(java.lang.String, org.eclipse.jetty.server.Request)
634      */
635     @Override
636     protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
637     {
638         Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
639 
640         if (mappings != null)
641         {
642             String httpMethod = request.getMethod();
643             RoleInfo roleInfo = mappings.get(httpMethod);
644             if (roleInfo == null)
645             {
646                 //No specific http-method names matched
647                 List<RoleInfo> applicableConstraints = new ArrayList<RoleInfo>();
648 
649                 //Get info for constraint that matches all methods if it exists
650                 RoleInfo all = mappings.get(ALL_METHODS);
651                 if (all != null)
652                     applicableConstraints.add(all);
653           
654                 
655                 //Get info for constraints that name method omissions where target method name is not omitted
656                 //(ie matches because target method is not omitted, hence considered covered by the constraint)
657                 for (Entry<String, RoleInfo> entry: mappings.entrySet())
658                 {
659                     if (entry.getKey() != null && entry.getKey().endsWith(OMISSION_SUFFIX) && ! entry.getKey().contains(httpMethod))
660                         applicableConstraints.add(entry.getValue());
661                 }
662                 
663                 if (applicableConstraints.size() == 1)
664                     roleInfo = applicableConstraints.get(0);
665                 else
666                 {
667                     roleInfo = new RoleInfo();
668                     roleInfo.setUserDataConstraint(UserDataConstraint.None);
669                     
670                     for (RoleInfo r:applicableConstraints)
671                         roleInfo.combine(r);
672                 }
673 
674             }
675             return roleInfo;
676         }
677 
678         return null;
679     }
680 
681     @Override
682     protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, RoleInfo roleInfo) throws IOException
683     {
684         if (roleInfo == null)
685             return true;
686 
687         if (roleInfo.isForbidden())
688             return false;
689 
690         UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint();
691         if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
692             return true;
693 
694         HttpConfiguration httpConfig = HttpChannel.getCurrentHttpChannel().getHttpConfiguration();
695 
696 
697         if (dataConstraint == UserDataConstraint.Confidential || dataConstraint == UserDataConstraint.Integral)
698         {
699             if (request.isSecure())
700                 return true;
701 
702             if (httpConfig.getSecurePort() > 0)
703             {
704                 String scheme = httpConfig.getSecureScheme();
705                 int port = httpConfig.getSecurePort();
706                 String url = ("https".equalsIgnoreCase(scheme) && port==443)
707                     ? "https://"+request.getServerName()+request.getRequestURI()
708                     : scheme + "://" + request.getServerName() + ":" + port + request.getRequestURI();                    
709                 if (request.getQueryString() != null)
710                     url += "?" + request.getQueryString();
711                 response.setContentLength(0);
712                 response.sendRedirect(url);
713             }
714             else
715                 response.sendError(HttpStatus.FORBIDDEN_403,"!Secure");
716 
717             request.setHandled(true);
718             return false;
719         }
720         else
721         {
722             throw new IllegalArgumentException("Invalid dataConstraint value: " + dataConstraint);
723         }
724 
725     }
726 
727     @Override
728     protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
729     {
730         return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked();
731     }
732     
733     
734     /* ------------------------------------------------------------ */
735     /** 
736      * @see org.eclipse.jetty.security.SecurityHandler#checkWebResourcePermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object, org.eclipse.jetty.server.UserIdentity)
737      */
738     @Override
739     protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
740             throws IOException
741     {
742         if (constraintInfo == null)
743         {
744             return true;
745         }
746         RoleInfo roleInfo = (RoleInfo)constraintInfo;
747 
748         if (!roleInfo.isChecked())
749         {
750             return true;
751         }
752 
753         if (roleInfo.isAnyRole() && request.getAuthType()!=null)
754             return true;
755 
756         for (String role : roleInfo.getRoles())
757         {
758             if (userIdentity.isUserInRole(role, null))
759                 return true;
760         }
761         return false;
762     }
763 
764     /* ------------------------------------------------------------ */
765     @Override
766     public void dump(Appendable out,String indent) throws IOException
767     {
768         // TODO these should all be beans
769         dumpBeans(out,indent,
770                 Collections.singleton(getLoginService()),
771                 Collections.singleton(getIdentityService()),
772                 Collections.singleton(getAuthenticator()),
773                 Collections.singleton(_roles),
774                 _constraintMap.entrySet());
775     }
776 
777 }