You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tb...@apache.org on 2006/12/12 16:24:14 UTC

svn commit: r486187 [24/49] - in /directory/trunks/triplesec: ./ admin-api/ admin-api/src/ admin-api/src/main/ admin-api/src/main/java/ admin-api/src/main/java/org/ admin-api/src/main/java/org/safehaus/ admin-api/src/main/java/org/safehaus/triplesec/ a...

Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/PolicyProtectionInterceptor.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/PolicyProtectionInterceptor.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/PolicyProtectionInterceptor.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/PolicyProtectionInterceptor.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,774 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.safehaus.triplesec.store.interceptor;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParserImpl;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.server.core.configuration.InterceptorConfiguration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.DirectoryServiceConfiguration;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+
+
+/**
+ * An ApacheDS {@link Interceptor} that prevents users from deleting
+ * (or renaming) policy entries (permissions and roles) used by roles and profiles.  
+ *
+ * @author Trustin Lee
+ * @version $Rev: 956 $, $Date: 2006-09-21 10:10:21 -0400 (Thu, 21 Sep 2006) $
+ */
+public class PolicyProtectionInterceptor extends BaseInterceptor
+{
+    private DirectoryServiceConfiguration factoryConfiguration;
+    private ApplicationAciManager aciManager = null;
+    
+
+    public PolicyProtectionInterceptor()
+    {
+    }
+
+    
+    public void init( DirectoryServiceConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
+    {
+        factoryConfiguration = factoryCfg;
+        aciManager = new ApplicationAciManager( factoryCfg.getGlobalRegistries().getAttributeTypeRegistry() );
+    }
+
+    
+    public void add( NextInterceptor next, LdapDN name, Attributes attrs ) throws NamingException
+    {
+        boolean policyEntry = false;
+        boolean isApplication = false;
+
+        Attribute attr = getObjectClass( attrs );
+        NamingEnumeration ocList = attr.getAll();
+        try
+        {
+            while( ocList.hasMore() )
+            {
+                String value = String.valueOf( ocList.next() );
+                if( "policyPermission".equalsIgnoreCase( value ) )
+                {
+                    checkNewPolicyEntry( next, name, "2.5.4.11=permissions" );
+                    policyEntry = true;
+                }
+                else if( "policyRole".equalsIgnoreCase( value ) )
+                {
+                    checkNewPolicyEntry( next, name, "2.5.4.11=roles" );
+                    policyEntry = true;
+                }
+                else if( "policyProfile".equalsIgnoreCase( value ) )
+                {
+                    checkNewPolicyEntry( next, name, "2.5.4.11=profiles" );
+                    policyEntry = true;
+                }
+                else if( "policyApplication".equalsIgnoreCase( value ) )
+                {
+                    isApplication = true;
+                }
+            }
+        }
+        finally
+        {
+            ocList.close();
+        }
+
+        if( !policyEntry )
+        {
+            checkNewNonPolicyEntry( next, name );
+        }
+        else
+        {
+            // Check if all grants, denials, and roles are valid.
+            LdapDN baseName = ( LdapDN ) name.clone();
+            baseName.remove( baseName.size() -1 );
+            baseName.remove( baseName.size() -1 );
+            NamingEnumeration attrList = attrs.getAll();
+            try
+            {
+                while( attrList.hasMore() )
+                {
+                    attr = ( Attribute ) attrList.next();
+                    checkAttributeAddition( next, baseName, attr );
+                }
+            }
+            finally
+            {
+                ocList.close();
+            }
+        }
+
+        next.add( name, attrs );
+
+        if( isApplication )
+        {
+            aciManager.appAdded( name );
+        }
+    }
+
+    
+    public void delete( NextInterceptor next, LdapDN name ) throws NamingException
+    {
+        boolean isApplication = isPolicyApplication( name );
+
+        LdapDN baseName = getBaseName( next, name );
+        if( baseName == null )
+        {
+            next.delete( name );
+
+            if ( isApplication )
+            {
+                aciManager.appRemoved( name );
+            }
+            return;
+        }
+
+        checkNotInUse( next, baseName, name );
+        next.delete( name );
+        if ( isApplication )
+        {
+            aciManager.appRemoved( name );
+        }
+    }
+
+    
+    public void modify( NextInterceptor next, LdapDN name, int modOp, Attributes attrs ) throws NamingException
+    {
+        LdapDN baseName = getBaseName( next, name );
+        if( baseName == null )
+        {
+            next.modify( name, modOp, attrs );
+            return;
+        }
+
+        NamingEnumeration e = attrs.getAll();
+        try
+        {
+            while( e.hasMore() )
+            {
+                Attribute attr = ( Attribute ) e.next();
+                if( attr != null )
+                {
+                    switch( modOp )
+                    {
+                    case DirContext.ADD_ATTRIBUTE:
+                    case DirContext.REPLACE_ATTRIBUTE:
+                        checkAttributeAddition( next, baseName, attr );
+                        break;
+                    case DirContext.REMOVE_ATTRIBUTE:
+                        checkAttributeRemoval( attr );
+                        break;
+                    }
+                }
+            }
+        }
+        finally
+        {
+            e.close();
+        }
+
+        next.modify( name, modOp, attrs );
+    }
+
+    
+    public void modify( NextInterceptor next, LdapDN name, ModificationItem[] modItems ) throws NamingException
+    {
+        LdapDN baseName = getBaseName( next, name );
+        if( baseName == null )
+        {
+            next.modify( name, modItems );
+            return;
+        }
+
+        for( int i = modItems.length - 1; i >= 0; i-- )
+        {
+            Attribute attr = modItems[ i ].getAttribute();
+            switch( modItems[ i ].getModificationOp() ) {
+            case DirContext.ADD_ATTRIBUTE:
+            case DirContext.REPLACE_ATTRIBUTE:
+                checkAttributeAddition( next, baseName, attr );
+                break;
+            case DirContext.REMOVE_ATTRIBUTE:
+                checkAttributeRemoval( attr );
+                break;
+            }
+        }
+
+        next.modify( name, modItems );
+    }
+
+    
+    public void modifyRn( NextInterceptor next, LdapDN name, String newRN, boolean deleteOldRN ) throws NamingException
+    {
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        Attributes entry = proxy.lookup( name, ApplicationAciManager.LOOKUP_BYPASS );
+        Attribute oc = entry.get( "objectClass" );
+        boolean isApplication = false;
+
+        for ( int ii = 0; ii < oc.size(); ii++ )
+        {
+            String item = ( String ) oc.get( ii );
+            if ( item.equalsIgnoreCase( "policyApplication" ) )
+            {
+                isApplication = true;
+            }
+        }
+
+        // calculate the new name
+        LdapDN newNameUpDn = ( LdapDN ) name.clone();
+        newNameUpDn.remove( name.size() - 1 );
+        newNameUpDn.add( newRN );
+        LdapDN rdn = new LdapDN( newRN );
+        LdapDN newDn = ( LdapDN ) name.clone();
+        newDn.remove( name.size() - 1 );
+        newDn.add( rdn.get( 0 ) );
+
+        LdapDN baseName = getBaseName( next, name );
+        if( baseName == null )
+        {
+            if ( isApplication )
+            {
+                // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+                aciManager.removeApplicationSubentry( proxy, name );
+            }
+            next.modifyRn( name, newRN, deleteOldRN );
+            if ( isApplication )
+            {
+                aciManager.addApplicationSubentry( proxy, newDn );
+            }
+
+            return;
+        }
+
+        checkModification( next, baseName, name );
+
+        if ( isApplication )
+        {
+            // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+            aciManager.removeApplicationSubentry( proxy, name );
+        }
+        next.modifyRn( name, newRN, deleteOldRN );
+        if ( isApplication )
+        {
+            aciManager.addApplicationSubentry( proxy, newDn );
+        }
+    }
+
+    public void move( NextInterceptor next, LdapDN name, LdapDN newParentName, String newRN, boolean deleteOldRN ) throws NamingException
+    {
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        Attributes entry = proxy.lookup( name, ApplicationAciManager.LOOKUP_BYPASS );
+        Attribute oc = entry.get( "objectClass" );
+        boolean isApplication = false;
+
+        for ( int ii = 0; ii < oc.size(); ii++ )
+        {
+            String item = ( String ) oc.get( ii );
+            if ( item.equalsIgnoreCase( "policyApplication" ) )
+            {
+                isApplication = true;
+            }
+        }
+
+        // calculate the new name
+        LdapDN newNameUpDn = ( LdapDN ) newParentName.clone();
+        newNameUpDn.add( newRN );
+        LdapDN rdn = new LdapDN( newRN );
+        LdapDN newDn = ( LdapDN ) newParentName.clone();
+        newDn.add( rdn.get( 0 ) );
+
+        LdapDN baseName = getBaseName( next, name );
+        if( baseName == null )
+        {
+            if ( isApplication )
+            {
+                // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+                aciManager.removeApplicationSubentry( proxy, name );
+            }
+            next.move( name, newParentName, newRN, deleteOldRN );
+            if ( isApplication )
+            {
+                aciManager.addApplicationSubentry( proxy, newDn );
+            }
+            return;
+        }
+
+        checkModification( next, baseName, name );
+        if ( isApplication )
+        {
+            // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+            aciManager.removeApplicationSubentry( proxy, name );
+        }
+        next.move( name, newParentName, newRN, deleteOldRN );
+        if ( isApplication )
+        {
+            aciManager.addApplicationSubentry( proxy, newDn );
+        }
+    }
+
+    public void move( NextInterceptor next, LdapDN name, LdapDN newParentName ) throws NamingException
+    {
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        Attributes entry = proxy.lookup( name, ApplicationAciManager.LOOKUP_BYPASS );
+        Attribute oc = entry.get( "objectClass" );
+        boolean isApplication = false;
+
+        for ( int ii = 0; ii < oc.size(); ii++ )
+        {
+            String item = ( String ) oc.get( ii );
+            if ( item.equalsIgnoreCase( "policyApplication" ) )
+            {
+                isApplication = true;
+            }
+        }
+
+        // calculate the new name
+        LdapDN newDn = ( LdapDN ) newParentName.clone();
+        newDn.add( name.get( name.size() - 1 ) );
+
+        LdapDN baseName = getBaseName( next, name );
+        if( baseName == null )
+        {
+            if ( isApplication )
+            {
+                // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+                aciManager.removeApplicationSubentry( proxy, name );
+            }
+            next.move( name, newParentName );
+            if ( isApplication )
+            {
+                aciManager.addApplicationSubentry( proxy, newDn );
+            }
+            return;
+        }
+
+        checkModification( next, baseName, name );
+        if ( isApplication )
+        {
+            // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+            aciManager.removeApplicationSubentry( proxy, name );
+        }
+        next.move( name, newParentName );
+        if ( isApplication )
+        {
+            aciManager.addApplicationSubentry( proxy, newDn );
+        }
+    }
+
+    private LdapDN getBaseName( NextInterceptor next, LdapDN name ) throws NamingException
+    {
+        if( name.size() >= 3 )
+        {
+            Attributes attrs = next.lookup( name );
+            Attribute attr = getObjectClass( attrs );
+            NamingEnumeration e = attr.getAll();
+            try
+            {
+                while( e.hasMore() )
+                {
+                    String value = String.valueOf( e.next() );
+                    if( "policyPermission".equalsIgnoreCase( value ) ||
+                            "policyProfile".equalsIgnoreCase( value ) ||
+                            "policyRole".equalsIgnoreCase( value ) ) 
+                    {
+                        LdapDN retVal = ( LdapDN ) name.clone();
+                        retVal.remove( retVal.size() -1 );
+                        retVal.remove( retVal.size() -1 );
+                        
+                        return retVal;
+                    }
+                    if( "policyApplication".equalsIgnoreCase( value ) )
+                    {
+                        return name;
+                    }
+                }
+            }
+            finally
+            {
+                e.close();
+            }
+        }
+
+        if( name.size() >= 2 )
+        {
+            // Try the parent node in case we add group entries.
+            try
+            {
+                name = ( LdapDN ) name.clone();
+                name.remove( name.size() - 1 );
+                Attributes attrs = next.lookup( name );
+                Attribute attr = getObjectClass( attrs );
+                NamingEnumeration e = attr.getAll();
+                try
+                {
+                    while( e.hasMore() )
+                    {
+                        String value = String.valueOf( e.next() );
+                        if( "policyApplication".equalsIgnoreCase( value ) )
+                        {
+                            return name;
+                        }
+                    }
+                }
+                finally
+                {
+                    e.close();
+                }
+            }
+            catch( NamingException e )
+            {
+                // Ignore silently
+            }
+        }
+
+        return null;
+    }
+    
+
+    /**
+     * Checks to see if a policy entry should be added under a parent.
+     * 
+     * @param next
+     * @param name
+     * @param parentName
+     * @throws NamingException
+     */
+    private void checkNewPolicyEntry( NextInterceptor next, LdapDN name, String parentName ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() -1 );
+
+        if( name.size() < 3 )
+        {
+            throw new SchemaViolationException( "Name is too short: " + name );
+        }
+
+        
+        if( !parentName.equalsIgnoreCase( parentDn.getRdn().toString() ) )
+        {
+            throw new SchemaViolationException( "Parent entry for policyPermissions must be '" +
+                parentName + "': " + name );
+        }
+
+        parentDn.remove( parentDn.size() -1 );
+        if( !isPolicyApplication( parentDn ) )
+        {
+            throw new SchemaViolationException( "Grandparent must be a policyApplication." );
+        }
+    }
+    
+    
+    private static final String[] OBJECT_CLASS_ATTRS = { "objectClass" };
+    private boolean isPolicyApplication( LdapDN dn ) throws NamingException
+    {
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        Attributes entry = proxy.lookup( dn, OBJECT_CLASS_ATTRS, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attribute oc = getObjectClass( entry );
+
+        NamingEnumeration list = oc.getAll();
+        try
+        {
+            while( list.hasMore() )
+            {
+                if( "policyApplication".equalsIgnoreCase( String.valueOf( list.next() ) ) )
+                {
+                    return true;
+                }
+            }
+        }
+        finally {
+            list.close();
+        }
+        
+        return false;
+    }
+
+    
+    private void checkNewNonPolicyEntry( NextInterceptor next, LdapDN name ) throws NamingException
+    {
+        if( name.size() < 3 )
+        {
+            return;
+        }
+
+        if( isEntityGroup( name ) )
+        {
+            // Strip the name once more; this will prevent this method
+            // from throwing an exception when these entries are added
+            // just beneath policyApplication entry.
+            name = ( LdapDN ) name.getPrefix( 1 );
+        }
+
+        name = ( LdapDN ) name.clone();
+        do
+        {
+            name.remove( name.size() - 1 );
+            try
+            {
+                Attributes entry = next.lookup( name );
+                Attribute attr = getObjectClass( entry );
+                NamingEnumeration e = attr.getAll();
+                try
+                {
+                    while( e.hasMore() )
+                    {
+                        if( "policyApplication".equalsIgnoreCase( String.valueOf( e.next() ) ) )
+                        {
+                            throw new SchemaViolationException( "Non-policy entries cannot reside under policyApplication." );
+                        }
+                    }
+                }
+                finally
+                {
+                    e.close();
+                }
+            }
+            catch( SchemaViolationException e )
+            {
+                throw e;
+            }
+            catch( Exception e )
+            {
+                // Ignore silently.
+            }
+        }
+        while( name.size() > 1 );
+    }
+    
+
+    private void checkAttributeAddition( NextInterceptor next, LdapDN baseName, Attribute attr ) 
+        throws NamingException, SchemaViolationException
+    {
+        
+        // If the attribute is a permission
+        if( "grants".equalsIgnoreCase( attr.getID() ) ||
+            "denials".equalsIgnoreCase( attr.getID() ) )
+        {
+            NamingEnumeration e = attr.getAll();
+            try
+            {
+                while( e.hasMore() )
+                {
+                    String value = String.valueOf( e.next() );
+                    LdapDN name = ( LdapDN ) baseName.clone();
+                    
+                    // ou=permissions
+                    name.add( "2.5.4.11=permissions" );
+                    // permName=
+                    name.add( "1.2.6.1.4.1.22555.1.1.1.3.201=" + value );
+                    if( !next.hasEntry( name ) ) {
+                        throw new SchemaViolationException( "No such permission: " + value );
+                    }
+                }
+            }
+            finally {
+                e.close();
+            }
+        }
+        
+        // If the attribute is a role
+        if( "roles".equalsIgnoreCase( attr.getID() ) ) {
+            NamingEnumeration e = attr.getAll();
+            try
+            {
+                while( e.hasMore() )
+                {
+                    String value = String.valueOf( e.next() );
+                    LdapDN name = ( LdapDN ) baseName.clone();
+                    
+                    // ou = roles
+                    name.add( "2.5.4.11=roles" );
+                    
+                    // roleName=
+                    name.add( "1.2.6.1.4.1.22555.1.1.1.3.204=" + value );
+                    if( !next.hasEntry( name ) ) {
+                        throw new SchemaViolationException( "No such role: " + value );
+                    }
+                }
+            }
+            finally {
+                e.close();
+            }
+        }
+    }
+
+    
+    private void checkAttributeRemoval( Attribute attr ) throws NamingException, SchemaViolationException
+    {
+        if( !"objectClass".equalsIgnoreCase( attr.getID() ) )
+        {
+            return;
+        }
+
+        NamingEnumeration e = attr.getAll();
+        try
+        {
+            while( e.hasMore() )
+            {
+                String value = String.valueOf( e.next() );
+                if( "policyPermission".equalsIgnoreCase( value ) ||
+                        "policyRole".equalsIgnoreCase( value ) ||
+                        "policyProfile".equalsIgnoreCase( value ) )
+                {
+                    throw new SchemaViolationException( "Removing policy objectClasses are not allowed." );
+                }
+            }
+        }
+        finally
+        {
+            e.close();
+        }
+    }
+    
+    
+    private static final String PERMNAME_ATTR_OID = "1.2.6.1.4.1.22555.1.1.1.3.201";
+    private static final String ROLENAME_ATTR_OID = "1.2.6.1.4.1.22555.1.1.1.3.204";
+    
+    private void checkNotInUse( NextInterceptor next, LdapDN baseName, LdapDN name ) 
+        throws NamingException, SchemaViolationException
+    {
+        String nameType = NamespaceTools.getRdnAttribute( name.get( name.size() - 1 ) );
+        String nameValue = NamespaceTools.getRdnValue( name.get( name.size() - 1 ) );
+
+        // Prepare a search control.
+        SearchControls ctrl = new SearchControls();
+        ctrl.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        ctrl.setReturningAttributes( new String[]
+        {
+                "roles", "grants", "denials"
+        } );
+
+        // Get an appropriate filter.
+        ExprNode filter = null;
+        if( PERMNAME_ATTR_OID.equals( nameType ) )
+        {
+            try
+            {
+                filter = new FilterParserImpl().parse(
+                        "(|" +
+                        "(grants=" + nameValue + ")" +
+                        "(denials=" + nameValue + ")" +
+                        ")" );
+            }
+            catch( Exception e )
+            {
+                throw ( NamingException ) new NamingException().initCause( e );
+            }
+        }
+        else if( ROLENAME_ATTR_OID.equals( nameType ) )
+        {
+            try
+            {
+                filter = new FilterParserImpl().parse(
+                        "(roles=" + nameValue + ")" );
+            }
+            catch( Exception e )
+            {
+                throw ( NamingException ) new NamingException().initCause( e );
+            }
+        }
+
+        // If it is able to find an appropriate filter, 
+        if( filter != null )
+        {
+            // execute search
+            NamingEnumeration e = next.search( baseName, factoryConfiguration.getEnvironment(),
+                    filter, ctrl );
+
+            // throw an exception if search returned more than 0 usage.
+            try
+            {
+                if( e.hasMore() )
+                {
+                    throw new SchemaViolationException( "Policy entry in use: " + name );
+                }
+            }
+            finally
+            {
+                e.close();
+            }
+        }
+    }
+
+    
+    private void checkModification( NextInterceptor next, LdapDN baseName, LdapDN name ) 
+        throws SchemaViolationException, NamingException
+    {
+        if( isEntityGroup( name ) )
+        {
+            throw new SchemaViolationException( "Entity groups are not allowed to move: " + name );
+        }
+        checkNotInUse( next, baseName, name );
+    }
+
+
+    private static final String OU_ATTR_OID = "2.5.4.11";
+    private static boolean isEntityGroup( LdapDN name )
+    {
+        String rn = name.get( name.size() - 1 );
+        if( ! OU_ATTR_OID.equalsIgnoreCase( NamespaceTools.getRdnAttribute( rn ).trim() ) )
+        {
+            return false;
+        }
+
+        rn = NamespaceTools.getRdnValue( rn ).trim();
+
+        return ( rn.equalsIgnoreCase( "permissions" ) ||
+                 rn.equalsIgnoreCase( "roles" ) ||
+                 rn.equalsIgnoreCase( "profiles" ) );
+    }
+
+    
+    private static Attribute getObjectClass( Attributes attrs ) throws NamingException
+    {
+        NamingEnumeration e = attrs.getAll();
+        try
+        {
+            while( e.hasMore() )
+            {
+                Attribute attr = ( Attribute ) e.next();
+                if( "objectClass".equalsIgnoreCase( attr.getID() ) )
+                {
+                    return attr;
+                }
+            }
+        }
+        finally
+        {
+            e.close();
+        }
+
+        return null;
+    }
+}

Added: directory/trunks/triplesec/store/src/main/schema/safehaus.schema
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/schema/safehaus.schema?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/schema/safehaus.schema (added)
+++ directory/trunks/triplesec/store/src/main/schema/safehaus.schema Tue Dec 12 07:23:31 2006
@@ -0,0 +1,210 @@
+# -----------------------------------------------------------------------------
+#                             Safehaus LDAP Schema
+# -----------------------------------------------------------------------------
+#
+# Author: $author$
+# Author: Alex Karasulu 
+# Revision: $rev$
+# Nag-to: dev@safehaus.org
+# Dependencies: system, core
+#
+# -----------------------------------------------------------------------------
+
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.100
+        NAME 'safehausUid'
+        DESC 'A safehaus user id'
+        SUP uid SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.101
+        NAME 'safehausRealm'
+        DESC 'The domain/realm name of the safehaus user account'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.102
+        NAME 'safehausSecret'
+        DESC 'The shared secret for the Safehaus user'
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.103
+        NAME 'safehausFactor'
+        DESC 'The shared moving factor for the Safehaus user'
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.104
+        NAME 'safehausInfo'
+        DESC 'Additional information about a Safehaus account'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.105
+        NAME 'safehausLabel'
+        DESC 'Additional information about a Safehaus account'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.106
+        NAME 'safehausResynchCount'
+        DESC 'The number of successful resync attempts so far'
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.107
+        NAME 'safehausFailuresInEpoch'
+        DESC 'The number of authentication failures within an epoch used for lockouts'
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.108
+        NAME 'safehausDeleted'
+        DESC 'An operational marker attribute for labelling an entry as deleted'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 
+        SINGLE-VALUE 
+        NO-USER-MODIFICATION
+        USAGE directoryOperation )        
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.109
+        NAME 'safehausActivationKey'
+        DESC 'Activation key for safehaus accounts which if present means the account is not active'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.110
+        NAME 'safehausMidletName'
+        DESC 'Optional name override for the hauskeys midlet'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.111
+        NAME 'safehausNotifyBy'
+        DESC 'Mechanism used to notify the user'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.112
+        NAME 'safehausMobileCarrier'
+        DESC 'The mobile carrier for the cell phone of the user'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.113
+        NAME 'safehausTokenPin'
+        DESC 'The mobile token pin for the hauskeys application'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.114
+        NAME 'safehausDisabled'
+        DESC 'Used to disable user accounts and policyProfiles'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+objectclass ( 1.2.6.1.4.1.22555.1.1.1.4.100 NAME 'safehausProfile'
+    SUP top
+    AUXILIARY
+    MUST (  safehausUid $ safehausRealm $ safehausLabel $ safehausSecret $
+            safehausFactor $ safehausResynchCount $ safehausFailuresInEpoch $ safehausNotifyBy )
+    MAY  ( safehausInfo $ safehausActivationKey $ safehausMidletName $ safehausDisabled ) )
+
+
+# -----------------------------------------------------------------------------
+# Safehaus Policy Entities
+# -----------------------------------------------------------------------------
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.200
+        NAME 'appName'
+        DESC 'the name of an application whose policy is managed by triplesec'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+objectclass ( 1.2.6.1.4.1.22555.1.1.1.4.200 NAME 'policyApplication'
+    SUP top
+    STRUCTURAL
+    MUST ( appName )
+    MAY  ( userPassword $ description ) )
+
+objectclass ( 1.2.6.1.4.1.22555.1.1.1.4.201 NAME 'policyUser'
+    SUP top
+    AUXILIARY
+    MUST ( uid )
+    MAY  ( userPassword $ description $ safehausDisabled ) )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.201
+        NAME 'permName'
+        DESC 'the case sensitive name of a permission within the system'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+objectclass ( 1.2.6.1.4.1.22555.1.1.1.4.202 NAME 'policyPermission'
+    SUP top
+    AUXILIARY
+    MUST ( permName )
+    MAY ( description )
+    )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.202
+        NAME 'grants'
+        DESC 'the permissions granted to a role or a profile'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.203
+        NAME 'denials'
+        DESC 'the permissions denied for a profile'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.204
+        NAME 'roleName'
+        DESC 'the name of a role'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.205
+        NAME 'roles'
+        DESC 'the roles assigned to a profile'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.206
+        NAME 'profileId'
+        DESC 'a profile identifier'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.2.6.1.4.1.22555.1.1.1.3.207
+        NAME 'user'
+        DESC 'the name of a user defined in the policy store'
+        EQUALITY caseExactMatch
+        SUBSTR caseExactSubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+objectclass ( 1.2.6.1.4.1.22555.1.1.1.4.203 NAME 'policyRole'
+    SUP top
+    AUXILIARY
+    MUST ( roleName )
+    MAY  ( grants $ description ) )
+
+objectclass ( 1.2.6.1.4.1.22555.1.1.1.4.204 NAME 'policyProfile'
+    SUP top
+    AUXILIARY
+    MUST ( profileId $ user )
+    MAY  ( grants $ denials $ roles $ userPassword $ description $ safehausDisabled ) )
+

