Forráskód Böngészése

Add an Authentication and Acl taglib.

Ben Alex 21 éve
szülő
commit
68dc38841f

+ 249 - 0
core/src/main/java/org/acegisecurity/taglibs/authz/AclTag.java

@@ -0,0 +1,249 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.taglibs.authz;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.acl.AclEntry;
+import net.sf.acegisecurity.acl.AclManager;
+import net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.context.ApplicationContext;
+
+import org.springframework.web.context.support.WebApplicationContextUtils;
+import org.springframework.web.util.ExpressionEvaluationUtils;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+import javax.servlet.jsp.tagext.TagSupport;
+
+
+/**
+ * An implementation of {@link javax.servlet.jsp.tagext.Tag} that allows its
+ * body through if some authorizations are granted to the request's principal.
+ * 
+ * <P>
+ * Only works with permissions that are subclasses of {@link
+ * net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry}.
+ * </p>
+ * 
+ * <p>
+ * One or more comma separate integer permissions are specified via the
+ * <code>hasPermission</code> attribute. The tag will include its body if
+ * <b>any</b> of the integer permissions have been granted to the current
+ * <code>Authentication</code> (obtained from the <code>ContextHolder</code>).
+ * </p>
+ * 
+ * <p>
+ * For this class to operate it must be able to access the application context
+ * via the <code>WebApplicationContextUtils</code> and locate an {@link
+ * AclManager}. Application contexts have no need to have more than one
+ * <code>AclManager</code> (as a provider-based implementation can be used so
+ * that it locates a provider that is authoritative for the given domain
+ * object instance), so the first <code>AclManager</code> located will be
+ * used.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AclTag extends TagSupport {
+    //~ Static fields/initializers =============================================
+
+    protected static final Log logger = LogFactory.getLog(AclTag.class);
+
+    //~ Instance fields ========================================================
+
+    private Object domainObject;
+    private String hasPermission = "";
+
+    //~ Methods ================================================================
+
+    public void setDomainObject(Object domainObject) {
+        this.domainObject = domainObject;
+    }
+
+    public Object getDomainObject() {
+        return domainObject;
+    }
+
+    public void setHasPermission(String hasPermission) {
+        this.hasPermission = hasPermission;
+    }
+
+    public String getHasPermission() {
+        return hasPermission;
+    }
+
+    public int doStartTag() throws JspException {
+        if ((null == hasPermission) || "".equals(hasPermission)) {
+            return Tag.SKIP_BODY;
+        }
+
+        final String evaledPermissionsString = ExpressionEvaluationUtils
+            .evaluateString("hasPermission", hasPermission, pageContext);
+
+        if ((null != evaledPermissionsString)
+            && !"".equals(evaledPermissionsString)) {
+            Integer[] requiredIntegers = null;
+
+            try {
+                requiredIntegers = parseIntegersString(evaledPermissionsString);
+            } catch (NumberFormatException nfe) {
+                throw new JspException(nfe);
+            }
+
+            if (requiredIntegers.length == 0) {
+                throw new JspException(
+                    "A comma separate list of integers representing authorised permissions was NOT provided via the 'hasPermission' attribute");
+            }
+
+            Object resolvedDomainObject = null;
+
+            if (domainObject instanceof String) {
+                resolvedDomainObject = ExpressionEvaluationUtils.evaluate("domainObject",
+                        (String) domainObject, Object.class, pageContext);
+            } else {
+                resolvedDomainObject = domainObject;
+            }
+
+            if (resolvedDomainObject == null) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug(
+                        "domainObject resolved to null, so including tag body");
+                }
+
+                // Of course they have access to a null object!
+                return Tag.EVAL_BODY_INCLUDE;
+            }
+
+            if ((ContextHolder.getContext() == null)
+                || !(ContextHolder.getContext() instanceof SecureContext)
+                || (((SecureContext) ContextHolder.getContext())
+                .getAuthentication() == null)) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug(
+                        "ContextHolder did not return a non-null Authentication object, so skipping tag body");
+                }
+
+                return Tag.SKIP_BODY;
+            }
+
+            Authentication auth = ((SecureContext) ContextHolder.getContext())
+                .getAuthentication();
+
+            ApplicationContext context = getContext(pageContext
+                    .getServletContext());
+
+            if (context == null) {
+                throw new JspException(
+                    "applicationContext unavailable from servlet context");
+            }
+
+            Map beans = context.getBeansOfType(AclManager.class, false, false);
+
+            if (beans.size() == 0) {
+                throw new JspException(
+                    "No AclManager would found the application context: "
+                    + context.toString());
+            }
+
+            String beanName = (String) beans.keySet().iterator().next();
+            AclManager aclManager = (AclManager) context.getBean(beanName);
+
+            // Obtain aclEntrys applying to the current Authentication object
+            AclEntry[] acls = aclManager.getAcls(resolvedDomainObject, auth);
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authentication: '" + auth + "' has: "
+                    + ((acls == null) ? 0 : acls.length)
+                    + " AclEntrys for domain object: '" + resolvedDomainObject
+                    + "' from AclManager: '" + aclManager.toString() + "'");
+            }
+
+            if ((acls == null) || (acls.length == 0)) {
+                return Tag.SKIP_BODY;
+            }
+
+            for (int i = 0; i < acls.length; i++) {
+                // Locate processable AclEntrys
+                if (acls[i] instanceof AbstractBasicAclEntry) {
+                    AbstractBasicAclEntry processableAcl = (AbstractBasicAclEntry) acls[i];
+
+                    // See if principal has any of the required permissions
+                    for (int y = 0; y < requiredIntegers.length; y++) {
+                        if (processableAcl.isPermitted(
+                                requiredIntegers[y].intValue())) {
+                            if (logger.isDebugEnabled()) {
+                                logger.debug(
+                                    "Including tag body as found permission: "
+                                    + requiredIntegers[y]
+                                    + " due to AclEntry: '" + processableAcl
+                                    + "'");
+                            }
+
+                            return Tag.EVAL_BODY_INCLUDE;
+                        }
+                    }
+                }
+            }
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("No permission, so skipping tag body");
+            }
+
+            return Tag.SKIP_BODY;
+        } else {
+            throw new JspException("Unsupported use of auth:acl tag");
+        }
+    }
+
+    /**
+     * Allows test cases to override where application context obtained from.
+     *
+     * @param servletContext as required by Spring's
+     *        <code>WebApplicationContextUtils</code>
+     *
+     * @return the Spring application context
+     */
+    protected ApplicationContext getContext(ServletContext servletContext) {
+        return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
+    }
+
+    private Integer[] parseIntegersString(String integersString)
+        throws NumberFormatException {
+        final Set integers = new HashSet();
+        final StringTokenizer tokenizer;
+        tokenizer = new StringTokenizer(integersString, ",", false);
+
+        while (tokenizer.hasMoreTokens()) {
+            String integer = tokenizer.nextToken();
+            integers.add(new Integer(integer));
+        }
+
+        return (Integer[]) integers.toArray(new Integer[] {});
+    }
+}

