View Javadoc

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