Added: directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ProfileFactoryITest.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ProfileFactoryITest.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ProfileFactoryITest.java (added)
+++ directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ProfileFactoryITest.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.safehaus.triplesec.store;
+
+
+import javax.naming.directory.Attributes;
+import javax.naming.NamingException;
+import javax.naming.Context;
+
+import org.apache.directory.server.core.unit.AbstractAdminTestCase;
+import org.apache.directory.server.core.schema.bootstrap.*;
+import org.apache.directory.server.core.configuration.MutableStartupConfiguration;
+
+import org.apache.directory.shared.ldap.ldif.Entry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.safehaus.profile.ServerProfile;
+import org.safehaus.profile.BaseServerProfileModifier;
+import org.safehaus.triplesec.store.schema.SafehausSchema;
+
+import java.util.Set;
+import java.util.HashSet;
+
+
+/**
+ * Tests the ProfileObjectFactory and ProfileStateFactory classes.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ProfileFactoryITest extends AbstractAdminTestCase
+{
+    public ProfileFactoryITest()
+    {
+        MutableStartupConfiguration cfg = super.configuration;
+        Set schemas = new HashSet();
+        schemas.add( new SystemSchema() );
+        schemas.add( new SafehausSchema() );
+        schemas.add( new ApacheSchema() );
+        schemas.add( new CoreSchema() );
+        schemas.add( new CosineSchema() );
+        schemas.add( new InetorgpersonSchema() );
+        schemas.add( new Krb5kdcSchema() );
+        cfg.setBootstrapSchemas( schemas );
+        cfg.setShutdownHookEnabled( false );
+        super.overrideEnvironment( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+        super.overrideEnvironment( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+    }
+
+
+    public void testObjectFactory() throws Exception
+    {
+        String ldif = "dn: cn=bogus\n"
+            + "cn: Alex Karasulu\n" + "sn: Karasulu\n" + "givenname: Alex\n" + "objectClass: top\n"
+            + "objectClass: person\n" + "objectClass: organizationalPerson\n" + "objectClass: inetOrgPerson\n"
+            + "objectClass: krb5Principal\n" + "objectClass: krb5KDCEntry\n" + "objectClass: safehausProfile\n"
+            + "ou: Directory\n" + "ou: Users\n" + "l: Jacksonville\n" + "uid: akarasulu2\n"
+            + "krb5PrincipalName: akarasulu@EXAMPLE.COM\n" + "krb5KeyVersionNumber: 0\n"
+            + "mail: akarasulu@example.com\n" + "telephonenumber: +1 904 982 6882\n"
+            + "facsimiletelephonenumber: +1 904 982 6883\n" + "roomnumber: 666\n" + "userPassword: maxwell\n"
+            + "safehausUid: akarasulu2\n" + "safehausRealm: example.com\n" + "safehausLabel: example realm\n"
+            + "safehausFactor: 34258723456\n" + "safehausSecret:: d$U*E&*^dss%$(345j\n"
+            + "safehausFailuresInEpoch: 7\n" + "safehausResynchCount: -1\n" + "safehausTokenPin: 1234\n"
+            + "safehausNotifyBy: sms\n"
+            + "safehausInfo: for testcases\n";
+
+        LdifReader parser = new LdifReader();
+        Entry entry = ( Entry ) parser.parseLdif( ldif ).get( 0 );
+        Attributes attributes = entry.getAttributes();
+        
+        attributes.remove( "dn" );
+
+        sysRoot.bind( "uid=akarasulu2", null, attributes );
+        ServerProfile p = ( ServerProfile ) sysRoot.lookup( "uid=akarasulu2" );
+
+        assertNotNull( p );
+        assertEquals( 34258723456L, p.getFactor() );
+        assertEquals( 7, p.getFailuresInEpoch() );
+        assertEquals( -1, p.getResynchCount() );
+        assertEquals( "for testcases", p.getInfo() );
+        assertEquals( "example realm", p.getLabel() );
+        assertEquals( "example.com", p.getRealm() );
+        assertEquals( "akarasulu2", p.getUserId() );
+    }
+
+
+    public void testStateFactory() throws NamingException
+    {
+        String ldif = "dn: uid=akarasulu,ou=users,dc=example,dc=com\n" +
+            "cn: Alex Karasulu\n" + "sn: Karasulu\n" +
+            "givenname: Alex\n" +
+            "objectClass: top\n" +
+            "objectClass: person\n" +
+            "objectClass: organizationalPerson\n" +
+            "objectClass: inetOrgPerson\n" +
+            "objectClass: krb5Principal\n" +
+            "objectClass: krb5KDCEntry\n" +
+            "objectClass: safehausProfile\n" +
+            "ou: Directory\n" +
+            "ou: Users\n" +
+            "l: Jacksonville\n" +
+            "uid: akarasulu2\n" +
+            "krb5PrincipalName: akarasulu@EXAMPLE.COM\n" +
+            "krb5KeyVersionNumber: 0\n" +
+            "mail: akarasulu@example.com\n" +
+            "telephonenumber: +1 904 982 6882\n" +
+            "facsimiletelephonenumber: +1 904 982 6883\n" +
+            "safehausNotifyBy: sms\n" +
+            "roomnumber: 666\n" +
+            "userPassword: maxwell\n";
+
+        LdifReader parser = new LdifReader();
+        Entry entry = ( Entry ) parser.parseLdif( ldif ).get( 0 );
+        Attributes attributes = ( Attributes ) entry.getAttributes();
+        attributes.remove( "dn" );
+
+        BaseServerProfileModifier modifier = new BaseServerProfileModifier();
+        modifier.setFactor( 56902748234756L );
+        modifier.setFailuresInEpoch( 234 );
+        modifier.setLabel( "test domain" );
+        modifier.setInfo( "just some info" );
+        modifier.setSecret( new byte[]
+            { 'a', 's', 'd', 'f', 'a', 'd', 's', 'f', 'a', 'd', 'f', 'a', 'd', 'f', 'a', 'f', 's', 'f', 'd', 'f' } );
+        modifier.setUserId( "akarasulu2" );
+        modifier.setRealm( "example.com" );
+        modifier.setResynchCount( 2 );
+
+        sysRoot.bind( "uid=akarasulu2", modifier.getServerProfile(), attributes );
+        Attributes reload = sysRoot.getAttributes( "uid=akarasulu2" );
+        assertNotNull( reload );
+        assertEquals( 56902748234756L, Long.parseLong( ( String ) reload.get( "safehausFactor" ).get() ) );
+        assertEquals( 234, Integer.parseInt( ( String ) reload.get( "safehausFailuresInEpoch" ).get() ) );
+        assertEquals( 2, Integer.parseInt( ( String ) reload.get( "safehausResynchCount" ).get() ) );
+        assertEquals( "akarasulu2", reload.get( "safehausUid" ).get() );
+        assertEquals( "example.com", reload.get( "safehausRealm" ).get() );
+        assertEquals( "test domain", reload.get( "safehausLabel" ).get() );
+        assertEquals( "just some info", reload.get( "safehausInfo" ).get() );
+    }
+}

Added: directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ServerProfileStoreITest.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ServerProfileStoreITest.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ServerProfileStoreITest.java (added)
+++ directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/ServerProfileStoreITest.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,253 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.safehaus.triplesec.store;
+
+
+import java.io.File;
+import java.util.*;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
+import org.apache.directory.server.core.configuration.Configuration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.MutableStartupConfiguration;
+import org.apache.directory.server.core.configuration.ShutdownConfiguration;
+import org.apache.directory.server.core.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.core.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.core.schema.bootstrap.CosineSchema;
+import org.apache.directory.server.core.schema.bootstrap.InetorgpersonSchema;
+import org.apache.directory.server.core.schema.bootstrap.Krb5kdcSchema;
+import org.apache.directory.server.core.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.protocol.shared.store.Krb5KdcEntryFilter;
+import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
+import org.safehaus.profile.BaseServerProfileModifier;
+import org.safehaus.profile.ProfileTestData;
+import org.safehaus.profile.ServerProfile;
+import org.safehaus.triplesec.store.schema.SafehausSchema;
+import junit.framework.TestCase;
+
+
+/**
+ * A set of testcases for a profile store implementation.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ServerProfileStoreITest extends TestCase
+{
+    /** the server side store for profiles */
+    ServerProfileStore store = null;
+    DirContext userContext = null;
+
+    public ServerProfileStoreITest() throws Exception
+    {
+        super();
+    }
+
+
+    protected void setUp() throws Exception
+    {
+        File workingDirectory = File.createTempFile( "ServerProfileStoreITest", "test" );
+        if ( ! workingDirectory.exists() ) 
+        {
+            workingDirectory.mkdirs();
+        }
+        FileUtils.forceDelete( workingDirectory );
+        
+        MutableStartupConfiguration config = new MutableStartupConfiguration();
+        config.setWorkingDirectory( workingDirectory );
+        MutablePartitionConfiguration partConfig = new MutablePartitionConfiguration();
+        partConfig.setName( "example" );
+
+        HashSet indices = new HashSet();
+        indices.add( "dc" );
+        indices.add( "ou" );
+        indices.add( "objectClass" );
+        indices.add( "cn" );
+        indices.add( "uid" );
+        partConfig.setIndexedAttributes( indices );
+
+        partConfig.setSuffix( "dc=example,dc=com" );
+
+        LockableAttributesImpl attrs = new LockableAttributesImpl();
+        LockableAttributeImpl attr = new LockableAttributeImpl( "objectClass" );
+        attr.add( "top" );
+        attr.add( "domain" );
+        attrs.put( attr );
+        attrs.put( "dc", "example" );
+        partConfig.setContextEntry( attrs );
+
+        Set schemas = new HashSet();
+        schemas.add( new SystemSchema() );
+        schemas.add( new SafehausSchema() );
+        schemas.add( new ApacheSchema() );
+        schemas.add( new CoreSchema() );
+        schemas.add( new CosineSchema() );
+        schemas.add( new InetorgpersonSchema() );
+        schemas.add( new Krb5kdcSchema() );
+        config.setBootstrapSchemas( schemas );
+        config.setContextPartitionConfigurations( Collections.singleton( partConfig ) );
+        config.setShutdownHookEnabled( false );
+        
+        Hashtable env = new Hashtable();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Configuration.JNDI_KEY, config );
+        env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+        env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+
+        userContext = new InitialDirContext( env );
+        try
+        {
+            userContext = ( DirContext ) userContext.lookup( "ou=users" );
+        }
+        catch ( NamingException e )
+        {
+            Attributes users = new BasicAttributes( "objectClass", "top", true );
+            users.get( "objectClass" ).add( "organizationalUnit" );
+            attrs.put( "ou", "users" );
+            userContext = userContext.createSubcontext( "ou=users", attrs );
+        }
+
+        store = new DefaultServerProfileStore( userContext );
+        store.init();
+
+        List filters = Collections.singletonList( new Krb5KdcEntryFilter() );
+        LdifFileLoader loader = new LdifFileLoader( userContext, new File( "safehaus.ldif" ), filters, getClass().getClassLoader() );
+        loader.execute();
+
+        assertNotNull( store );
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        userContext.close();
+        ShutdownConfiguration config = new ShutdownConfiguration();
+        Hashtable env = new Hashtable();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Configuration.JNDI_KEY, config );
+        env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+        env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+        new InitialDirContext( env );
+        
+        userContext = null;
+        store = null;
+    }
+
+
+    /**
+     * Tests getting a profile.
+     *
+     * @throws Exception if it fails
+     */
+    public void testGetProfile() throws Exception
+    {
+        KerberosPrincipal principal = new KerberosPrincipal( "akarasulu@EXAMPLE.COM" );
+        ServerProfile p = store.getProfile( principal );
+        assertNotNull( p );
+        assertEquals( "akarasulu", p.getUserId() );
+        assertEquals( "EXAMPLE.COM", p.getRealm() );
+        assertEquals( "example realm", p.getLabel() );
+        assertEquals( 27304238, p.getFactor() );
+        assertEquals( 0, p.getFailuresInEpoch() );
+        assertEquals( -1, p.getResynchCount() );
+        assertEquals( "test account", p.getInfo() );
+    }
+
+
+    /**
+     * Tests updating a profile.
+     *
+     * @throws Exception if it fails
+     */
+    public void testUpdate() throws Exception
+    {
+        KerberosPrincipal principal = new KerberosPrincipal( "akarasulu@EXAMPLE.COM" );
+        ServerProfile p = store.getProfile( principal );
+        assertNotNull( p );
+        assertEquals( "akarasulu", p.getUserId() );
+        assertEquals( "EXAMPLE.COM", p.getRealm() );
+        assertEquals( "example realm", p.getLabel() );
+        assertEquals( 27304238, p.getFactor() );
+        assertEquals( 0, p.getFailuresInEpoch() );
+        assertEquals( -1, p.getResynchCount() );
+        assertEquals( "test account", p.getInfo() );
+
+        BaseServerProfileModifier modifier = new BaseServerProfileModifier( p );
+        modifier.setFailuresInEpoch( 123 );
+        modifier.setResynchCount( 0 );
+        modifier.setFactor( 27304238 + 1 );
+        store.update( principal, modifier.getServerProfile() );
+
+        ServerProfile updated = store.getProfile( principal );
+        assertEquals( 123, updated.getFailuresInEpoch() );
+        assertEquals( 0, updated.getResynchCount() );
+        assertEquals( 27304239, updated.getFactor() );
+    }
+
+
+    /**
+     * Tests adding a profile.
+     *
+     * @throws Exception if it fails
+     */
+    public void testAddProfile() throws Exception
+    {
+        for ( int ii = 0; ii < ProfileTestData.PROFILES.length; ii++ )
+        {
+            ServerProfile profile = ProfileTestData.PROFILES[ii];
+            String name = profile.getUserId() + "@" + profile.getRealm();
+            KerberosPrincipal principal = new KerberosPrincipal( name );
+
+            if ( ! store.hasProfile( principal ) )
+            {
+                store.add( profile );
+            }
+
+            profile = store.getProfile( principal );
+            assertNotNull( profile );
+            assertEquals( profile.getFactor(), profile.getFactor() );
+            assertEquals( profile.getUserId(), profile.getUserId() );
+            assertEquals( profile.getFailuresInEpoch(), profile.getFailuresInEpoch() );
+            assertEquals( profile.getInfo(), profile.getInfo() );
+            assertEquals( profile.getLabel(), profile.getLabel() );
+            assertEquals( profile.getRealm(), profile.getRealm() );
+            assertEquals( profile.getResynchCount(), profile.getResynchCount() );
+            assertEquals( profile.getSecret(), profile.getSecret() );
+        }
+    }
+}

