You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by vt...@apache.org on 2004/02/16 07:14:15 UTC

svn commit: rev 6688 - in incubator/directory/janus/trunk/authentication/xml: . src src/java src/java/org src/java/org/apache src/java/org/apache/janus src/java/org/apache/janus/authentication src/java/org/apache/janus/authentication/realm src/test src/test/org src/test/org/apache src/test/org/apache/janus src/test/org/apache/janus/authentication src/test/org/apache/janus/authentication/realm

Author: vtence
Date: Sun Feb 15 22:14:14 2004
New Revision: 6688

Added:
   incubator/directory/janus/trunk/authentication/xml/
   incubator/directory/janus/trunk/authentication/xml/project.xml
   incubator/directory/janus/trunk/authentication/xml/src/
   incubator/directory/janus/trunk/authentication/xml/src/java/
   incubator/directory/janus/trunk/authentication/xml/src/java/org/
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/AvalonLoggerAdapter.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/AvalonXMLRealm.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/DefaultXMLRealm.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/Dom4JRealmBuilder.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/NullRealmBuilderMonitor.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/PicoXMLRealm.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/RealmBuilder.java
   incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/RealmBuilderMonitor.java
   incubator/directory/janus/trunk/authentication/xml/src/test/
   incubator/directory/janus/trunk/authentication/xml/src/test/org/
   incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/
   incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/
   incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/
   incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/
   incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/DefaultXMLRealmTest.java
   incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/Dom4JRealmBuilderTest.java
Log:
Initial import

