diff --git a/core/src/main/java/org/acegisecurity/taglibs/authz/AclTag.java b/core/src/main/java/org/acegisecurity/taglibs/authz/AclTag.java
new file mode 100644
index 0000000000..bedb19cefe
--- /dev/null
+++ b/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.
+ *
+ *
+ * Only works with permissions that are subclasses of {@link
+ * net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry}.
+ *
+ *
+ *
+ * One or more comma separate integer permissions are specified via the
+ * hasPermission attribute. The tag will include its body if
+ * any of the integer permissions have been granted to the current
+ * Authentication (obtained from the ContextHolder).
+ *
+ *
+ *
+ * For this class to operate it must be able to access the application context
+ * via the WebApplicationContextUtils and locate an {@link
+ * AclManager}. Application contexts have no need to have more than one
+ * AclManager (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 AclManager located will be
+ * used.
+ *
+ *
+ * @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
+ * WebApplicationContextUtils
+ *
+ * @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[] {});
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java b/core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java
new file mode 100644
index 0000000000..e361835ed2
--- /dev/null
+++ b/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 Authentication object.
+ *
+ *
+ * Whilst JSPs can access the ContextHolder directly, this tag
+ * avoids handling null and the incorrect type of
+ * Context in the ContextHolder. The tag also
+ * properly accommodates Authentication.getPrincipal(), which can
+ * either be a String or a UserDetails.
+ *
+ *
+ * @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);
+ }
+ }
+}
diff --git a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld
index e26678ef2d..e46f254b16 100644
--- a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld
+++ b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld
@@ -50,4 +50,56 @@
+
+
+ authentication
+ net.sf.acegisecurity.taglibs.authz.AuthenticationTag
+
+ Allows access to the current Authentication object.
+
+
+
+ operation
+ true
+ true
+
+ Must be "principal", for a String representation of the
+ username. An attribute to aid in future extension of the tag.
+
+
+
+
+
+
+ acl
+ net.sf.acegisecurity.taglibs.authz.AclTag
+
+ 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).
+
+
+
+ hasPermission
+ true
+ true
+
+ A comma separated list of integers, each representing a
+ required bit mask permission from a subclass of
+ net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry.
+
+
+
+ domainObject
+ true
+ true
+
+ The actual domain object instance for which permissions
+ are being evaluated.
+
+
+
+