Hi Warwick

Frustrated with the way that SlideRealm class works I decided to try a different approach. The problem with the Slide realm is that it requires a few libraries to run, many of which also need to be deployed in the web application itself. This seemed wrong to me so I hit upon a new idea.

I exposed Slide authentication through a simple JavaBean (see attached) stored in the Tomcat JNDI context. The new Slide realm accesses the getter and setter methods of this bean without having to know about the Slide API. In fact, the new Slide realm is only dependant on the standard Java libraries and the Tomcat realm libraries.

I could probably optimise the new Slide realm. I'm also unsure about the performance hits with constantly looking up calls. I'd be interested to know what others think.

Kind regards...

--
Ricardo Gladwell


Warwick Burrows wrote:
Ricardo,

I haven't tried this myself with the sliderealm enabled, but the way I use
it right now is to define the path to the domain.xml in the web.xml in your
slide webapp dir eg. webapps/slide/WEB-INF/web.xml. But as I noted I'm using
it this way, but this may not work if you're using the sliderealm since the
realm is loaded from the Tomcat server.xml which seems to be loaded earlier
than the webapps are and with a different classpath to the webapps
themselves.

So if that doesn't work, then I did get it working with the sliderealm
enabled at one time by modifying the domain.xml path in the slide.properties
file that is in ./src/conf/catalina/slide.properties then copying it to a
directory in Tomcat's startup classpath. Eg. common/classes or
server/classes. The path for domain.xml that you use in slide.properties can
be a relative path if you know exactly which directory Tomcat will be
started from but if it can vary then you may want to use a hard-coded path
to the domain.xml. Eg. run startup.sh from within tomcat/bin and your path
is relative to that. Run /tomcat/bin/startup.sh from the root dir and all
paths are relative to that.

Warwick


----------------------------------------------------------- Warwick Burrows E2open Senior Engineer 9600 Great Hills Trail, #325 http://www.e2open.com Austin TX 78759 -----------------------------------------------------------


-----Original Message-----
From: Ricardo Gladwell [mailto:[EMAIL PROTECTED] Sent: Wednesday, July 21, 2004 5:38 AM
To: Slide Users Mailing List
Subject: Tomcat and Security



Hi All,

I am in the process of setting up security for Slide through Tomcat using the SlideRealm. However, I am experiencing some problems. When I deploy the application I get an InvocationTargetException as the servlet containter complains that it cannot find the Domain.xml. I assume this is because the SlideRealm is attempting to look up Domain.xml but I have idea how to specify the location of the domain.xml to the SlideRealm:

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39
)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl
.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.commons.beanutils.MethodUtils.invokeMethod(MethodUtils.java:252)
at org.apache.commons.digester.SetNextRule.end(SetNextRule.java:256)
at org.apache.commons.digester.Rule.end(Rule.java:276)
at
org.apache.commons.digester.Digester.endElement(Digester.java:1058)
at org.apache.catalina.util.CatalinaDigester.endElement(CatalinaDigester.java:7
6)
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown
Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanEndElement(Unknown


Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatc
her.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown
Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown
Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at org.apache.commons.digester.Digester.parse(Digester.java:1567)
at org.apache.catalina.core.StandardHostDeployer.install(StandardHostDeployer.j
ava:488)
at
org.apache.catalina.core.StandardHost.install(StandardHost.java:863)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:482
)
at
org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:427)
at
org.apache.catalina.startup.HostConfig.check(HostConfig.java:1064)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:327)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSuppor
t.java:119)
at org.apache.catalina.core.StandardHost.backgroundProcess(StandardHost.java:80
0)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processC
hildren(ContainerBase.java:1619)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processC
hildren(ContainerBase.java:1628)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(Cont
ainerBase.java:1608)
at java.lang.Thread.run(Thread.java:534)
Caused by: org.apache.slide.common.DomainInitializationFailedError: Domain initialization error : Domain.xml (The system cannot find the file specified)
at org.apache.slide.common.Domain.selfInit(Domain.java:752)
at org.apache.slide.common.Domain.accessNamespace(Domain.java:235)
at wrappers.catalina.SlideRealm.start(SlideRealm.java:178)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4222)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:8
23)
at
org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:807)
at
org.apache.catalina.core.StandardHost.addChild(StandardHost.java:595)
at org.apache.catalina.core.StandardHostDeployer.addChild(StandardHostDeployer.
java:903)
... 30 more


