1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.taglibs.authz;
17
18 import org.acegisecurity.Authentication;
19 import org.acegisecurity.acl.AclEntry;
20 import org.acegisecurity.acl.AclManager;
21 import org.acegisecurity.acl.basic.BasicAclEntry;
22 import org.acegisecurity.context.SecurityContextHolder;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import org.springframework.context.ApplicationContext;
28
29 import org.springframework.web.context.support.WebApplicationContextUtils;
30 import org.springframework.web.util.ExpressionEvaluationUtils;
31
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.StringTokenizer;
36
37 import javax.servlet.ServletContext;
38 import javax.servlet.jsp.JspException;
39 import javax.servlet.jsp.PageContext;
40 import javax.servlet.jsp.tagext.Tag;
41 import javax.servlet.jsp.tagext.TagSupport;
42
43
44 /***
45 * An implementation of {@link javax.servlet.jsp.tagext.Tag} that allows its
46 * body through if some authorizations are granted to the request's principal.
47 *
48 * <P>
49 * Only works with permissions that are subclasses of {@link
50 * org.acegisecurity.acl.basic.BasicAclEntry}.
51 * </p>
52 *
53 * <p>
54 * One or more comma separate integer permissions are specified via the
55 * <code>hasPermission</code> attribute. The tag will include its body if
56 * <b>any</b> of the integer permissions have been granted to the current
57 * <code>Authentication</code> (obtained from the <code>SecurityContextHolder</code>).
58 * </p>
59 *
60 * <p>
61 * For this class to operate it must be able to access the application context
62 * via the <code>WebApplicationContextUtils</code> and locate an {@link
63 * AclManager}. Application contexts have no need to have more than one
64 * <code>AclManager</code> (as a provider-based implementation can be used so
65 * that it locates a provider that is authoritative for the given domain
66 * object instance), so the first <code>AclManager</code> located will be
67 * used.
68 * </p>
69 *
70 * @author Ben Alex
71 * @version $Id: AclTag.java,v 1.8 2005/11/17 00:56:29 benalex Exp $
72 */
73 public class AclTag extends TagSupport {
74
75
76 protected static final Log logger = LogFactory.getLog(AclTag.class);
77
78
79
80 private Object domainObject;
81 private String hasPermission = "";
82
83
84
85 public void setDomainObject(Object domainObject) {
86 this.domainObject = domainObject;
87 }
88
89 public Object getDomainObject() {
90 return domainObject;
91 }
92
93 public void setHasPermission(String hasPermission) {
94 this.hasPermission = hasPermission;
95 }
96
97 public String getHasPermission() {
98 return hasPermission;
99 }
100
101 public int doStartTag() throws JspException {
102 if ((null == hasPermission) || "".equals(hasPermission)) {
103 return Tag.SKIP_BODY;
104 }
105
106 final String evaledPermissionsString = ExpressionEvaluationUtils
107 .evaluateString("hasPermission", hasPermission, pageContext);
108
109 Integer[] requiredIntegers = null;
110
111 try {
112 requiredIntegers = parseIntegersString(evaledPermissionsString);
113 } catch (NumberFormatException nfe) {
114 throw new JspException(nfe);
115 }
116
117 Object resolvedDomainObject = null;
118
119 if (domainObject instanceof String) {
120 resolvedDomainObject = ExpressionEvaluationUtils.evaluate("domainObject",
121 (String) domainObject, Object.class, pageContext);
122 } else {
123 resolvedDomainObject = domainObject;
124 }
125
126 if (resolvedDomainObject == null) {
127 if (logger.isDebugEnabled()) {
128 logger.debug(
129 "domainObject resolved to null, so including tag body");
130 }
131
132
133 return Tag.EVAL_BODY_INCLUDE;
134 }
135
136 if (SecurityContextHolder.getContext().getAuthentication() == null) {
137 if (logger.isDebugEnabled()) {
138 logger.debug(
139 "SecurityContextHolder did not return a non-null Authentication object, so skipping tag body");
140 }
141
142 return Tag.SKIP_BODY;
143 }
144
145 Authentication auth = SecurityContextHolder.getContext()
146 .getAuthentication();
147
148 ApplicationContext context = getContext(pageContext);
149 Map beans = context.getBeansOfType(AclManager.class, false, false);
150
151 if (beans.size() == 0) {
152 throw new JspException(
153 "No AclManager would found the application context: "
154 + context.toString());
155 }
156
157 String beanName = (String) beans.keySet().iterator().next();
158 AclManager aclManager = (AclManager) context.getBean(beanName);
159
160
161 AclEntry[] acls = aclManager.getAcls(resolvedDomainObject, auth);
162
163 if (logger.isDebugEnabled()) {
164 logger.debug("Authentication: '" + auth + "' has: "
165 + ((acls == null) ? 0 : acls.length)
166 + " AclEntrys for domain object: '" + resolvedDomainObject
167 + "' from AclManager: '" + aclManager.toString() + "'");
168 }
169
170 if ((acls == null) || (acls.length == 0)) {
171 return Tag.SKIP_BODY;
172 }
173
174 for (int i = 0; i < acls.length; i++) {
175
176 if (acls[i] instanceof BasicAclEntry) {
177 BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
178
179
180 for (int y = 0; y < requiredIntegers.length; y++) {
181 if (processableAcl.isPermitted(
182 requiredIntegers[y].intValue())) {
183 if (logger.isDebugEnabled()) {
184 logger.debug(
185 "Including tag body as found permission: "
186 + requiredIntegers[y] + " due to AclEntry: '"
187 + processableAcl + "'");
188 }
189
190 return Tag.EVAL_BODY_INCLUDE;
191 }
192 }
193 }
194 }
195
196 if (logger.isDebugEnabled()) {
197 logger.debug("No permission, so skipping tag body");
198 }
199
200 return Tag.SKIP_BODY;
201 }
202
203 /***
204 * Allows test cases to override where application context obtained from.
205 *
206 * @param pageContext so the <code>ServletContext</code> can be accessed as
207 * required by Spring's <code>WebApplicationContextUtils</code>
208 *
209 * @return the Spring application context (never <code>null</code>)
210 */
211 protected ApplicationContext getContext(PageContext pageContext) {
212 ServletContext servletContext = pageContext.getServletContext();
213
214 return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
215 }
216
217 private Integer[] parseIntegersString(String integersString)
218 throws NumberFormatException {
219 final Set integers = new HashSet();
220 final StringTokenizer tokenizer;
221 tokenizer = new StringTokenizer(integersString, ",", false);
222
223 while (tokenizer.hasMoreTokens()) {
224 String integer = tokenizer.nextToken();
225 integers.add(new Integer(integer));
226 }
227
228 return (Integer[]) integers.toArray(new Integer[] {});
229 }
230 }