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       * @see #setRoles(Set)
68       * @see #setConstraintMappings(ConstraintMapping[], Set)
69       */
70      public void setStrict(boolean strict)
71      {
72          _strict = strict;
73      }
74  
75      /* ------------------------------------------------------------ */
76      /**
77       * @return Returns the contraintMappings.
78       */
79      public ConstraintMapping[] getConstraintMappings()
80      {
81          return _constraintMappings;
82      }
83  
84      /* ------------------------------------------------------------ */
85      public Set<String> getRoles()
86      {
87          return _roles;
88      }
89  
90      /* ------------------------------------------------------------ */
91      /**
92       * Process the constraints following the combining rules in Servlet 3.0 EA
93       * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
94       * 
95       * @param constraintMappings
96       *            The contraintMappings to set, from which the set of known roles
97       *            is determined.
98       */
99      public void setConstraintMappings(ConstraintMapping[] constraintMappings)
100     {
101         setConstraintMappings(constraintMappings,null);
102     }
103         
104     /* ------------------------------------------------------------ */
105     /**
106      * Process the constraints following the combining rules in Servlet 3.0 EA
107      * spec section 13.7.1 Note that much of the logic is in the RoleInfo class.
108      * 
109      * @param constraintMappings
110      *            The contraintMappings to set.
111      * @param roles The known roles (or null to determine them from the mappings)
112      */
113     public void setConstraintMappings(ConstraintMapping[] constraintMappings, Set<String> roles)
114     {
115         if (isStarted())
116             throw new IllegalStateException("Started");
117         _constraintMappings = constraintMappings;
118         
119         if (roles==null)
120         {
121             roles = new HashSet<String>();
122             for (ConstraintMapping cm : constraintMappings)
123             {
124                 String[] cmr = cm.getConstraint().getRoles();
125                 if (cmr!=null)
126                 {
127                     for (String r : cmr)
128                         if (!"*".equals(r))
129                             roles.add(r);
130                 }
131             }
132         }
133         setRoles(roles);
134     }
135     
136     /* ------------------------------------------------------------ */
137     /**
138      * Set the known roles.
139      * This may be overridden by a subsequent call to {@link #setConstraintMappings(ConstraintMapping[])} or
140      * {@link #setConstraintMappings(ConstraintMapping[], Set)}.
141      * @see #setStrict(boolean)
142      * @param roles The known roles (or null to determine them from the mappings)
143      */
144     public void setRoles(Set<String> roles)
145     {
146         if (isStarted())
147             throw new IllegalStateException("Started");
148         
149         this._roles = roles;
150     }
151 
152     /* ------------------------------------------------------------ */
153     /**
154      * @see org.eclipse.jetty.security.SecurityHandler#doStart()
155      */
156     @Override
157     protected void doStart() throws Exception
158     {
159         _constraintMap.clear();
160         if (_constraintMappings!=null)
161         {
162             for (ConstraintMapping mapping : _constraintMappings)
163             {
164                 Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec());
165                 if (mappings == null)
166                 {
167                     mappings = new StringMap();
168                     _constraintMap.put(mapping.getPathSpec(),mappings);
169                 }
170                 RoleInfo allMethodsRoleInfo = mappings.get(null);
171                 if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
172                 {
173                     continue;
174                 }
175                 String httpMethod = mapping.getMethod();
176                 RoleInfo roleInfo = mappings.get(httpMethod);
177                 if (roleInfo == null)
178                 {
179                     roleInfo = new RoleInfo();
180                     mappings.put(httpMethod,roleInfo);
181                     if (allMethodsRoleInfo != null)
182                     {
183                         roleInfo.combine(allMethodsRoleInfo);
184                     }
185                 }
186                 if (roleInfo.isForbidden())
187                 {
188                     continue;
189                 }
190                 Constraint constraint = mapping.getConstraint();
191                 boolean forbidden = constraint.isForbidden();
192                 roleInfo.setForbidden(forbidden);
193                 if (forbidden)
194                 {
195                     if (httpMethod == null)
196                     {
197                         mappings.clear();
198                         mappings.put(null,roleInfo);
199                     }
200                 }
201                 else
202                 {
203                     UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
204                     roleInfo.setUserDataConstraint(userDataConstraint);
205 
206                     boolean checked = constraint.getAuthenticate();
207                     roleInfo.setChecked(checked);
208                     if (roleInfo.isChecked())
209                     {
210                         if (constraint.isAnyRole())
211                         {
212                             if (_strict)
213                             {
214                                 // * means "all defined roles"
215                                 for (String role : _roles)
216                                     roleInfo.addRole(role);
217                             }
218                             else
219                                 // * means any role
220                                 roleInfo.setAnyRole(true);
221                         }
222                         else
223                         {
224                             String[] newRoles = constraint.getRoles();
225                             for (String role : newRoles)
226                             {
227                                 if (_strict &&!_roles.contains(role))
228                                     throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
229                                 roleInfo.addRole(role);
230                             }
231                         }
232                     }
233                     if (httpMethod == null)
234                     {
235                         for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
236                         {
237                             if (entry.getKey() != null)
238                             {
239                                 RoleInfo specific = entry.getValue();
240                                 specific.combine(roleInfo);
241                             }
242                         }
243                     }
244                 }
245             }
246         }
247         super.doStart();
248     }
249 
250     protected Object prepareConstraintInfo(String pathInContext, Request request)
251     {
252         Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
253 
254         if (mappings != null)
255         {
256             String httpMethod = request.getMethod();
257             RoleInfo roleInfo = mappings.get(httpMethod);
258             if (roleInfo == null)
259                 roleInfo = mappings.get(null);
260             return roleInfo;
261         }
262        
263         return null;
264     }
265 
266     protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException
267     {
268         if (constraintInfo == null)
269             return true;
270         
271         RoleInfo roleInfo = (RoleInfo)constraintInfo;
272         if (roleInfo.isForbidden())
273             return false;
274         
275         
276         UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint();
277         if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
278         {
279             return true;
280         }
281         HttpConnection connection = HttpConnection.getCurrentConnection();
282         Connector connector = connection.getConnector();
283 
284         if (dataConstraint == UserDataConstraint.Integral)
285         {
286             if (connector.isIntegral(request))
287                 return true;
288             if (connector.getConfidentialPort() > 0)
289             {
290                 String url = connector.getIntegralScheme() + "://" + request.getServerName() + ":" + connector.getIntegralPort() + request.getRequestURI();
291                 if (request.getQueryString() != null)
292                     url += "?" + request.getQueryString();
293                 response.setContentLength(0);
294                 response.sendRedirect(url);
295             }
296             else
297                 response.sendError(Response.SC_FORBIDDEN,"!Integral");
298 
299             request.setHandled(true);
300             return false;
301         }
302         else if (dataConstraint == UserDataConstraint.Confidential)
303         {
304             if (connector.isConfidential(request))
305                 return true;
306 
307             if (connector.getConfidentialPort() > 0)
308             {
309                 String url = connector.getConfidentialScheme() + "://" + request.getServerName() + ":" + connector.getConfidentialPort()
310                         + request.getRequestURI();
311                 if (request.getQueryString() != null)
312                     url += "?" + request.getQueryString();
313 
314                 response.setContentLength(0);
315                 response.sendRedirect(url);
316             }
317             else
318                 response.sendError(Response.SC_FORBIDDEN,"!Confidential");
319             
320             request.setHandled(true);
321             return false;
322         }
323         else
324         {
325             throw new IllegalArgumentException("Invalid dataConstraint value: " + dataConstraint);
326         }
327 
328     }
329 
330     protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
331     {
332         if (constraintInfo == null)
333         {
334             return false;
335         }
336         return ((RoleInfo)constraintInfo).isChecked();
337     }
338 
339     protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
340             throws IOException
341     {
342         if (constraintInfo == null)
343         {
344             return true;
345         }
346         RoleInfo roleInfo = (RoleInfo)constraintInfo;
347 
348         if (!roleInfo.isChecked())
349         {
350             return true;
351         }
352         
353         if (roleInfo.isAnyRole() && request.getAuthType()!=null)
354             return true;
355         
356         String[] roles = roleInfo.getRoles();
357         for (String role : roles)
358         {
359             if (userIdentity.isUserInRole(role, null))
360                 return true;
361         }
362         return false;
363     }
364     
365     /* ------------------------------------------------------------ */
366     protected void dump(StringBuilder b,String indent)
367     {
368         super.dump(b,indent);
369         b.append(indent).append(" +=roles=").append(_roles).append('\n');
370         
371         for (Object path : _constraintMap.keySet())
372         {
373             Object constraint = _constraintMap.get(path);
374             b.append(indent).append(" +=").append(path).append('=').append(constraint).append('\n');
375         }
376     }
377 }