Added: directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/interceptor/ApplicationACIManagerITest.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/interceptor/ApplicationACIManagerITest.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/interceptor/ApplicationACIManagerITest.java (added)
+++ directory/trunks/triplesec/store/src/test/java/org/safehaus/triplesec/store/interceptor/ApplicationACIManagerITest.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,366 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.safehaus.triplesec.store.interceptor;
+
+
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.directory.server.core.unit.AbstractAdminTestCase;
+import org.apache.directory.server.core.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.core.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.core.schema.bootstrap.Krb5kdcSchema;
+import org.apache.directory.server.core.configuration.Configuration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.MutableInterceptorConfiguration;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.safehaus.triplesec.store.ProfileObjectFactory;
+import org.safehaus.triplesec.store.ProfileStateFactory;
+import org.safehaus.triplesec.store.schema.SafehausSchema;
+
+
+/**
+ * Test case for the PolicyProtectionInterceptor.
+ *
+ * @author Trustin Lee
+ * @version $Rev: 946 $, $Date: 2006-09-18 22:59:56 -0400 (Mon, 18 Sep 2006) $
+ */
+public class ApplicationACIManagerITest extends AbstractAdminTestCase
+{
+    private DirContext ctx;
+
+
+    public void setUp() throws Exception
+    {
+        Set schemas = super.configuration.getBootstrapSchemas();
+        schemas.add( new CoreSchema() );
+        schemas.add( new SystemSchema() );
+        schemas.add( new Krb5kdcSchema() );
+        schemas.add( new SafehausSchema() );
+        super.configuration.setBootstrapSchemas( schemas );
+        super.configuration.setShutdownHookEnabled( false );
+        
+        MutablePartitionConfiguration partitionCfg = new MutablePartitionConfiguration();
+        partitionCfg.setName( "example" );
+        partitionCfg.setSuffix( "dc=example,dc=com" );
+        Attributes ctxEntry = new LockableAttributesImpl();
+        ctxEntry.put( "objectClass", "top" );
+        ctxEntry.put( "dc", "example" );
+        ctxEntry.put( "administrativeRole", "accessControlSpecificArea" );
+        partitionCfg.setContextEntry( ctxEntry );
+        partitionCfg.setContextPartition( new JdbmPartition() );
+
+        Set partitions = super.configuration.getContextPartitionConfigurations();
+        partitions.add( partitionCfg );
+        super.configuration.setContextPartitionConfigurations( partitions );
+
+        List interceptors = super.configuration.getInterceptorConfigurations();
+        MutableInterceptorConfiguration interceptorCfg = new MutableInterceptorConfiguration();
+        interceptorCfg.setName( "protector" );
+        interceptorCfg.setInterceptor( new PolicyProtectionInterceptor() );
+        interceptors.add( interceptorCfg );
+        super.configuration.setInterceptorConfigurations( interceptors );
+        super.configuration.setAccessControlEnabled( true );
+        
+        super.overrideEnvironment( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+        super.overrideEnvironment( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+        super.setLdifPath( "/interceptor.ldif", getClass() );
+        super.setUp();
+
+        Hashtable env = new Hashtable();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Configuration.JNDI_KEY, super.configuration );
+        env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+        env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+
+        ctx = new InitialDirContext( env );
+    }
+
+
+    public void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+    
+    
+    private void assertNoAccessToAdminGroupByApp( String appName, String userPassword ) throws Exception
+    {
+        if ( userPassword == null )
+        {
+            userPassword = "secret";
+        }
+        
+        LdapDN dn = new LdapDN( "appName="+appName+",ou=Applications,dc=example,dc=com" );
+
+        Hashtable env = new Hashtable();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
+        env.put( Context.SECURITY_PRINCIPAL, dn.getUpName() );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, userPassword );
+        env.put( Configuration.JNDI_KEY, super.configuration );
+        env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+        env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+
+        InitialDirContext ctx = new InitialDirContext( env );
+
+        try
+        {
+            ctx.lookup( "cn=" + appName + "AdminGroup,ou=Groups" );
+            fail( dn + " should not have access to it's admin group" );
+        }
+        catch( NoPermissionException e )
+        {
+        }
+    }
+    
+    
+    private boolean aciItemsExist( String appName ) throws Exception
+    {
+        // check for the Aci just for the application's access
+        LdapDN dn = new LdapDN( "cn="+appName+"Aci,dc=example,dc=com" );
+        
+        try
+        {
+            Attributes attrs = ctx.getAttributes( dn, new String[] { "cn", "objectClass", 
+                "subtreeSpecification", "prescriptiveACI" } );
+            
+            if ( attrs == null )
+            {
+                return false;
+            }
+            
+            Attribute prescriptiveACI = attrs.get( "prescriptiveACI" );
+
+            if ( prescriptiveACI == null )
+            {
+                return false;
+            }
+            
+            return true;
+        }
+        catch ( NameNotFoundException e )
+        {
+            return false;
+        }
+    }
+
+    
+    private boolean adminGroupExists( String appName ) throws Exception
+    {
+        LdapDN dn = new LdapDN( "cn="+appName+"AdminGroup,ou=Groups,dc=example,dc=com" );
+
+        try
+        {
+            Attributes attrs = ctx.getAttributes( dn );
+            if ( attrs == null )
+            {
+                return false;
+            }
+            
+            Attribute uniqueMembers = attrs.get( "uniqueMember" );
+
+            if ( uniqueMembers == null )
+            {
+                return false;
+            }
+            
+            return true;
+        }
+        catch ( NameNotFoundException e )
+        {
+            return false;
+        }
+    }
+    
+    
+    private DirContext getAppContextAsApp( String appName ) throws NamingException
+    {
+        return getAppContextAsApp( appName, null );
+    }
+    
+    
+    private DirContext getAppContextAsApp( String appName, String userPassword ) throws NamingException
+    {
+        if ( userPassword == null )
+        {
+            userPassword = "secret";
+        }
+        
+        LdapDN dn = new LdapDN( "appName="+appName+",ou=Applications,dc=example,dc=com" );
+
+        Hashtable env = new Hashtable();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, dn.getUpName() );
+        env.put( Context.SECURITY_PRINCIPAL, dn.getUpName() );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, userPassword );
+        env.put( Configuration.JNDI_KEY, super.configuration );
+        env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+        env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+
+        return new InitialDirContext( env );
+    }
+    
+    
+    private void createApplication( String appName, String userPassword ) throws NamingException
+    {
+        // create the main application entry
+        Attributes attrs = new LockableAttributesImpl();
+        Attribute oc = new LockableAttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "policyApplication" );
+        attrs.put( oc );
+        attrs.put( "appName", appName );
+        if ( userPassword != null )
+        {
+            attrs.put( "userPassword", userPassword );
+        }
+        LdapDN dn = new LdapDN( "appName="+appName+",ou=Applications,dc=example,dc=com" );
+        ctx.createSubcontext( dn, attrs );
+        
+        // create ou=permissions
+        attrs = new LockableAttributesImpl();
+        oc = new LockableAttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "organizationalUnit" );
+        attrs.put( oc );
+        attrs.put( "ou", "permissions" );
+        dn = new LdapDN( "ou=permissions,appName="+appName+",ou=Applications,dc=example,dc=com" );
+        ctx.createSubcontext( dn, attrs );
+
+        // create ou=roles
+        attrs = new LockableAttributesImpl();
+        oc = new LockableAttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "organizationalUnit" );
+        attrs.put( oc );
+        attrs.put( "ou", "roles" );
+        dn = new LdapDN( "ou=roles,appName="+appName+",ou=Applications,dc=example,dc=com" );
+        ctx.createSubcontext( dn, attrs );
+
+        // create ou=profiles
+        attrs = new LockableAttributesImpl();
+        oc = new LockableAttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "organizationalUnit" );
+        attrs.put( oc );
+        attrs.put( "ou", "profiles" );
+        dn = new LdapDN( "ou=profiles,appName="+appName+",ou=Applications,dc=example,dc=com" );
+        ctx.createSubcontext( dn, attrs );
+    }
+    
+    
+    public void addAppUserToAdminGroup( String appName ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( "appName="+appName+",ou=Applications,dc=example,dc=com" );
+        Attributes attrs = new LockableAttributesImpl();
+        attrs.put( "uniqueMember", dn.getUpName() );
+        
+        ctx.modifyAttributes( "cn=" + appName + "AdminGroup,ou=Groups,dc=example,dc=com", 
+            DirContext.ADD_ATTRIBUTE, attrs );
+    }
+    
+    
+    private boolean canWriteToPermissions( String appName ) throws NamingException
+    {
+        DirContext appUserCtx = getAppContextAsApp( appName );
+        Attributes attrs = new LockableAttributesImpl();
+        attrs.put( "objectClass", "policyPermission" );
+        attrs.put( "permName", "testPerm" );
+        
+        try
+        {
+            appUserCtx.createSubcontext( "permName=testPerm,ou=permissions", attrs );
+            return true;
+        }
+        catch ( NoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            try
+            {
+                appUserCtx.destroySubcontext( "permName=testPerm,ou=permissions" );
+            }
+            catch( Throwable t )
+            {
+            }
+        }
+    }
+    
+    
+    public void testAddApplication() throws Exception
+    {
+        createApplication( "testApp", "secret" );
+        assertTrue( adminGroupExists( "testApp" ) );
+        assertTrue( aciItemsExist( "testApp" ) );
+        assertNoAccessToAdminGroupByApp( "testApp", "secret" );
+        assertFalse( canWriteToPermissions( "testApp" ) );
+        addAppUserToAdminGroup( "testApp" );
+        assertTrue( canWriteToPermissions( "testApp" ) );
+    }
+    
+
+    public void testRemoveApplication() throws Exception
+    {
+        testAddApplication();
+        destroyApplication( "testApp" );
+        assertFalse( adminGroupExists( "testApp" ) );
+        assertFalse( aciItemsExist( "testApp" ) );
+    }
+    
+
+    private void destroyApplication( String appName ) throws Exception
+    {
+        DirContext appCtx = ( DirContext ) ctx.lookup( "appName="+appName+",ou=Applications,dc=example,dc=com" );
+        appCtx.destroySubcontext( "ou=permissions" );
+        appCtx.destroySubcontext( "ou=profiles" );
+        appCtx.destroySubcontext( "ou=roles" );
+        appCtx.close();
+        
+        ctx.destroySubcontext( "appName="+appName+",ou=Applications,dc=example,dc=com" );
+    }
+
+
+    public static void main( String[] args )
+    {
+        junit.textui.TestRunner.run( ApplicationACIManagerITest.class );
+    }
+}
+