I'm also slightly confused as to why the SlideRealm is attempting to look-up the Domain.xml at webapp initialisation when it is expecting the Domain.xml to be contained in the web application itself: surely a case of whether the chicken or the egg came first? There do not seem to be any parameters you can pass to the SlideRealm? I also notice that the SlideRealm requires the other Slide libraries to work. Surely, it would be better to make the SlideRealm more 'independant' by allowing it to be used without the various Slide JAR files?

Kind regarsd...

package net.sf.jexus.server.slide;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.NameNotFoundException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.slide.webdav.WebdavServlet;

/**
 * Extension of Slide WebdavServlet that stores a reference to the NAT
 * in JNDI so it can be referenced by the JexusSlideRealm on the fly.
 * @author rgladwel
 */
public class JexusWebdavServlet extends WebdavServlet {

    /**
     * Log for this class.
     */
    private static final Log log = LogFactory.getLog(JexusWebdavServlet.class);

    /**
     * Key NAT JNDI context key for Servlet initialisation parameter.
     */
    private static final String NAT_JNDI_CONTEXT_INIT_PARAM = "nat-jndi-context";

    /**
     * Default NAT JNDI context.
     */
    private static final String DEFAULT_NAT_JNDI_CONTEXT = "SlideAuthenticationBean";

    String jndiContext;

    SlideAuthenticationBean bean = new SlideAuthenticationBean();

    /**
     * After performing normal Slide WebdavServlet initialisation stores NAT
     * in JNDI context as specified by [EMAIL PROTECTED] NAT_JNDI_CONTEXT_INIT_PARAM}.
     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        if(log.isTraceEnabled()) log.trace("init("+config+")");
        jndiContext = config.getInitParameter(NAT_JNDI_CONTEXT_INIT_PARAM);
        if(jndiContext == null) {
            log.debug(NAT_JNDI_CONTEXT_INIT_PARAM+" not set: not storing NAT in 
default context.");
            jndiContext = DEFAULT_NAT_JNDI_CONTEXT;
        }
        log.debug("context=["+jndiContext+"]");
        bean.setAccessToken(token);
        try {
            InitialContext context = new InitialContext();
            if(log.isDebugEnabled()) log.debug("Initial 
context=["+context.getNameInNamespace()+"]");
            Context slideContext;

            try {
                slideContext = (Context) context.lookup("slide");
                log.debug("Found slide context.");
            } catch (NameNotFoundException e) {
                log.debug("Creating slide context.");
                slideContext = context.createSubcontext("slide");
            }

            if(log.isDebugEnabled()) log.debug("Binding 
SlideAuthenticationBean=["+bean+"] to context=["+slideContext+"]");
            try{
                slideContext.lookup(jndiContext);
                slideContext.rebind(jndiContext,bean);
            }catch (NameNotFoundException e) {
                slideContext.bind(jndiContext,bean);
            }

            context.lookup("/slide/"+jndiContext);
            log.info("SlideAuthenticationBean bound to 
context=["+context.getNameInNamespace()+"/slide/"+jndiContext+"]");
        } catch(NamingException e) {
            throw new ServletException("Error binding SlideAuthenticationBean to 
context=[java:/slide/"+jndiContext+"]",e);
        }
    }

    public void destroy() {
        if(log.isTraceEnabled()) log.trace("destroy()");
        try {
            InitialContext context = new InitialContext();
            Context slideContext = (Context) context.lookup("slide");
            slideContext.unbind(jndiContext);
            log.info("SlideAuthenticationBean unbound from 
context=["+context.getNameInNamespace()+"/slide/"+jndiContext+"]");
        } catch(NamingException e) {
            log.warn("Error binding SlideAuthenticationBean to 
context=[java:/slide/"+jndiContext+"]",e);
        }
        super.destroy();
    }
}
package net.sf.jexus.server.slide;

import java.security.Principal;

import org.apache.slide.authenticate.CredentialsToken;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.SlideTokenImpl;
import org.apache.slide.content.Content;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.security.Security;
import org.apache.slide.structure.ObjectNode;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author rgladwel
 */
public class SlideAuthenticationBean {