Added: incubator/directory/janus/trunk/authentication/xml/project.xml
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/project.xml	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project>
+    <extend>${basedir}/../../project.xml</extend>
+
+    <name>Janus Authentication XML Implementation</name>
+    <id>janus-authentication-xml</id>
+    <package>org.apache.janus.authentication</package>
+
+    <shortDescription>Janus Authentication XML Implementation</shortDescription>
+
+    <description>
+    XML Implementation of the Janus Security Framework Authentication API
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>janus-authentication-api</artifactId>
+            <version>${pom.currentVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>janus-authentication-impl</artifactId>
+            <version>${pom.currentVersion}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>avalon-framework</groupId>
+            <artifactId>avalon-framework</artifactId>
+            <version>4.1.5</version>
+        </dependency>
+
+        <!-- XML Stuff -->
+        <dependency>
+            <groupId>dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>1.4</version>
+        </dependency>
+        <dependency>
+            <groupId>xerces</groupId>
+            <artifactId>xercesImpl</artifactId>
+            <version>2.6.0</version>
+        </dependency>
+        <dependency>
+            <groupId>xml-apis</groupId>
+            <artifactId>xml-apis</artifactId>
+            <version>1.0.b2</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/AvalonLoggerAdapter.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/AvalonLoggerAdapter.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,111 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import org.apache.avalon.framework.logger.Logger;
+
+import java.security.Principal;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class AvalonLoggerAdapter
+        implements RealmBuilderMonitor
+{
+    private final Logger m_logger;
+
+    public AvalonLoggerAdapter( Logger logger )
+    {
+        m_logger = logger;
+    }
+
+    public void duplicatePrincipal( PrincipalAlreadyExistsException e )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug(
+                    "Principal " + e.getPrincipal().getName()
+                    + " is already defined in realm and will be skipped" );
+        }
+    }
+
+    public void duplicateGroup( PrincipalAlreadyExistsException e )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug(
+                    "Group " + e.getPrincipal().getName()
+                    + " is already defined in realm and will be skipped" );
+        }
+    }
+
+    public void invalidSubGroup( Principal group, Principal invalid )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug(
+                    "Group " + invalid.getName()
+                    + " was not found in realm and could not be added to group "
+                    + group.getName() );
+        }
+    }
+
+    public void cyclicDependency( Principal group, Principal subGroup )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug( "Cyclic dependency detected; group "
+                            + subGroup.getName()
+                            + " will not be added to group " + group.getName() );
+        }
+    }
+
+    public void invalidMember( Principal group, Principal invalid )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug(
+                    "Principal " + invalid.getName()
+                    + " was not found in realm and could not be added to group "
+                    + group.getName() );
+        }
+    }
+
+    public void duplicateSubGroup( Principal group, Principal duplicate )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug(
+                    "Group " + duplicate.getName()
+                    + " is already defined as sub-group of group "
+                    + group.getName()
+                    + " and will be skipped" );
+        }
+    }
+
+    public void duplicateMember( Principal group, Principal duplicate )
+    {
+        if ( m_logger.isDebugEnabled() )
+        {
+            m_logger.debug(
+                    "Principal " + duplicate.getName()
+                    + " is already defined as member of group "
+                    + group.getName()
+                    + " and will be skipped" );
+        }
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/AvalonXMLRealm.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/AvalonXMLRealm.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,70 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.LogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.xml.sax.InputSource;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class AvalonXMLRealm
+        extends DefaultXMLRealm
+        implements LogEnabled, Configurable, Initializable
+{
+    private Logger m_logger;
+    private URL m_url;
+
+    public void enableLogging( Logger logger )
+    {
+        m_logger = logger;
+    }
+
+    public void configure( Configuration configuration )
+            throws ConfigurationException
+    {
+        Configuration child = configuration.getChild( "realm-url", false );
+        if ( child == null )
+        {
+            throw new ConfigurationException( "No realm url defined" );
+        }
+
+        try
+        {
+            m_url = new URL( child.getValue() );
+        }
+        catch ( MalformedURLException e )
+        {
+            throw new ConfigurationException( "Malformed realm url", e );
+        }
+    }
+
+    public void initialize() throws Exception
+    {
+        setRealmSource( new InputSource( m_url.openStream() ) );
+        setMonitor( new AvalonLoggerAdapter( m_logger ) );
+        super.initialize();
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/DefaultXMLRealm.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/DefaultXMLRealm.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,68 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import org.apache.janus.authentication.CredentialCollection;
+import org.dom4j.Document;
+import org.dom4j.io.SAXReader;
+import org.xml.sax.InputSource;
+
+import java.security.Principal;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class DefaultXMLRealm implements Realm, GroupSupport
+{
+    private RealmBuilderMonitor m_monitor = new NullRealmBuilderMonitor();
+    private InputSource m_realmSource;
+    private DefaultRealm m_delegate;
+
+    public void setRealmSource( InputSource source )
+    {
+        m_realmSource = source;
+    }
+
+    public void setMonitor( RealmBuilderMonitor monitor )
+    {
+        m_monitor = monitor;
+    }
+
+    /*
+     * todo: add document validation against schema
+     */
+    public void initialize() throws Exception
+    {
+
+        SAXReader reader = new SAXReader();
+        Document root = reader.read( m_realmSource );
+        RealmBuilder builder = new Dom4JRealmBuilder( root, m_monitor );
+        m_delegate = new DefaultRealm();
+        builder.buildRealm( m_delegate );
+    }
+
+    public Principal validateCredentials( CredentialCollection credentials )
+    {
+        return m_delegate.validateCredentials( credentials );
+    }
+
+    public Set getGroupsForPrincipal( Principal principal )
+    {
+        return m_delegate.getGroupsForPrincipal( principal );
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/Dom4JRealmBuilder.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/Dom4JRealmBuilder.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,199 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import org.apache.janus.authentication.Credential;
+import org.dom4j.Document;
+import org.dom4j.Element;
+
+import java.security.Principal;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A builder who reads realm content from a DOM4J document.
+ * <p/>
+ * This document is assumed to be valid at construction, so this implementation
+ * does not perform any validation.
+ * <p/>
+ * todo: keep reference on principals created instead of instanciating transient ones
+ *
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class Dom4JRealmBuilder implements RealmBuilder
+{
+    private final Document m_doc;
+    private final RealmBuilderMonitor m_monitor;
+
+    public Dom4JRealmBuilder( Document doc )
+    {
+        this( doc, new NullRealmBuilderMonitor() );
+    }
+
+    public Dom4JRealmBuilder( Document doc, RealmBuilderMonitor monitor )
+    {
+        m_doc = doc;
+        m_monitor = monitor;
+    }
+
+    public void buildRealm( MutableRealm realm ) throws Exception
+    {
+        Element root = m_doc.getRootElement();
+
+        Element users = root.element( "users" );
+        addUsers( realm, users );
+
+        Element groups = root.element( "groups" );
+        addGroups( realm, groups );
+        addMembers( realm, groups );
+    }
+
+    private void addUsers( MutableRealm realm, Element users )
+    {
+        List usersList = users.elements( "user" );
+        for ( Iterator it = usersList.iterator(); it.hasNext(); )
+        {
+            final Element element = (Element) it.next();
+            addUserToRealm( realm, element );
+        }
+    }
+
+    private void addUserToRealm( MutableRealm realm, final Element element )
+    {
+        String username = element.attributeValue( "username" );
+        String password = element.attributeValue( "password" );
+        try
+        {
+            Principal user = realm.addPrincipal( username );
+            realm.addCredentialToPrincipal( user,
+                    new Credential( "password", password ) );
+        }
+        catch ( PrincipalAlreadyExistsException e )
+        {
+            m_monitor.duplicatePrincipal( e );
+        }
+    }
+
+    private void addGroups( MutableRealm realm, Element groups )
+    {
+        List groupsList = groups.elements( "group" );
+        for ( Iterator it = groupsList.iterator(); it.hasNext(); )
+        {
+            final Element element = (Element) it.next();
+            addGroupToRealm( realm, element );
+        }
+    }
+
+    private void addGroupToRealm( MutableRealm realm, final Element element )
+    {
+        String groupName = element.attributeValue( "name" );
+        try
+        {
+            realm.addGroup( groupName );
+        }
+        catch ( GroupAlreadyExistsException e )
+        {
+            m_monitor.duplicateGroup( e );
+        }
+    }
+
+    private void addMembers( MutableRealm realm, Element groups )
+    {
+        List groupsList = groups.elements( "group" );
+        for ( Iterator it = groupsList.iterator(); it.hasNext(); )
+        {
+            final Element element = (Element) it.next();
+            addSubGroupsOfGroup( realm, element );
+            addUsersToGroup( realm, element );
+        }
+    }
+
+    private void addSubGroupsOfGroup( MutableRealm realm,
+                                      final Element element )
+    {
+        String groupName = element.attributeValue( "name" );
+        Principal group = new GroupPrincipal( groupName );
+        List subGroups = element.elements( "group-ref" );
+        for ( Iterator it = subGroups.iterator(); it.hasNext(); )
+        {
+            final Element subElement = (Element) it.next();
+            addSubGroup( realm, group, subElement );
+        }
+    }
+
+    private void addSubGroup( MutableRealm realm,
+                              Principal group,
+                              final Element subElement )
+    {
+        String subGroupName = subElement.attributeValue( "name" );
+        Principal subGroup = new GroupPrincipal( subGroupName );
+
+        if ( subGroup.equals( group )
+             || (realm.getPrincipalsForGroup( subGroup ).contains( group )) )
+        {
+            m_monitor.cyclicDependency( group, subGroup );
+            return;
+        }
+
+        Set groupsInRealm = realm.getPrincipals( GroupPrincipal.class );
+        if ( !groupsInRealm.contains( subGroup ) )
+        {
+            m_monitor.invalidSubGroup( group, subGroup );
+            return;
+        }
+
+        boolean added = realm.addPrincipalToGroup( group, subGroup );
+        if ( !added )
+        {
+            m_monitor.duplicateSubGroup( group, subGroup );
+        }
+    }
+
+    private void addUsersToGroup( MutableRealm realm,
+                                  final Element element )
+    {
+        String groupName = element.attributeValue( "name" );
+        Principal group = new GroupPrincipal( groupName );
+        List subGroups = element.elements( "user-ref" );
+        for ( Iterator it = subGroups.iterator(); it.hasNext(); )
+        {
+            final Element subElement = (Element) it.next();
+            addMember( realm, group, subElement );
+        }
+    }
+
+    private void addMember( MutableRealm realm,
+                            Principal group,
+                            final Element subElement )
+    {
+        String username = subElement.attributeValue( "name" );
+        Principal user = new UsernamePrincipal( username );
+        Set usersInRealm = realm.getPrincipals( UsernamePrincipal.class );
+        if ( !usersInRealm.contains( user ) )
+        {
+            m_monitor.invalidMember( group, user );
+            return;
+        }
+
+        boolean added = realm.addPrincipalToGroup( group, user );
+        if ( !added )
+        {
+            m_monitor.duplicateMember( group, user );
+        }
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/NullRealmBuilderMonitor.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/NullRealmBuilderMonitor.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,53 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import java.security.Principal;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public final class NullRealmBuilderMonitor implements RealmBuilderMonitor
+{
+    public void duplicatePrincipal( PrincipalAlreadyExistsException e )
+    {
+    }
+
+    public void cyclicDependency( Principal group, Principal subGroup )
+    {
+    }
+
+    public void duplicateGroup( PrincipalAlreadyExistsException e )
+    {
+    }
+
+    public void invalidMember( Principal group, Principal invalid )
+    {
+    }
+
+    public void invalidSubGroup( Principal group, Principal invalid )
+    {
+    }
+
+    public void duplicateSubGroup( Principal group, Principal duplicate )
+    {
+    }
+
+    public void duplicateMember( Principal group, Principal duplicate )
+    {
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/PicoXMLRealm.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/PicoXMLRealm.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,31 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import org.xml.sax.InputSource;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class PicoXMLRealm extends DefaultXMLRealm
+{
+    public PicoXMLRealm( InputSource source ) throws Exception
+    {
+        setRealmSource( source );
+        initialize();
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/RealmBuilder.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/RealmBuilder.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,25 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public interface RealmBuilder
+{
+    void buildRealm( MutableRealm realm ) throws Exception;
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/RealmBuilderMonitor.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/java/org/apache/janus/authentication/realm/RealmBuilderMonitor.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,39 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import java.security.Principal;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public interface RealmBuilderMonitor
+{
+    void duplicatePrincipal( PrincipalAlreadyExistsException e );
+
+    void duplicateGroup( PrincipalAlreadyExistsException e );
+
+    void cyclicDependency( Principal group, Principal subGroup );
+
+    void invalidMember( Principal group, Principal invalid );
+
+    void invalidSubGroup( Principal group, Principal invalid );
+
+    void duplicateSubGroup( Principal group, Principal duplicate );
+
+    void duplicateMember( Principal group, Principal duplicate );
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/DefaultXMLRealmTest.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/DefaultXMLRealmTest.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,168 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import junit.framework.TestCase;
+import org.apache.avalon.framework.logger.ConsoleLogger;
+import org.apache.janus.authentication.Credential;
+import org.apache.janus.authentication.DefaultCredentialCollection;
+import org.xml.sax.InputSource;
+
+import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class DefaultXMLRealmTest extends TestCase
+{
+    private DefaultXMLRealm m_realm;
+
+    public static void main( String[] args )
+    {
+        junit.textui.TestRunner.run( DefaultXMLRealmTest.class );
+    }
+
+    protected void setUp() throws Exception
+    {
+        m_realm = new DefaultXMLRealm();
+    }
+
+    public void testRealmCredentialValidation() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "        <user username=\"jane\" password=\"doe\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups/>\n"
+                         + "</realm>";
+        m_realm.setRealmSource( new InputSource( new StringReader( content ) ) );
+        m_realm.initialize();
+
+        DefaultCredentialCollection johnCredentials = new DefaultCredentialCollection(
+                "username-password" );
+        johnCredentials.add( new Credential( "username", "john" ) );
+        johnCredentials.add( new Credential( "password", "doe" ) );
+        assertEquals( "John not correctly setup in the realm",
+                new UsernamePrincipal( "john" ),
+                m_realm.validateCredentials( johnCredentials ) );
+        DefaultCredentialCollection janeCredentials = new DefaultCredentialCollection(
+                "username-password" );
+        janeCredentials.add( new Credential( "username", "jane" ) );
+        janeCredentials.add( new Credential( "password", "doe" ) );
+        assertEquals( "Jane not correctly setup in the realm",
+                new UsernamePrincipal( "jane" ),
+                m_realm.validateCredentials( janeCredentials ) );
+    }
+
+    public void testRealmSubGroups() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users/>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"jazz\">\n"
+                         + "            <group-ref name=\"saxo\"/>\n"
+                         + "            <group-ref name=\"drums\"/>\n"
+                         + "            <group-ref name=\"trumpet\"/>\n"
+                         + "            <group-ref name=\"piano\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"saxo\"/>\n"
+                         + "        <group name=\"drums\"/>\n"
+                         + "        <group name=\"trumpet\"/>\n"
+                         + "        <group name=\"piano\"/>\n"
+                         + "    </groups>"
+                         + "</realm>";
+        m_realm.setRealmSource( new InputSource( new StringReader( content ) ) );
+        m_realm.initialize();
+
+        Set expected = new HashSet();
+        expected.add( new GroupPrincipal( "jazz" ) );
+        assertEquals( "Sub-group not in the right groups", expected,
+                m_realm.getGroupsForPrincipal( new GroupPrincipal( "saxo" ) ) );
+    }
+
+    public void testRealmGroupMembership() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "        <user username=\"jane\" password=\"doe\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"jazz\">\n"
+                         + "            <group-ref name=\"saxo\"/>\n"
+                         + "            <group-ref name=\"drums\"/>\n"
+                         + "            <group-ref name=\"trumpet\"/>\n"
+                         + "            <group-ref name=\"piano\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"saxo\">\n"
+                         + "            <user-ref name=\"john\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"drums\"/>\n"
+                         + "        <group name=\"trumpet\"/>\n"
+                         + "        <group name=\"piano\"/>\n"
+                         + "    </groups>"
+                         + "</realm>";
+        m_realm.setRealmSource( new InputSource( new StringReader( content ) ) );
+        m_realm.initialize();
+
+        Set expected = new HashSet();
+        expected.add( new GroupPrincipal( "saxo" ) );
+        expected.add( new GroupPrincipal( "jazz" ) );
+        assertEquals( "User not in the right groups", expected,
+                m_realm.getGroupsForPrincipal( new UsernamePrincipal( "john" ) ) );
+    }
+
+    public void testMonitoring() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"jazz\">\n"
+                         + "            <group-ref name=\"saxo\"/>\n"
+                         + "            <group-ref name=\"saxo\"/>\n"
+                         + "            <group-ref name=\"drums\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"saxo\">\n"
+                         + "            <group-ref name=\"saxo\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"drums\">\n"
+                         + "            <group-ref name=\"jazz\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"trumpet\">\n"
+                         + "            <user-ref name=\"john\"/>\n"
+                         + "            <user-ref name=\"john\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"piano\">\n"
+                         + "            <user-ref name=\"jane\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+        m_realm.setRealmSource( new InputSource( new StringReader( content ) ) );
+        m_realm.setMonitor( new AvalonLoggerAdapter( new ConsoleLogger() ) );
+        m_realm.initialize();
+    }
+}

Added: incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/Dom4JRealmBuilderTest.java
==============================================================================
--- (empty file)
+++ incubator/directory/janus/trunk/authentication/xml/src/test/org/apache/janus/authentication/realm/Dom4JRealmBuilderTest.java	Sun Feb 15 22:14:14 2004
@@ -0,0 +1,344 @@
+/*
+ *   Copyright 2004 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 org.apache.janus.authentication.realm;
+
+import com.mockobjects.dynamic.C;
+import com.mockobjects.dynamic.Mock;
+import junit.framework.TestCase;
+import org.apache.janus.authentication.Credential;
+import org.apache.janus.authentication.DefaultCredentialCollection;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.io.SAXReader;
+
+import java.io.StringReader;
+
+/**
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ */
+public class Dom4JRealmBuilderTest extends TestCase
+{
+    public static void main( String[] args )
+    {
+        junit.textui.TestRunner.run( Dom4JRealmBuilderTest.class );
+    }
+
+    public void testPopulatesRealmWithPrincipals() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "        <user username=\"jane\" password=\"doe\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "    </groups>"
+                         + "</realm>";
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ) );
+
+        MutableRealm realm = new DefaultRealm();
+        builder.buildRealm( realm );
+
+        assertTrue( "First principal not added to realm",
+                realm.getPrincipals( UsernamePrincipal.class ).contains(
+                        new UsernamePrincipal( "john" ) ) );
+        assertTrue( "Subsequent principal not added to realm",
+                realm.getPrincipals( UsernamePrincipal.class ).contains(
+                        new UsernamePrincipal( "jane" ) ) );
+
+        DefaultCredentialCollection c = new DefaultCredentialCollection(
+                "username-password" );
+        c.add( new Credential( "username", "john" ) );
+        c.add( new Credential( "password", "doe" ) );
+        assertNotNull( "First principal password incorrectly set",
+                realm.validateCredentials( c ) );
+
+        c = new DefaultCredentialCollection( "username-password" );
+        c.add( new Credential( "username", "jane" ) );
+        c.add( new Credential( "password", "doe" ) );
+        assertNotNull( "Subsequent principal password incorrectly set",
+                realm.validateCredentials( c ) );
+    }
+
+    public void testNotifiesOfDuplicatePrincipals() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "    </groups>"
+                         + "</realm>";
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "duplicatePrincipal", C.ANY_ARGS );
+        builder.buildRealm( new DefaultRealm() );
+        mockMonitor.verify();
+    }
+
+    public void testPopulatesRealmWithGroups() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\"/>\n"
+                         + "        <group name=\"women\"/>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ) );
+
+        MutableRealm realm = new DefaultRealm();
+        builder.buildRealm( realm );
+
+        assertTrue( "First group not added to realm",
+                realm.getPrincipals( GroupPrincipal.class ).contains(
+                        new GroupPrincipal( "men" ) ) );
+        assertTrue( "Subsequent group not added to realm",
+                realm.getPrincipals( GroupPrincipal.class ).contains(
+                        new GroupPrincipal( "women" ) ) );
+    }
+
+    public void testNotifiesOfDuplicateGroups() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\"/>\n"
+                         + "        <group name=\"men\"/>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "duplicateGroup", C.ANY_ARGS );
+        builder.buildRealm( new DefaultRealm() );
+        mockMonitor.verify();
+    }
+
+    public void testAddsSubGroupsToGroups() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"geek\"/>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <group-ref name=\"geek\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ) );
+
+        MutableRealm realm = new DefaultRealm();
+        builder.buildRealm( realm );
+        assertTrue( "Sub-group not added",
+                realm.getPrincipalsForGroup( new GroupPrincipal( "men" ) )
+                .contains( new GroupPrincipal( "geek" ) ) );
+    }
+
+    public void testAddsUsersToGroups() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"bone\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <user-ref name=\"john\"/>"
+                         + "        </group>"
+                         + "    </groups>"
+                         + "</realm>";
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ) );
+
+        MutableRealm realm = new DefaultRealm();
+        builder.buildRealm( realm );
+        assertTrue( "User not added",
+                realm.getPrincipalsForGroup( new GroupPrincipal( "men" ) )
+                .contains( new UsernamePrincipal( "john" ) ) );
+    }
+
+    public void testNotifiesOfUnknownSubGroupReferences() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <group-ref name=\"geek\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "invalidSubGroup",
+                C.args( C.eq( new GroupPrincipal( "men" ) ),
+                        C.eq( new GroupPrincipal( "geek" ) ) ) );
+        builder.buildRealm( new DefaultRealm() );
+        mockMonitor.verify();
+    }
+
+    public void testNotifiesOfDuplicateSubGroupReferences() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"geek\"/>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <group-ref name=\"geek\"/>\n"
+                         + "            <group-ref name=\"geek\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "duplicateSubGroup",
+                C.args( C.eq( new GroupPrincipal( "men" ) ),
+                        C.eq( new GroupPrincipal( "geek" ) ) ) );
+        builder.buildRealm( new DefaultRealm() );
+        mockMonitor.verify();
+    }
+
+    public void testNotifiesOfUnknownMemberReferences() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <user-ref name=\"john\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "invalidMember",
+                C.args( C.eq( new GroupPrincipal( "men" ) ),
+                        C.eq( new UsernamePrincipal( "john" ) ) ) );
+        builder.buildRealm( new DefaultRealm() );
+        mockMonitor.verify();
+    }
+
+    public void testNotifiesOfDuplicateMemberReferences() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "        <user username=\"john\" password=\"doe\"/>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <user-ref name=\"john\"/>\n"
+                         + "            <user-ref name=\"john\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "duplicateMember",
+                C.args( C.eq( new GroupPrincipal( "men" ) ),
+                        C.eq( new UsernamePrincipal( "john" ) ) ) );
+        builder.buildRealm( new DefaultRealm() );
+        mockMonitor.verify();
+    }
+
+    public void testNotifiesOfCyclicDependencies() throws Exception
+    {
+        String content = "<?xml version=\"1.0\"?>\n"
+                         + "<realm>\n"
+                         + "    <users>\n"
+                         + "    </users>\n"
+                         + "    <groups>\n"
+                         + "        <group name=\"men\">\n"
+                         + "            <group-ref name=\"geek\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"geek\">\n"
+                         + "            <group-ref name=\"men\"/>\n"
+                         + "        </group>\n"
+                         + "        <group name=\"women\">\n"
+                         + "            <group-ref name=\"women\"/>\n"
+                         + "        </group>\n"
+                         + "    </groups>"
+                         + "</realm>";
+
+        Mock mockMonitor = new Mock( RealmBuilderMonitor.class );
+        Dom4JRealmBuilder builder = new Dom4JRealmBuilder(
+                getDocument( content ),
+                (RealmBuilderMonitor) mockMonitor.proxy() );
+
+        mockMonitor.expect( "cyclicDependency",
+                C.args( C.eq( new GroupPrincipal( "geek" ) ),
+                        C.eq( new GroupPrincipal( "men" ) ) ) );
+        mockMonitor.expect( "cyclicDependency",
+                C.args( C.eq( new GroupPrincipal( "women" ) ),
+                        C.eq( new GroupPrincipal( "women" ) ) ) );
+
+        try
+        {
+            builder.buildRealm( new DefaultRealm() );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            fail( "Cyclic dependency not detected" );
+        }
+
+        mockMonitor.verify();
+    }
+
+    private static Document getDocument( String xml ) throws DocumentException
+    {
+        return new SAXReader().read( new StringReader( xml ) );
+    }
+}