View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.security;
15  
16  import java.io.IOException;
17  import java.util.HashSet;
18  import java.util.Map;
19  import java.util.Set;
20  
21  import org.eclipse.jetty.http.PathMap;
22  import org.eclipse.jetty.http.security.Constraint;
23  import org.eclipse.jetty.server.Connector;
24  import org.eclipse.jetty.server.HttpConnection;
25  import org.eclipse.jetty.server.Request;
26  import org.eclipse.jetty.server.Response;
27  import org.eclipse.jetty.server.UserIdentity;
28  import org.eclipse.jetty.util.StringMap;
29  
30  /* ------------------------------------------------------------ */
31  /**
32   * Handler to enforce SecurityConstraints. This implementation is servlet spec
33   * 2.4 compliant and precomputes the constraint combinations for runtime
34   * efficiency.
35   * 
36   */
37  public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
38  {
39      private ConstraintMapping[] _constraintMappings;
40      private Set<String> _roles;
41      private PathMap _constraintMap = new PathMap();
42      private boolean _strict = true;
43  
44      
45      /* ------------------------------------------------------------ */
46      /** Get the strict mode.
47       * @return true if the security handler is running in strict mode.
48       */
49      public boolean isStrict()
50      {
51          return _strict;
52      }
53  
54      /* ------------------------------------------------------------ */
55      /** Set the strict mode of the security handler.
56       * <p>
57       * When in strict mode (the default), the full servlet specification
58       * will be implemented.
59       * If not in strict mode, some additional flexibility in configuration
60       * is allowed:<ul>
61       * <li>All users do not need to have a role defined in the deployment descriptor
62       * <li>The * role in a constraint applies to ANY role rather than all roles defined in
63       * the deployment descriptor.
64       * </ul>
65       * 
66       * @param strict the strict to set
67       */
68      public void setStrict(boolean strict)
69      {
70          _strict = strict;
71      }
72  
73      /* ------------------------------------------------------------ */
74      /**
75       * @return Returns the contraintMappings.
76       */
77      public ConstraintMapping[] getConstraintMappings()
78      {
79          return _constraintMappings;
80      }
81  
82      /* ------------------------------------------------------------ */
83      public Set<String> getRoles()
84      {
85          return _roles;
86      }
87  
88      /* ------------------------------------------------------------ */
89      /**
90       * Process the constraints following the combining rules in Servlet 3.0 EA
91       * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
92       * 
93       * @param constraintMappings
94       *            The contraintMappings to set, from which the set of known roles
95       *            is determined.
96       */
97      public void setConstraintMappings(ConstraintMapping[] constraintMappings)
98      {
99          setConstraintMappings(constraintMappings,null);
100     }
101         
102     /* ------------------------------------------------------------ */
103     /**
104      * Process the constraints following the combining rules in Servlet 3.0 EA
105      * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
106      * 
107      * @param constraintMappings
108      *            The contraintMappings to set.
109      * @param roles The known roles (or null to determine them from the mappings)
110      */
111     public void setConstraintMappings(ConstraintMapping[] constraintMappings, Set<String> roles)
112     {
113         if (isStarted())
114             throw new IllegalStateException("Started");
115         _constraintMappings = constraintMappings;
116         
117         if (roles==null)
118         {
119             roles = new HashSet<String>();
120             for (ConstraintMapping cm : constraintMappings)
121             {
122                 String[] cmr = cm.getConstraint().getRoles();
123                 if (cmr!=null)
124                 {
125                     for (String r : cmr)
126                         if (!"*".equals(r))
127                             roles.add(r);
128                 }
129             }
130         }
131         
132         this._roles = roles;
133 
134     }
135 
136     /* ------------------------------------------------------------ */
137     /**
138      * @see org.eclipse.jetty.security.SecurityHandler#doStart()
139      */
140     @Override
141     protected void doStart() throws Exception
142     {
143         _constraintMap.clear();
144         if (_constraintMappings!=null)
145         {
146             for (ConstraintMapping mapping : _constraintMappings)
147             {
148                 Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec());
149                 if (mappings == null)
150                 {
151                     mappings = new StringMap();
152                     _constraintMap.put(mapping.getPathSpec(),mappings);
153                 }
154                 RoleInfo allMethodsRoleInfo = mappings.get(null);
155                 if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
156                 {
157                     continue;
158                 }
159                 String httpMethod = mapping.getMethod();
160                 RoleInfo roleInfo = mappings.get(httpMethod);
161                 if (roleInfo == null)
162                 {
163                     roleInfo = new RoleInfo();
164                     mappings.put(httpMethod,roleInfo);
165                     if (allMethodsRoleInfo != null)
166                     {
167                         roleInfo.combine(allMethodsRoleInfo);
168                     }
169                 }
170                 if (roleInfo.isForbidden())
171                 {
172                     continue;
173                 }
174                 Constraint constraint = mapping.getConstraint();
175                 boolean forbidden = constraint.isForbidden();
176                 roleInfo.setForbidden(forbidden);
177                 if (forbidden)
178                 {
179                     if (httpMethod == null)
180                     {
181                         mappings.clear();
182                         mappings.put(null,roleInfo);
183                     }
184                 }
185                 else
186                 {
187                     UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
188                     roleInfo.setUserDataConstraint(userDataConstraint);
189 
190                     boolean checked = constraint.getAuthenticate();
191                     roleInfo.setChecked(checked);
192                     if (roleInfo.isChecked())
193                     {
194                         if (constraint.isAnyRole())
195                         {
196                             if (_strict)
197                             {
198                                 // * means "all defined roles"
199                                 for (String role : _roles)
200                                     roleInfo.addRole(role);
201                             }
202                             else
203                                 // * means any role
204                                 roleInfo.setAnyRole(true);
205                         }
206                         else
207                         {
208                             String[] newRoles = constraint.getRoles();
209                             for (String role : newRoles)
210                             {
211                                 if (_strict &&!_roles.contains(role))
212                                     throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
213                                 roleInfo.addRole(role);
214                             }
215                         }
216                     }
217                     if (httpMethod == null)
218                     {
219                         for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
220                         {
221                             if (entry.getKey() != null)
222                             {
223                                 RoleInfo specific = entry.getValue();
224                                 specific.combine(roleInfo);
225                             }
226                         }
227                     }
228                 }
229             }
230         }
231         super.doStart();
232     }
233 
234     protected Object prepareConstraintInfo(String pathInContext, Request request)
235     {
236         Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
237 
238         if (mappings != null)
239         {
240             String httpMethod = request.getMethod();
241             RoleInfo roleInfo = mappings.get(httpMethod);
242             if (roleInfo == null)
243                 roleInfo = mappings.get(null);
244             return roleInfo;
245         }
246        
247         return null;
248     }
249 
250     protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException
251     {
252         if (constraintInfo == null)
253         {
254             return true;
255         }
256         RoleInfo roleInfo = (RoleInfo)constraintInfo;
257         if (roleInfo.isForbidden())
258         {
259             return false;
260         }
261         UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint();
262         if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
263         {
264             return true;
265         }
266         HttpConnection connection = HttpConnection.getCurrentConnection();
267         Connector connector = connection.getConnector();
268 
269         if (dataConstraint == UserDataConstraint.Integral)
270         {
271             if (connector.isIntegral(request))
272                 return true;
273             if (connector.getConfidentialPort() > 0)
274             {
275                 String url = connector.getIntegralScheme() + "://" + request.getServerName() + ":" + connector.getIntegralPort() + request.getRequestURI();
276                 if (request.getQueryString() != null)
277                     url += "?" + request.getQueryString();
278                 response.setContentLength(0);
279                 response.sendRedirect(url);
280                 request.setHandled(true);
281             }
282             return false;
283         }
284         else if (dataConstraint == UserDataConstraint.Confidential)
285         {
286             if (connector.isConfidential(request))
287                 return true;
288 
289             if (connector.getConfidentialPort() > 0)
290             {
291                 String url = connector.getConfidentialScheme() + "://" + request.getServerName() + ":" + connector.getConfidentialPort()
292                         + request.getRequestURI();
293                 if (request.getQueryString() != null)
294                     url += "?" + request.getQueryString();
295 
296                 response.setContentLength(0);
297                 response.sendRedirect(url);
298                 request.setHandled(true);
299             }
300             return false;
301         }
302         else
303         {
304             throw new IllegalArgumentException("Invalid dataConstraint value: " + dataConstraint);
305         }
306 
307     }
308 
309     protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
310     {
311         if (constraintInfo == null)
312         {
313             return false;
314         }
315         return ((RoleInfo)constraintInfo).isChecked();
316     }
317 
318     protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
319             throws IOException
320     {
321         if (constraintInfo == null)
322         {
323             return true;
324         }
325         RoleInfo roleInfo = (RoleInfo)constraintInfo;
326 
327         if (!roleInfo.isChecked())
328         {
329             return true;
330         }
331         
332         if (roleInfo.isAnyRole() && request.getAuthType()!=null)
333             return true;
334         
335         String[] roles = roleInfo.getRoles();
336         for (String role : roles)
337         {
338             if (userIdentity.isUserInRole(role, null))
339                 return true;
340         }
341         return false;
342     }
343 }