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.
-----Original Message----- From: Jonathan Barker [mailto:[EMAIL PROTECTED] Sent: Saturday, June 10, 2006 1:03 AM To: 'Tapestry users' Subject: RE: Tapestry/Acegi Integration... Hi, I've now been lurking for two years. I finally had a project where I could use Tapestry... and Hibernate, and Spring, and Acegi... So many hills to climb... Anyway, I put together an @Authorize component combining code from the Tapestry @If component and the JSP taglib from Acegi. As it stands, it behaves exactly as an @If, except that it takes comma-separated lists of roles as parameters like the Acegi Auuthorize tag. I also use @Else along with it - it hardly seemed necessary to come up with a different AuthorizeElse component I'm using it in production, and it works beautifully. I have also put together an AclAuthorize component for instance-based security, if you are interested. I would be happy to see this component included in a properly maintained library under the Apache license. At this point, I haven't even had a chance to extract it into a library of my own or get back to it to clean up the code and comments which include remnants of the taglib. I haven't tried tapernate yet so I don't know if this is a simple drop-in. Best regards, Jonathan Barker Code: ===================================================== package ca.itstrategic.tls.ptpip.ui.tapestry.components; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.acegisecurity.Authentication; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.acl.AclManager; import org.acegisecurity.context.SecurityContextHolder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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; import org.apache.tapestry.web.WebRequest; import org.springframework.context.ApplicationContext; import org.springframework.util.StringUtils; import diaphragma.tapspr.XWebApplicationContextUtils; /** * @author jabarker * @version $Revision: $ */ @ComponentClass(allowInformalParameters=true, allowBody=true) public abstract class Authorize extends AbstractFormComponent { @Parameter(required=false) public abstract String getIfAllGranted(); @Parameter(required=false) public abstract String getIfAnyGranted(); @Parameter(required=false) public abstract String getIfNotGranted(); /** * An implementation of {BaseComponent} 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 */ public final static String IF_VALUE_ATTRIBUTE = "org.mb.tapestry.base.IfValue"; @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(); @InjectObject("service:tapestry.globals.WebRequest") public abstract WebRequest getWebRequest(); 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; } @SuppressWarnings("unchecked") private boolean checkPermission(){ String ifAllGranted = getIfAllGranted(); String ifAnyGranted = getIfAnyGranted(); String ifNotGranted = getIfNotGranted(); if (((null == ifAllGranted) || "".equals(ifAllGranted)) && ((null == ifAnyGranted) || "".equals(ifAnyGranted)) && ((null == ifNotGranted) || "".equals(ifNotGranted))) { return false; } final Collection granted = getPrincipalAuthorities(); if ((null != ifNotGranted) && !"".equals(ifNotGranted)) { Set grantedCopy = retainAll(granted, parseAuthoritiesString(ifNotGranted)); if (!grantedCopy.isEmpty()) { return false; } } if ((null != ifAllGranted) && !"".equals(ifAllGranted)) { if (!granted.containsAll(parseAuthoritiesString(ifAllGranted))) { return false; } } if ((null != ifAnyGranted) && !"".equals(ifAnyGranted)) { Set grantedCopy = retainAll(granted, parseAuthoritiesString(ifAnyGranted)); if (grantedCopy.isEmpty()) { return false; } } return true; //========= } /** * 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() ); } private Collection getPrincipalAuthorities() { // SecurityContext sc = (SecurityContext) getWebRequest().getSession(false).getAttribute(org.acegisecurity.context.Htt pSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY); Authentication currentUser = null; // if(sc != null) // currentUser = sc.getAuthentication(); currentUser = SecurityContextHolder.getContext().getAuthentication(); if (null == currentUser) { return Collections.EMPTY_LIST; } if ((null == currentUser.getAuthorities()) || (currentUser.getAuthorities().length < 1)) { return Collections.EMPTY_LIST; } Collection granted = Arrays.asList(currentUser.getAuthorities()); return granted; } @SuppressWarnings("unchecked") private Set authoritiesToRoles(Collection c) { Set target = new HashSet(); for (Iterator iterator = c.iterator(); iterator.hasNext();) { GrantedAuthority authority = (GrantedAuthority) iterator.next(); if (null == authority.getAuthority()) { throw new IllegalArgumentException( "Cannot process GrantedAuthority objects which return null from getAuthority() - attempting to process " + authority.toString()); } target.add(authority.getAuthority()); } return target; } @SuppressWarnings("unchecked") private Set parseAuthoritiesString(String authorizationsString) { final Set requiredAuthorities = new HashSet(); final String[] authorities = StringUtils .commaDelimitedListToStringArray(authorizationsString); for (int i = 0; i < authorities.length; i++) { String authority = authorities[i]; // Remove the role's whitespace characters without depending on JDK 1.4+ // Includes space, tab, new line, carriage return and form feed. String role = StringUtils.replace(authority, " ", ""); role = StringUtils.replace(role, "\t", ""); role = StringUtils.replace(role, "\r", ""); role = StringUtils.replace(role, "\n", ""); role = StringUtils.replace(role, "\f", ""); requiredAuthorities.add(new GrantedAuthorityImpl(role)); } return requiredAuthorities; } /** * Find the common authorities between the current authentication's [EMAIL PROTECTED] * GrantedAuthority} and the ones that have been specified in the tag's * ifAny, ifNot or ifAllGranted attributes. * * <p> * We need to manually iterate over both collections, because the granted * authorities might not implement [EMAIL PROTECTED] Object#equals(Object)} and * [EMAIL PROTECTED] Object#hashCode()} in the same way as [EMAIL PROTECTED] * GrantedAuthorityImpl}, thereby invalidating [EMAIL PROTECTED] * Collection#retainAll(java.util.Collection)} results. * </p> * * <p> * <strong>CAVEAT</strong>: This method <strong>will not</strong> work if * the granted authorities returns a <code>null</code> string as the * return value of [EMAIL PROTECTED] * org.acegisecurity.GrantedAuthority#getAuthority()}. * </p> * * <p> * Reported by rawdave, on Fri Feb 04, 2005 2:11 pm in the Acegi Security * System for Spring forums. * </p> * * @param granted The authorities granted by the authentication. May be any * implementation of [EMAIL PROTECTED] GrantedAuthority} that does * <strong>not</strong> return <code>null</code> from [EMAIL PROTECTED] * org.acegisecurity.GrantedAuthority#getAuthority()}. * @param required A [EMAIL PROTECTED] Set} of [EMAIL PROTECTED] GrantedAuthorityImpl}s that have * been built using ifAny, ifAll or ifNotGranted. * * @return A set containing only the common authorities between * <var>granted</var> and <var>required</var>. * * @see <a * href="http://forum.springframework.org/viewtopic.php?t=3367">authz:authorize * ifNotGranted not behaving as expected</a> */ @SuppressWarnings("unchecked") private Set retainAll(final Collection granted, final Set required) { Set grantedRoles = authoritiesToRoles(granted); Set requiredRoles = authoritiesToRoles(required); grantedRoles.retainAll(requiredRoles); return rolesToAuthorities(grantedRoles, granted); } @SuppressWarnings("unchecked") private Set rolesToAuthorities(Set grantedRoles, Collection granted) { Set target = new HashSet(); for (Iterator iterator = grantedRoles.iterator(); iterator.hasNext();) { String role = (String) iterator.next(); for (Iterator grantedIterator = granted.iterator(); grantedIterator.hasNext();) { GrantedAuthority authority = (GrantedAuthority) grantedIterator .next(); if (authority.getAuthority().equals(role)) { target.add(authority); break; } } } return target; } //======================================================== /** * */ public Authorize() { super(); // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see org.apache.tapestry.form.AbstractFormComponent#getForm() */ @Override public IForm getForm() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see org.apache.tapestry.form.AbstractFormComponent#setForm(org.apache.tapestry.I Form) */ @Override public void setForm(IForm arg0) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see org.apache.tapestry.form.IFormComponent#getDisplayName() */ public String getDisplayName() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see org.apache.tapestry.form.IFormComponent#getClientId() */ public String getClientId() { // TODO Auto-generated method stub return null; } } =========================================================================== Authorize.jwc <?xml version="1.0"?> <!DOCTYPE component-specification PUBLIC "-//Apache Software Foundation//Tapestry Specification 4.0//EN" "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd"> <component-specification class="ca.itstrategic.tls.ptpip.ui.tapestry.components.Authorize"> <inject property="dataSqueezer" object="service:tapestry.data.DataSqueezer"/> <inject property="listenerInvoker" object="infrastructure:listenerInvoker"/> </component-specification> -----Original Message----- From: James Carman [mailto:[EMAIL PROTECTED] Sent: Friday, June 09, 2006 5:49 PM To: 'Tapestry users'; [EMAIL PROTECTED] Subject: RE: Tapestry/Acegi Integration... I was actually thinking of a @Secured component that would conditionally show its contents only if the user has the required permissions. -----Original Message----- From: Hugo Palma [mailto:[EMAIL PROTECTED] Sent: Friday, June 09, 2006 5:45 PM To: Tapestry users Subject: Re: Tapestry/Acegi Integration... I was just thinking about something that would be really cool. It's a common requirement in some applications that some ui elements are hidden/shown depending on user role. What i'm thinking is that tapestry-acegi could provide the same @Secured annotation for component classes but it would have a different behavior. Instead of simply checking authorization and returning an error it the user doesn't have access permissions, it would show the component if the user had the given role and hide it otherwise. Would this be cool or what ? :o) James Carman wrote: > Form-based authentication is coming soon! :-) It should be quite easy. > > >> Hi, >> a smooth integration of ACEGI into Tapestry is really cool stuff. We >> managed it by using >> the internal org.acegisecurity.util.FilterToBeanProxy and >> FilterChainProxy for authentication >> and partially for authorization (user /not logged in without role check) >> within the web.xml. >> >> Does this mean that it would be possible to just configure the >> filterChainProxy in spring and >> inject this spring bean as a "tapestry filter" in front of Tapestry >> servlet? Or am I completely >> wrong? Unfortunately we don't use basic HTTP authentication but a form >> based authentication >> incobination with an internal SSO solution. >> Gernot >> >> On Friday 09 June 2006 10:49, James Carman wrote: >> >>> Gernot, >>> >>> I plan on making the different login mechanisms more "pluggable" soon. >>> I've >>> got an idea that will make it much easier (making Tapestry support >>> servlet >>> filters as ServletRequestServicerFilters so I don't have to write >>> "adapter" >>> subclasses). Once I get everything running smoothly, I'll release it as >>> a >>> 1.0 (hopefully won't be too long). You can use it now as-is, if all you >>> need is HTTP basic authentication. >>> >>> James >>> >> -- >> Gernot Stocker, >> Institute for Genomics and Bioinformatics(IGB) >> Petersgasse 14, 8010 Graz, Austria >> Tel.: ++43 316 873 5345 >> http://genome.tugraz.at >> >> > > > James Carman, President > Carman Consulting, Inc. > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > > > --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]