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]

Reply via email to