    private final static Log log = LogFactory.getLog(SlideAuthenticationBean.class);

    NamespaceAccessToken token;

    ThreadLocal userNameStore = new ThreadLocal();
    ThreadLocal roleStore = new ThreadLocal();
    ThreadLocal principalStore = new ThreadLocal();

    public SlideAuthenticationBean() {
    }

    public void setAccessToken(NamespaceAccessToken token) {
        this.token = token;
    }

    protected NamespaceAccessToken getAccessToken() {
        return token;
    }

    protected Security getSecurityHelper() {
        return getAccessToken().getSecurityHelper();
    }

    protected Content getContentHelper() {
        return getAccessToken().getContentHelper();
    }

    protected String getUsersPath() {
        return getAccessToken().getNamespaceConfig().getUsersPath();
    }

    public void setUserName(String userName) {
        if(log.isTraceEnabled()) log.trace("setUserName("+userName+")");
        userNameStore.set(userName);
    }

    public String getUserName() {
        return (String) userNameStore.get();
    }

    public void setRole(String role) {
        if(log.isTraceEnabled()) log.trace("setRole("+role+")");
        roleStore.set(role);
    }

    public String getRole() {
        return (String) roleStore.get();
    }

    public void setPrincipal(Principal principal) {
        if(log.isTraceEnabled()) log.trace("setPrincipal("+principal+")");
        principalStore.set(principal);
    }

    public Principal getPrincipal() {
        return (Principal) principalStore.get();
    }

