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]

Reply via email to