James, I'm including the code for the AclAuthorize component. Again, it is based on the Acegi JSP taglib code, and the tapestry @If component, and requires cleanup as before.
Perhaps "@AclSecured" would be an appropriate given your current naming. The application I developed most recently services a call center, as well as the clients they service, who are part of the same franchise company. As such, there are some interesting security restrictions because it's seldom an all-or-none situation. The declarative security so easy in Acegi hasn't really applied very well. The combination of the Authorize and AclAuthorize (with a few custom AclProvider implementations), has been great. I use them to selectively display links (including in menus), and show or hide information on a page that should be available to one group, but not another. Really cool stuff. I had two basic reasons for using Spring. 1) Hibernate support via Hivemind was a little immature when I started, and 2) I knew I would be using Acegi. I'm looking forward to giving Tapernate a try now that it will be combining solutions to my major problems. On an unrelated note, I'm interested in Conversations. Have you looked at the Seam project at JBoss? Jonathan ================================== //Copyright 2004, 2005, 2006 The Apache Software Foundation // // 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.* /* Created on Apr 7, 2006 * * $Id: $ * * */ /* * This class is an adaptation of the code developed by Ben Alex and published * under the Apache License, Version 2.0. * */ package ca.itstrategic.tls.ptpip.ui.tapestry.components; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.acegisecurity.Authentication; import org.acegisecurity.acl.AclEntry; import org.acegisecurity.acl.AclManager; import org.acegisecurity.acl.basic.BasicAclEntry; import org.acegisecurity.context.SecurityContextHolder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import diaphragma.tapspr.XWebApplicationContextUtils; import org.apache.hivemind.ApplicationRuntimeException; import org.apache.hivemind.HiveMind; import org.apache.tapestry.IActionListener; import org.apache.tapestry.IBinding; import org.apache.tapestry.IForm; import org.apache.tapestry.IMarkupWriter; import org.apache.tapestry.IRequestCycle; import org.apache.tapestry.Tapestry; import org.apache.tapestry.TapestryUtils; import org.apache.tapestry.annotations.ComponentClass; import org.apache.tapestry.annotations.InjectObject; import org.apache.tapestry.annotations.Parameter; import org.apache.tapestry.form.AbstractFormComponent; import org.apache.tapestry.services.DataSqueezer; import org.apache.tapestry.web.WebContext; /** * @author jabarker * @version $Revision: $ */ @ComponentClass(allowBody=true , allowInformalParameters=true) public abstract class AclAuthorize extends AbstractFormComponent { /** * An implementation of {AbstractFormComponent} that allows its * body through if some authorizations are granted to the request's principal. * * <P> * Only works with permissions that are subclasses of [EMAIL PROTECTED] * org.acegisecurity.acl.basic.BasicAclEntry}. * </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>SecurityContextHolder</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 [EMAIL PROTECTED] * 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: AclTag.java,v 1.8 2005/11/17 00:56:29 benalex Exp $ */ public final static String IF_VALUE_ATTRIBUTE = "org.mb.tapestry.base.IfValue"; @Parameter(required=true) public abstract void setDomainObject(Object domainObject); public abstract Object getDomainObject(); @Parameter(required=true) public abstract String getHasPermission(); public abstract void setHasPermission(String hasPermission); @Parameter(required=false) public abstract IBinding getConditionValueBinding(); @Parameter(required=false) public abstract boolean getVolatile(); @Parameter(required=false) public abstract String getElement(); @Parameter(required=false) public abstract IActionListener getListener(); private boolean _rendering = false; private boolean _conditionValue; //~ Static fields/initializers ============================================= protected static final Log logger = LogFactory.getLog(AclAuthorize.class); //~ Methods ================================================================ @InjectObject("service:tapestry.globals.WebContext") public abstract WebContext getWebContext(); protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) { boolean cycleRewinding = cycle.isRewinding(); // form may be null if component is not located in a form IForm form = (IForm) cycle.getAttribute(TapestryUtils.FORM_ATTRIBUTE); // If the cycle is rewinding, but not this particular form, // then do nothing (don't even render the body). if (cycleRewinding && form != null && !form.isRewinding()) return; // get the condition. work with a hidden field if necessary _conditionValue = evaluateCondition(cycle, form, cycleRewinding); _rendering = true; try { // call listener IActionListener listener = getListener(); if (listener != null) listener.actionTriggered(this, cycle); // now render if condition is true if (_conditionValue) { String element = getElement(); boolean render = !cycleRewinding && HiveMind.isNonBlank(element); if (render) { writer.begin(element); renderInformalParameters(writer, cycle); } renderBody(writer, cycle); if (render) writer.end(element); } } finally { _rendering = false; } cycle.setAttribute(IF_VALUE_ATTRIBUTE, new Boolean(_conditionValue)); } protected boolean evaluateCondition(IRequestCycle cycle, IForm form, boolean cycleRewinding) { boolean condition; if (form == null || getVolatile()) { condition = checkPermission(); } else { // we are in a form and we care -- load/store the condition in a hidden field String name = form.getElementId(this); if (!cycleRewinding) { condition = checkPermission(); writeValue(form, name, condition); } else { condition = readValue(cycle, name); } } // write condition value if parameter is bound IBinding conditionValueBinding = getConditionValueBinding(); if (conditionValueBinding != null) conditionValueBinding.setObject(new Boolean(condition)); return condition; } private void writeValue(IForm form, String name, boolean value) { String externalValue; Object booleanValue = new Boolean(value); try { externalValue = getDataSqueezer().squeeze(booleanValue); } catch (Exception ex) { throw new ApplicationRuntimeException(Tapestry.format( "If.unable-to-convert-value", booleanValue), this, null, ex); } form.addHiddenValue(name, externalValue); } private boolean readValue(IRequestCycle cycle, String name) { String submittedValue = cycle.getParameter(name); try { Object valueObject = getDataSqueezer().unsqueeze(submittedValue); if (!(valueObject instanceof Boolean)) throw new ApplicationRuntimeException(Tapestry.format( "If.invalid-condition-type", submittedValue)); return ((Boolean) valueObject).booleanValue(); } catch (Exception ex) { throw new ApplicationRuntimeException(Tapestry.format( "If.unable-to-convert-string", submittedValue), this, null, ex); } } public abstract DataSqueezer getDataSqueezer(); public boolean isDisabled() { return false; } /** * Returns the value of the condition * * @return the condition value */ public boolean getConditionValue() { if (!_rendering) throw Tapestry.createRenderOnlyPropertyException(this, "conditionValue"); return _conditionValue; } // Do nothing in those methods, but make the JVM happy protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) { } protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) { } /** * For component can not take focus. */ protected boolean getCanTakeFocus() { return false; } private boolean checkPermission(){ String hasPermission = getHasPermission(); if ((null == hasPermission) || "".equals(hasPermission)) { return false; } Integer[] requiredIntegers = null; try { requiredIntegers = parseIntegersString(hasPermission); } catch (NumberFormatException nfe) { throw new RuntimeException(nfe); } Object domainObject = getDomainObject(); if (domainObject == null) { if (logger.isDebugEnabled()) { logger.debug( "domainObject resolved to null, so including tag body"); } return true; } if (SecurityContextHolder.getContext().getAuthentication() == null) { if (logger.isDebugEnabled()) { logger.debug( "SecurityContextHolder did not return a non-null Authentication object, so skipping tag body"); } return false; } Authentication auth = SecurityContextHolder.getContext() .getAuthentication(); ApplicationContext context = getContext(); Map beans = context.getBeansOfType(AclManager.class, false, false); if (beans.size() == 0) { throw new RuntimeException( "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(domainObject, auth); if (logger.isDebugEnabled()) { logger.debug("Authentication: '" + auth + "' has: " + ((acls == null) ? 0 : acls.length) + " AclEntrys for domain object: '" + domainObject + "' from AclManager: '" + aclManager.toString() + "'"); } if ((acls == null) || (acls.length == 0)) { return false; } for (int i = 0; i < acls.length; i++) { // Locate processable AclEntrys if (acls[i] instanceof BasicAclEntry) { BasicAclEntry processableAcl = (BasicAclEntry) 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 true; } } } } if (logger.isDebugEnabled()) { logger.debug("No permission, so skipping tag body"); } return false; } /** * Allows test cases to override where application context obtained from. * * @param pageContext so the <code>ServletContext</code> can be accessed as * required by Spring's <code>WebApplicationContextUtils</code> * * @return the Spring application context (never <code>null</code>) */ protected ApplicationContext getContext(){ return XWebApplicationContextUtils.getRequiredWebApplicationContext(getWebContext() ); } @SuppressWarnings("unchecked") 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[] {}); } } ================================================ -----Original Message----- From: James Carman [mailto:[EMAIL PROTECTED] Sent: Sunday, June 11, 2006 8:00 PM To: 'Tapestry users' Subject: RE: Tapestry/Acegi Integration... Sounds good! I'll probably call mine Secured, since it will match up with the @Secured annotation that we use in code. But, I'll probably lean on some of the work you've done here once I get a chance to read through it. <snip> --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]