View Javadoc

1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.acegisecurity.taglibs.authz;
16  
17  import org.acegisecurity.acls.Acl;
18  import org.acegisecurity.acls.AclService;
19  import org.acegisecurity.acls.NotFoundException;
20  import org.acegisecurity.acls.Permission;
21  import org.acegisecurity.acls.domain.BasePermission;
22  import org.acegisecurity.acls.objectidentity.ObjectIdentity;
23  import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy;
24  import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
25  import org.acegisecurity.acls.sid.Sid;
26  import org.acegisecurity.acls.sid.SidRetrievalStrategy;
27  import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl;
28  
29  import org.acegisecurity.context.SecurityContextHolder;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import org.springframework.context.ApplicationContext;
35  
36  import org.springframework.web.context.support.WebApplicationContextUtils;
37  import org.springframework.web.util.ExpressionEvaluationUtils;
38  
39  import java.util.HashSet;
40  import java.util.Map;
41  import java.util.Set;
42  import java.util.StringTokenizer;
43  
44  import javax.servlet.ServletContext;
45  import javax.servlet.jsp.JspException;
46  import javax.servlet.jsp.PageContext;
47  import javax.servlet.jsp.tagext.Tag;
48  import javax.servlet.jsp.tagext.TagSupport;
49  
50  
51  /**
52   * An implementation of {@link javax.servlet.jsp.tagext.Tag} that allows its body through if some authorizations
53   * are granted to the request's principal.<p>One or more comma separate numeric are specified via the
54   * <code>hasPermission</code> attribute. Those permissions are then converted into {@link Permission} instances. These
55   * instances are then presented as an array to the {@link Acl#isGranted(Permission[],
56   * org.acegisecurity.acls.sid.Sid[], boolean)} method. The {@link Sid} presented is determined by the {@link
57   * SidRetrievalStrategy}.</p>
58   *  <p>For this class to operate it must be able to access the application context via the
59   * <code>WebApplicationContextUtils</code> and locate an {@link AclService} and {@link SidRetrievalStrategy}.
60   * Application contexts must provide one and only one of these Java types.</p>
61   *
62   * @author Ben Alex
63   * @version $Id: AccessControlListTag.java 1784 2007-02-24 21:00:24Z luke_t $
64   */
65  public class AccessControlListTag extends TagSupport {
66      //~ Static fields/initializers =====================================================================================
67  
68      protected static final Log logger = LogFactory.getLog(AccessControlListTag.class);
69  
70      //~ Instance fields ================================================================================================
71  
72      private AclService aclService;
73      private ApplicationContext applicationContext;
74      private Object domainObject;
75      private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy;
76      private SidRetrievalStrategy sidRetrievalStrategy;
77      private String hasPermission = "";
78  
79      //~ Methods ========================================================================================================
80  
81      public int doStartTag() throws JspException {
82          initializeIfRequired();
83  
84          if ((null == hasPermission) || "".equals(hasPermission)) {
85              return Tag.SKIP_BODY;
86          }
87  
88          final String evaledPermissionsString = ExpressionEvaluationUtils.evaluateString("hasPermission", hasPermission,
89                  pageContext);
90  
91          Permission[] requiredPermissions = null;
92  
93          try {
94              requiredPermissions = parsePermissionsString(evaledPermissionsString);
95          } catch (NumberFormatException nfe) {
96              throw new JspException(nfe);
97          }
98  
99          Object resolvedDomainObject = null;
100 
101         if (domainObject instanceof String) {
102             resolvedDomainObject = ExpressionEvaluationUtils.evaluate("domainObject", (String) domainObject,
103                     Object.class, pageContext);
104         } else {
105             resolvedDomainObject = domainObject;
106         }
107 
108         if (resolvedDomainObject == null) {
109             if (logger.isDebugEnabled()) {
110                 logger.debug("domainObject resolved to null, so including tag body");
111             }
112 
113             // Of course they have access to a null object!
114             return Tag.EVAL_BODY_INCLUDE;
115         }
116 
117         if (SecurityContextHolder.getContext().getAuthentication() == null) {
118             if (logger.isDebugEnabled()) {
119                 logger.debug(
120                     "SecurityContextHolder did not return a non-null Authentication object, so skipping tag body");
121             }
122 
123             return Tag.SKIP_BODY;
124         }
125 
126         Sid[] sids = sidRetrievalStrategy.getSids(SecurityContextHolder.getContext().getAuthentication());
127         ObjectIdentity oid = objectIdentityRetrievalStrategy.getObjectIdentity(resolvedDomainObject);
128 
129         // Obtain aclEntrys applying to the current Authentication object
130         try {
131             Acl acl = aclService.readAclById(oid, sids);
132 
133             if (acl.isGranted(requiredPermissions, sids, false)) {
134                 return Tag.EVAL_BODY_INCLUDE;
135             } else {
136                 return Tag.SKIP_BODY;
137             }
138         } catch (NotFoundException nfe) {
139             return Tag.SKIP_BODY;
140         }
141     }
142 
143     /**
144      * Allows test cases to override where application context obtained from.
145      *
146      * @param pageContext so the <code>ServletContext</code> can be accessed as required by Spring's
147      *        <code>WebApplicationContextUtils</code>
148      *
149      * @return the Spring application context (never <code>null</code>)
150      */
151     protected ApplicationContext getContext(PageContext pageContext) {
152         ServletContext servletContext = pageContext.getServletContext();
153 
154         return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
155     }
156 
157     public Object getDomainObject() {
158         return domainObject;
159     }
160 
161     public String getHasPermission() {
162         return hasPermission;
163     }
164 
165     private void initializeIfRequired() throws JspException {
166         if (applicationContext == null) {
167             this.applicationContext = getContext(pageContext);
168 
169             Map map = applicationContext.getBeansOfType(AclService.class);
170 
171             if (map.size() != 1) {
172                 throw new JspException(
173                     "Found incorrect number of AclService instances in application context - you must have only have one!");
174             }
175 
176             aclService = (AclService) map.values().iterator().next();
177 
178             map = applicationContext.getBeansOfType(SidRetrievalStrategy.class);
179 
180             if (map.size() == 0) {
181                 sidRetrievalStrategy = new SidRetrievalStrategyImpl();
182             } else if (map.size() == 1) {
183                 sidRetrievalStrategy = (SidRetrievalStrategy) map.values().iterator().next();
184             } else {
185                 throw new JspException("Found incorrect number of SidRetrievalStrategy instances in application "
186                         + "context - you must have only have one!");
187             }
188 
189             map = applicationContext.getBeansOfType(ObjectIdentityRetrievalStrategy.class);
190 
191             if (map.size() == 0) {
192                 objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
193             } else if (map.size() == 1) {
194                 objectIdentityRetrievalStrategy = (ObjectIdentityRetrievalStrategy) map.values().iterator().next();
195             } else {
196                 throw new JspException("Found incorrect number of ObjectIdentityRetrievalStrategy instances in "
197                         + "application context - you must have only have one!");
198             }
199         }
200     }
201 
202     private Permission[] parsePermissionsString(String integersString)
203         throws NumberFormatException {
204         final Set permissions = new HashSet();
205         final StringTokenizer tokenizer;
206         tokenizer = new StringTokenizer(integersString, ",", false);
207 
208         while (tokenizer.hasMoreTokens()) {
209             String integer = tokenizer.nextToken();
210             permissions.add(BasePermission.buildFromMask(new Integer(integer).intValue()));
211         }
212 
213         return (Permission[]) permissions.toArray(new Permission[] {});
214     }
215 
216     public void setDomainObject(Object domainObject) {
217         this.domainObject = domainObject;
218     }
219 
220     public void setHasPermission(String hasPermission) {
221         this.hasPermission = hasPermission;
222     }
223 }