    public String getPassword() {
        if(log.isTraceEnabled()) log.trace("getPassword()");
        Principal userPrincipal = getPrincipal();
        if(userPrincipal == null) {
            log.warn("Principal not set - not authenticating.");
            return null;
        }
        CredentialsToken credToken = new CredentialsToken(userPrincipal);
        SlideToken slideToken = new SlideTokenImpl(credToken);

        // Fetch the Slide object representing the user.
        try {
            ObjectNode user = getSecurityHelper().getPrincipal(slideToken);
        } catch(SlideException e) {
            log.error("Error getting principal.",e);
            return null;
        }

        String passwordValue = null;

        try {

            NodeRevisionDescriptors revisionDescriptors = token.getContentHelper()
                    .retrieve(slideToken, getUsersPath() + "/" + getUserName());
            NodeRevisionDescriptor revisionDescriptor = 
token.getContentHelper().retrieve(
                    slideToken, revisionDescriptors);
            NodeProperty password = revisionDescriptor.getProperty("password",
                    NodeProperty.SLIDE_NAMESPACE);
            if (password != null) {
                passwordValue = (String) password.getValue();
            }

        } catch(AccessDeniedException e) {
            log.warn("Access denied getting password for user=["+getUserName()+"]");
            return null;
        } catch (SlideException e) {
            log.error("Error getting password.",e);
            return null;
        }

        if (passwordValue == null) {
            log.warn("User "+getUserName()+" doesn't have his password property set : 
can't authenticate");
        }
        
        log.debug("User=["+getUserName()+"],password=["+passwordValue+"]");
        return passwordValue;
    }

    public void setPassword(String password) {
        // do nothing
    }

    public boolean isHasRole() {
        if(log.isTraceEnabled()) log.trace("isHasRole()");
        Principal userPrincipal = getPrincipal();
        if(userPrincipal == null) return false;
        CredentialsToken credToken = new CredentialsToken(userPrincipal);
        SlideToken slideToken = new SlideTokenImpl(credToken);
        try {
            boolean hasRole = getSecurityHelper().hasRole(slideToken, getRole());
            if(log.isDebugEnabled()) {
                if(hasRole) log.debug("user=["+getUserName()+"] has 
role=["+getRole()+"]");
                else log.debug("user=["+getUserName()+"] does not have 
role=["+getRole()+"]");
            }
            return hasRole;
        } catch (SlideException e) {
            log.error("Error getting role.",e);
            return false;
        }

    }

    public void setHasRole(boolean hasROle) {
        // do nothing
    }

}
/*
 * $Header: 
/cvsroot/jexus/jexus-slide-realm/src/net/sf/jexus/server/catalina/JexusSlideRealm.java,v
 1.2 2004/07/23 13:46:30 axonrg Exp $
 * $Revision: 1.2 $
 * $Date: 2004/07/23 13:46:30 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 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.
 *
 */
package net.sf.jexus.server.catalina;

import java.security.Principal;
import java.beans.PropertyDescriptor;
import java.beans.IntrospectionException;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.realm.RealmBase;

/**
 * Implemetation of a Catalina realm which authenticates users defined in a
 * Slide namespace.
 * <p>
 * The namespace used will have the same name as the container to which the
 * realm is associated. If such a namespace doesn't exist, it falls back to
 * tomcat, webdav or default.
 * 
 * @author Remy Maucherat
 * @author Ricardo Gladwell
 * @version $Revision: 1.2 $ $Date: 2004/07/23 13:46:30 $
 */
public final class JexusSlideRealm extends RealmBase {

    // ----------------------------------------------------- Instance Variables

    /**
     * Descriptive information about this Realm implementation.
     */
    private static final String info = 
"net.sf.jexus.server.catalina.JexusSlideRealm/1.0";

    /**
     * JNDI context name where the NAT is stored.
     */
    private String binding;

    /**
     * Reference to SlideAuthenticationBean
     */
    Object bean = null;

    // ------------------------------------------------------------- Properties

    /**
     * Set the context name where the NAT is stored.
     */
    public void setBinding(String binding) {
        this.binding = binding;
    }

    /**
     * Get name.
     */
    public String getName() {
        return "Jexus Slide realm";
    }

    // --------------------------------------------------------- Public Methods

    /**
     * Return <code>true</code> if the specified Principal has the specified
     * security role, within the context of this Realm; otherwise return
     * <code>false</code>.
     * 
     * @param principal
     *            Principal for whom the role is to be checked
     * @param role
     *            Security role to be checked
     */
    public boolean hasRole(Principal principal, String role) {
        Object bean;
        try {
            bean = getAuthenticationBean();
        } catch(NamingException e) {
            log("Error obtaining reference to authentication bean.",e);
            return false;
        }

        if(bean == null) {
            log("Authentication bean not retrieved value=["+bean+"]");
            return false;
        }
        
        try {
            PropertyDescriptor roleProperty = new 
PropertyDescriptor("role",bean.getClass());
            Method setRoleMethod = roleProperty.getWriteMethod();
            String[] args = new String[1];
            args[0] = role;
            setRoleMethod.invoke(bean,args);
        } catch(IntrospectionException e) {
            log("Error examining authentication bean userName property.",e);
            return false;    
        } catch(IllegalAccessException e) {
            log("Access denied authentication bean userName property.",e);
            return false; 
        } catch(InvocationTargetException e) {
            log("Error occured within authentication bean userName property.",e);
            return false; 
        }

        try {
            PropertyDescriptor hasRoleProperty = new 
PropertyDescriptor("hasRole",bean.getClass());
            Method getRoleMethod = hasRoleProperty.getReadMethod();
            Object[] args = new Object[0];
            return ((Boolean) getRoleMethod.invoke(bean,args)).booleanValue();
        } catch(IntrospectionException e) {
            log("Error examining authentication bean password property.",e);
            return false;    
        } catch(IllegalAccessException e) {
            log("Access denied authentication bean password property.",e);
            return false; 
        } catch(InvocationTargetException e) {
            log("Error occured within authentication bean password property.",e);
            return false; 
        }
    }

    protected Object getAuthenticationBean() throws NamingException {
        if(bean == null) {
            InitialContext context = new InitialContext();
            bean = context.lookup(binding);
        }
        return bean;
    }

    /**
     * Start the realm.
     */
    public void start() throws LifecycleException {
        super.start();
    }

    // ------------------------------------------------------ Protected Methods

    /**
     * Return the password associated with the given principal's user name.
     * @see org.apache.catalina.realm.RealmBase#getPassword
     */
    protected String getPassword(String username) {
        Object bean;
        try {
            bean = getAuthenticationBean();
        } catch(NamingException e) {
            log("Error obtaining reference to authentication bean.",e);
            return null;
        }

        if(bean == null) {
            log("Authentication bean not retrieved value=["+bean+"]");
            return null;
        }

        try {
            PropertyDescriptor userNameProperty = new 
PropertyDescriptor("userName",bean.getClass());
            Method setUserNameMethod = userNameProperty.getWriteMethod();
            String[] args = new String[1];
            args[0] = username;
            setUserNameMethod.invoke(bean,args);
        } catch(IntrospectionException e) {
            log("Error examining authentication bean userName property.",e);
            return null;    
        } catch(IllegalAccessException e) {
            log("Access denied authentication bean userName property.",e);
            return null; 
        } catch(InvocationTargetException e) {
            log("Error occured within authentication bean userName property.",e);
            return null; 
        }

        try {
            PropertyDescriptor principalProperty = new 
PropertyDescriptor("principal",bean.getClass());
            Method setPrincipalMethod = principalProperty.getWriteMethod();
            Principal[] args = new Principal[1];
            args[0] = getPrincipal(username);
            setPrincipalMethod.invoke(bean,args);
        } catch(IntrospectionException e) {
            log("Error examining authentication bean userName property.",e);
            return null;    
        } catch(IllegalAccessException e) {
            log("Access denied authentication bean userName property.",e);
            return null; 
        } catch(InvocationTargetException e) {
            log("Error occured within authentication bean userName property.",e);
            return null; 
        }

        try {
            PropertyDescriptor passwordProperty = new 
PropertyDescriptor("password",bean.getClass());
            Method getPasswordMethod = passwordProperty.getReadMethod();
            Object[] args = new Object[0];
            return (String) getPasswordMethod.invoke(bean,args);
        } catch(IntrospectionException e) {
            log("Error examining authentication bean password property.",e);
            return null;    
        } catch(IllegalAccessException e) {
            log("Access denied authentication bean password property.",e);
            return null; 
        } catch(InvocationTargetException e) {
            log("Error occured within authentication bean password property.",e);
            return null; 
        }
    }

    /**
     * Return the Principal associated with the given user name.
     * @see org.apache.catalina.realm.RealmBase#getPrincipal
     */
    protected Principal getPrincipal(String username) {
        return new SlideRealmPrincipal(username);
    }

}

/**
 * Private class representing an individual user's Principal object.
 */
final class SlideRealmPrincipal implements Principal {

    /**
     * The username for this Principal.
     */
    private String username = null;

    /**
     * Construct a new MemoryRealmPrincipal instance.
     * @param username
     *            The username for this Principal
     */
    public SlideRealmPrincipal(String username) {
        this.username = username;
    }

    /**
     * Return the name of this Principal.
     */
    public String getName() {
        return (username);
    }

}


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to