+ 102 - 0
core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java

@@ -0,0 +1,102 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.taglibs.authz;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.UserDetails;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+import javax.servlet.jsp.tagext.TagSupport;
+
+
+/**
+ * An {@link javax.servlet.jsp.tagext.Tag} implementation that allows
+ * convenient access to the current <code>Authentication</code> object.
+ * 
+ * <p>
+ * Whilst JSPs can access the <code>ContextHolder</code> directly, this tag
+ * avoids handling <code>null</code> and the incorrect type of
+ * <code>Context</code> in the <code>ContextHolder</code>. The tag also
+ * properly accommodates <code>Authentication.getPrincipal()</code>, which can
+ * either be a <code>String</code> or a <code>UserDetails</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationTag extends TagSupport {
+    //~ Static fields/initializers =============================================
+
+    public static final String OPERATION_PRINCIPAL = "principal";
+
+    //~ Instance fields ========================================================
+
+    private String operation = "";
+
+    //~ Methods ================================================================
+
+    public void setOperation(String operation) {
+        this.operation = operation;
+    }
+
+    public String getOperation() {
+        return operation;
+    }
+
+    public int doStartTag() throws JspException {
+        if ((null == operation) || "".equals(operation)) {
+            return Tag.SKIP_BODY;
+        }
+
+        if ((ContextHolder.getContext() == null)
+            || !(ContextHolder.getContext() instanceof SecureContext)
+            || (((SecureContext) ContextHolder.getContext()).getAuthentication() == null)) {
+            return Tag.SKIP_BODY;
+        }
+
+        Authentication auth = ((SecureContext) ContextHolder.getContext())
+            .getAuthentication();
+
+        if (OPERATION_PRINCIPAL.equalsIgnoreCase(operation)) {
+            if (auth.getPrincipal() == null) {
+                return Tag.SKIP_BODY;
+            } else if (auth.getPrincipal() instanceof UserDetails) {
+                writeMessage(((UserDetails) auth.getPrincipal()).getUsername());
+
+                return Tag.SKIP_BODY;
+            } else {
+                writeMessage(auth.getPrincipal().toString());
+
+                return Tag.SKIP_BODY;
+            }
+        } else {
+            throw new JspException("Unsupported use of auth:athentication tag");
+        }
+    }
+
+    protected void writeMessage(String msg) throws JspException {
+        try {
+            pageContext.getOut().write(String.valueOf(msg));
+        } catch (IOException ioe) {
+            throw new JspException(ioe);
+        }
+    }
+}

+ 52 - 0
core/src/main/resources/org/acegisecurity/taglibs/authz.tld

@@ -50,4 +50,56 @@
 			</description>
 		</attribute>
 	</tag>
+
+	<tag>
+		<name>authentication</name>
+		<tag-class>net.sf.acegisecurity.taglibs.authz.AuthenticationTag</tag-class>
+		<description>
+            Allows access to the current Authentication object.
+		</description>
+
+		<attribute>
+			<name>operation</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+			<description>
+                Must be "principal", for a String representation of the
+				username. An attribute to aid in future extension of the tag.
+			</description>
+		</attribute>
+	</tag>
+
+
+	<tag>
+		<name>acl</name>
+		<tag-class>net.sf.acegisecurity.taglibs.authz.AclTag</tag-class>
+		<description>
+            Allows inclusion of a tag body if the current Authentication
+			has one of the specified permissions to the presented
+			domain object instance. This tag uses the first AclManager
+			it locates via
+			WebApplicationContextUtils.getRequiredWebApplicationContext(HttpServletContext).
+		</description>
+
+		<attribute>
+			<name>hasPermission</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+			<description>
+                A comma separated list of integers, each representing a
+				required bit mask permission from a subclass of
+				net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry.
+			</description>
+		</attribute>
+		<attribute>
+			<name>domainObject</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+			<description>
+                The actual domain object instance for which permissions
+				are being evaluated.
+			</description>
+		</attribute>
+	</tag>
+
 </taglib>