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 [6/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/ ad...

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapProfileDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapProfileDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapProfileDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapProfileDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,466 @@
+/*
+ *  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.admin.dao.ldap;
+
+
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+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.name.LdapDN;
+import org.safehaus.triplesec.admin.Constants;
+import org.safehaus.triplesec.admin.ConstraintViolationException;
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.EntryAlreadyExistsException;
+import org.safehaus.triplesec.admin.NoSuchEntryException;
+import org.safehaus.triplesec.admin.Profile;
+import org.safehaus.triplesec.admin.dao.ProfileDao;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class LdapProfileDao implements ProfileDao, LdapDao, Constants
+{
+    private static final Logger log = LoggerFactory.getLogger( LdapProfileDao.class );
+    private static final String[] ATTRIBUTES = new String[] {
+        PROFILEID_ID, DESCRIPTION_ID, USER_ID, GRANTS_ID, DENIALS_ID, ROLES_ID, PASSWORD_ID,
+        CREATORS_NAME_ID, CREATE_TIMESTAMP_ID, MODIFIERS_NAME_ID, MODIFY_TIMESTAMP_ID
+    };
+    private final DirContext ctx;
+    private final String baseUrl;
+    private final String principalName;
+    
+    
+    public LdapProfileDao( DirContext ctx ) throws DataAccessException
+    {
+        this.ctx = ctx;
+        String name = null;
+        String principal = null;
+        try
+        {
+            name = ctx.getNameInNamespace();
+            String principalDn = ( String ) ctx.getEnvironment().get( Context.SECURITY_PRINCIPAL );
+            if ( principalDn.equalsIgnoreCase( "uid=admin,ou=system" ) )
+            {
+                principal = "admin";
+            }
+            else
+            {
+                principal = ( String ) new LdapDN( principalDn ).getRdn().getValue();
+            }
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to get name in namespace for base context.";
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+        finally
+        {
+            baseUrl = name;
+            principalName = principal;
+        }
+    }
+
+
+    public Iterator profileIterator( String applicationName, String user ) throws DataAccessException
+    {
+        String base = getRelativeDn( applicationName );
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( ATTRIBUTES );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        try
+        {
+            return new JndiIterator( this, ctx.search( base, 
+                "(& (profileId=*) (user=" + user + ") (objectClass=policyProfile) )", controls ), applicationName );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to search " + base + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+    
+    public Iterator profileIterator( String appName ) throws DataAccessException
+    {
+        String base = getRelativeDn( appName );
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( ATTRIBUTES );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        try
+        {
+            return new JndiIterator( this, ctx.search( base, 
+                "(& (profileId=*) (objectClass=policyProfile) )", controls ), appName );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to search " + base + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+    
+    public Profile load( String appName, String profileId ) throws DataAccessException
+    {
+        String description;
+        String user;
+        Set roles;
+        Set grants;
+        Set denials;
+        String rdn = getRelativeDn( appName, profileId );
+        Attributes attrs = null;
+        
+        String creatorsName;
+        String modifiersName;
+        Date createTimestamp;
+        Date modifyTimestamp;
+        boolean disabled = false;
+        
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+            user = LdapUtils.getSingleValued( USER_ID, attrs );
+            description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs );
+            roles = getMultiValued( ROLES_ID, attrs );
+            grants = getMultiValued( GRANTS_ID, attrs );
+            denials = getMultiValued( DENIALS_ID, attrs );
+            disabled = LdapUtils.getBoolean( SAFEHAUS_DISABLED_ID, attrs, false );
+            
+            creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs );
+            modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs );
+            createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs );
+            modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs );
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = "Could not find " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to lookup " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+        
+        return new Profile( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, 
+            appName, profileId, user, description, grants, denials, roles, disabled );
+    }
+
+
+    public Profile add( String appName, String profileId, String user, String description, Set grants, Set denials,
+        Set roles ) throws DataAccessException
+    {
+        BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, POLICY_PROFILE_OC, true );
+        attrs.put( PROFILEID_ID, profileId );
+        attrs.put( USER_ID, user );
+        if ( description != null )
+        {
+            attrs.put( DESCRIPTION_ID, description );
+        }
+        addMultiValued( ROLES_ID, attrs, roles );
+        addMultiValued( GRANTS_ID, attrs, grants );
+        addMultiValued( DENIALS_ID, attrs, denials );
+
+        String rdn = getRelativeDn( appName, profileId );
+        try
+        {
+            ctx.createSubcontext( rdn, attrs );
+            return new Profile( principalName, new Date( System.currentTimeMillis() ), this, appName, 
+                profileId, user, description, grants, denials, roles );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            log.error( "Cannot create role " + rdn, e );
+            EntryAlreadyExistsException eaee = new EntryAlreadyExistsException();
+            eaee.initCause( e );
+            throw eaee;
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Unexpected failure", e );
+            throw new DataAccessException( e.getMessage() );
+        }
+    }
+
+
+    public Profile rename( String newProfileId, Profile profile ) throws DataAccessException
+    {
+        String oldRdn = getRelativeDn( profile.getApplicationName(), profile.getId() );
+        String newRdn = getRelativeDn( profile.getApplicationName(), newProfileId );
+        
+        try
+        {
+            ctx.rename( oldRdn, newRdn );
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = "Rename failed. Could not find " + oldRdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            String msg = "Rename failed. Another profile already exists at " + newRdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new EntryAlreadyExistsException( msg );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " is required by other entities";
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " could not be renamed to " + newRdn;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+        
+        return new Profile( profile.getCreatorsName(), profile.getCreateTimestamp(), principalName, 
+            new Date( System.currentTimeMillis() ), this, profile.getApplicationName(), newProfileId, 
+            profile.getUser(), profile.getDescription(), profile.getGrants(), 
+            profile.getDenials(), profile.getRoles(), profile.isDisabled() );
+    }
+
+
+    public Profile modify( String creatorsName, Date createTimestamp, String appName, String profileId, 
+        String user, String description, Set grants, Set denials,
+        Set roles, boolean disabled, ModificationItem[] mods ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( appName, profileId );
+        
+        try
+        {
+            ctx.modifyAttributes( rdn, mods );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Could not modify " + rdn + " under " + baseUrl;
+            msg += " The modification violates constraints.";
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = "Entry " + rdn + " under " + baseUrl + " does not exist";
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Could not modify " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        
+        return new Profile( creatorsName, createTimestamp, principalName, new Date( System.currentTimeMillis() ), 
+            this, appName, profileId, user, description, grants, denials, roles, disabled );
+    }
+
+
+    public void delete( String appName, String profileId ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( appName, profileId );
+
+        try
+        {
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            msg += ".  Other entities depend on " + profileId;
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Private utility methods
+    // -----------------------------------------------------------------------
+
+    
+    private String getRelativeDn( String appName, String profileId )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "profileId=" ).append( profileId );
+        buf.append( ",ou=Profiles,appName=" ).append( appName );
+        buf.append( ",ou=Applications" );
+        return buf.toString();
+    }
+    
+    
+    private String getRelativeDn( String appName )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "ou=Profiles,appName=" ).append( appName );
+        buf.append( ",ou=Applications" );
+        return buf.toString();
+    }
+    
+    
+    private Set getMultiValued( String id, Attributes attrs ) throws NamingException 
+    {
+        Set values = Collections.EMPTY_SET;
+        if ( attrs.get( id ) != null )
+        {
+            values = new HashSet();
+            for ( NamingEnumeration ii = attrs.get( id ).getAll(); ii.hasMore(); /**/ )
+            {
+                values.add( ii.next() );
+            }
+            return Collections.unmodifiableSet( values );
+        }
+        return values;
+    }
+    
+    
+    private String getSingleValued( String id, Attributes attrs ) throws NamingException
+    {
+        String value = null;
+        if ( attrs.get( id ) != null )
+        {
+            Object obj = attrs.get( id ).get();
+            if ( obj instanceof String )
+            {
+                value = ( String ) attrs.get( id ).get();
+            }
+            else
+            {
+                try
+                {
+                    value = new String( ( byte[] ) attrs.get( id ).get(), "UTF-8" );
+                }
+                catch ( UnsupportedEncodingException e )
+                {
+                    log.error( "Failed to encode string", e );
+                }
+            }
+        }
+        return value;
+    }
+    
+
+    private void addMultiValued( String id, Attributes attrs, Set values )
+    {
+        if ( values == null )
+        {
+            return;
+        }
+        if ( ! values.isEmpty() )
+        {
+            BasicAttribute attr = new BasicAttribute( id );
+            for ( Iterator ii = values.iterator(); ii.hasNext(); /**/ )
+            {
+                attr.add( ii.next() );
+            }
+            attrs.put( attr );
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // LdapDao method implementations
+    // -----------------------------------------------------------------------
+
+    
+    public Object getEntryObject( Object extra, Attributes attrs )
+    {
+        String profileId = null;
+        String user = null;
+        String description = null;
+        Set roles = Collections.EMPTY_SET;
+        Set grants = Collections.EMPTY_SET;
+        Set denials = Collections.EMPTY_SET;
+        
+        String creatorsName = null;
+        String modifiersName = null;
+        Date createTimestamp = null;
+        Date modifyTimestamp = null;
+        boolean disabled = false;
+        
+        try
+        {
+            profileId = ( String ) attrs.get( PROFILEID_ID ).get();
+            user =  ( String ) attrs.get( USER_ID ).get();
+            description = getSingleValued( DESCRIPTION_ID, attrs );
+            roles = getMultiValued( ROLES_ID, attrs );
+            grants = getMultiValued( GRANTS_ID, attrs );
+            denials = getMultiValued( DENIALS_ID, attrs );
+            disabled = LdapUtils.getBoolean( SAFEHAUS_DISABLED_ID, attrs, false );
+            
+            creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs );
+            modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs );
+            createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs );
+            modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to produce object for attributes: " + attrs;
+            log.error( msg, e );
+        }
+        
+        return new Profile( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, 
+            ( String ) extra, profileId, user, description, grants, denials, roles, disabled );
+    }
+
+
+    public void deleteEntry( String rdn )
+    {
+        try
+        {
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Failed to delete " + rdn + " under " + baseUrl, e );
+        }
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapRoleDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapRoleDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapRoleDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapRoleDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,388 @@
+/*
+ *  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.admin.dao.ldap;
+
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+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.name.LdapDN;
+import org.safehaus.triplesec.admin.Constants;
+import org.safehaus.triplesec.admin.ConstraintViolationException;
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.EntryAlreadyExistsException;
+import org.safehaus.triplesec.admin.NoSuchEntryException;
+import org.safehaus.triplesec.admin.Role;
+import org.safehaus.triplesec.admin.dao.RoleDao;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class LdapRoleDao implements RoleDao, LdapDao, Constants
+{
+    private static final Logger log = LoggerFactory.getLogger( LdapRoleDao.class );
+    private static final String[] ATTRIBUTES = new String[] {
+        ROLE_NAME_ID, DESCRIPTION_ID, GRANTS_ID, CREATORS_NAME_ID, CREATE_TIMESTAMP_ID, 
+        MODIFIERS_NAME_ID, MODIFY_TIMESTAMP_ID
+    };
+    private final DirContext ctx;
+    private final String baseUrl;
+    private final String principalName;
+    
+    
+    public LdapRoleDao( DirContext ctx ) throws DataAccessException
+    {
+        this.ctx = ctx;
+
+        String name = null;
+        String principal = null;
+        try
+        {
+            name = ctx.getNameInNamespace();
+            String principalDn = ( String ) ctx.getEnvironment().get( Context.SECURITY_PRINCIPAL );
+            if ( principalDn.equalsIgnoreCase( "uid=admin,ou=system" ) )
+            {
+                principal = "admin";
+            }
+            else 
+            {
+                principal = ( String ) new LdapDN( principalDn ).getRdn().getValue();
+            }
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to get name in namespace for base context.";
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+            
+        }
+        finally
+        {
+            baseUrl = name;
+            principalName = principal;
+        }
+    }
+
+    
+    public Iterator roleIterator( String appName ) throws DataAccessException
+    {
+        String base = getRelativeDn( appName );
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( ATTRIBUTES );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        try
+        {
+            return new JndiIterator( this, ctx.search( base, 
+                "(& (roleName=*) (objectClass=policyRole) )", controls ), appName );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to search " + base + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+
+    public Role load( String appName, String roleName ) throws DataAccessException
+    {
+        String description = null;
+        Set grants = Collections.EMPTY_SET;
+        String rdn = getRelativeDn( appName, roleName );
+        Attributes attrs = null;
+        
+        String creatorsName = null;
+        String modifiersName = null;
+        Date createTimestamp = null;
+        Date modifyTimestamp = null;
+        
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+            description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs );
+            creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs );
+            modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs );
+            createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs );
+            modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs );
+            
+            if ( attrs.get( GRANTS_ID ) != null )
+            {
+                grants = new HashSet();
+                for ( NamingEnumeration ii = attrs.get( GRANTS_ID ).getAll(); ii.hasMore(); /**/ )
+                {
+                    grants.add( ii.next() );
+                }
+            }
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = "Could not find " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to lookup " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+        
+        return new Role( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, appName, roleName, 
+            description, Collections.unmodifiableSet( grants ) );
+    }
+
+
+    public Role add( String appName, String roleName, String description, Set grants )
+        throws DataAccessException
+    {
+        BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, POLICY_ROLE_OC, true );
+        attrs.put( ROLE_NAME_ID, roleName );
+        if ( description != null )
+        {
+            attrs.put( DESCRIPTION_ID, description );
+        }
+        if ( ! grants.isEmpty() )
+        {
+            BasicAttribute attr = new BasicAttribute( GRANTS_ID );
+            for ( Iterator ii = grants.iterator(); ii.hasNext(); /**/ )
+            {
+                attr.add( ii.next() );
+            }
+            attrs.put( attr );
+        }
+        
+        String rdn = getRelativeDn( appName, roleName );
+        try
+        {
+            ctx.createSubcontext( rdn, attrs );
+            return new Role( principalName, new Date( System.currentTimeMillis() ), this, 
+                appName, roleName, description, grants );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            log.error( "Cannot create role " + rdn, e );
+            EntryAlreadyExistsException eaee = new EntryAlreadyExistsException();
+            eaee.initCause( e );
+            throw eaee;
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Unexpected failure", e );
+            throw new DataAccessException( e.getMessage() );
+        }
+    }
+
+
+    public Role rename( String newRoleName, Role role ) throws DataAccessException
+    {
+        String oldRdn = getRelativeDn( role.getApplicationName(), role.getName() );
+        String newRdn = getRelativeDn( role.getApplicationName(), newRoleName );
+        
+        try
+        {
+            ctx.rename( oldRdn, newRdn );
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = "Rename failed. Could not find " + oldRdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            String msg = "Rename failed. Another role already exists at " + newRdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new EntryAlreadyExistsException( msg );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " is required by other entities";
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " could not be renamed to " + newRdn;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+        
+        return new Role( role.getCreatorsName(), role.getCreateTimestamp(), principalName, 
+            new Date( System.currentTimeMillis() ), this, role.getApplicationName(), newRoleName, 
+            role.getDescription(), role.getGrants() );
+    }
+
+
+    public Role modify( String creatorsName, Date createTimestamp, String appName, String roleName, 
+        String description, Set grants, ModificationItem[] mods )
+        throws DataAccessException
+    {
+            String rdn = getRelativeDn( appName, roleName );
+            
+            try
+            {
+                ctx.modifyAttributes( rdn, mods );
+            }
+            catch ( SchemaViolationException e )
+            {
+                String msg = "Could not modify " + rdn + " under " + baseUrl;
+                msg += " The modification violates constraints.";
+                log.error( msg, e );
+                throw new ConstraintViolationException( msg );
+            }
+            catch ( NameNotFoundException e )
+            {
+                String msg = "Entry " + rdn + " under " + baseUrl + " does not exist";
+                log.error( msg, e );
+                throw new NoSuchEntryException( msg );
+            }
+            catch ( NamingException e )
+            {
+                String msg = "Could not modify " + rdn + " under " + baseUrl;
+                log.error( msg, e );
+                throw new NoSuchEntryException( msg );
+            }
+            
+            return new Role( creatorsName, createTimestamp, principalName, new Date( System.currentTimeMillis() ), 
+                this, appName, roleName, description, grants );
+    }
+
+
+    public void delete( String appName, String roleName ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( appName, roleName );
+
+        try
+        {
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            msg += ".  Other entities depend on " + roleName;
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Private utility methods
+    // -----------------------------------------------------------------------
+
+    
+    private String getRelativeDn( String appName, String roleName )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "roleName=" ).append( roleName );
+        buf.append( ",ou=Roles,appName=" ).append( appName );
+        buf.append( ",ou=Applications" );
+        return buf.toString();
+    }
+    
+    
+    private String getRelativeDn( String appName )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "ou=Roles,appName=" ).append( appName );
+        buf.append( ",ou=Applications" );
+        return buf.toString();
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // LdapDao method implementations
+    // -----------------------------------------------------------------------
+
+    
+    public Object getEntryObject( Object extra, Attributes attrs )
+    {
+        String roleName = null;
+        String description = null;
+        Set grants = Collections.EMPTY_SET;
+        
+        String creatorsName = null;
+        String modifiersName = null;
+        Date createTimestamp = null;
+        Date modifyTimestamp = null;
+        
+        try
+        {
+            roleName = ( String ) attrs.get( ROLE_NAME_ID ).get();
+            description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs );
+            creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs );
+            modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs );
+            createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs );
+            modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs );
+            if ( attrs.get( GRANTS_ID ) != null )
+            {
+                grants = new HashSet();
+                for ( NamingEnumeration ii = attrs.get( GRANTS_ID ).getAll(); ii.hasMore(); /**/ )
+                {
+                    grants.add( ii.next() );
+                }
+            }
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to produce object for attributes: " + attrs;
+            log.error( msg, e );
+        }
+        
+        return new Role( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, 
+            ( String ) extra, roleName, description, Collections.unmodifiableSet( grants ) );
+    }
+
+
+    public void deleteEntry( String rdn )
+    {
+        try
+        {
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Failed to delete " + rdn + " under " + baseUrl, e );
+        }
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUserDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUserDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUserDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUserDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,261 @@
+/*
+ *  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.admin.dao.ldap;
+
+
+import java.util.Iterator;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.math.RandomUtils;
+import org.safehaus.triplesec.admin.Constants;
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.NoSuchEntryException;
+import org.safehaus.triplesec.admin.PermissionDeniedException;
+import org.safehaus.triplesec.admin.User;
+import org.safehaus.triplesec.admin.dao.UserDao;
+
+
+public class LdapUserDao extends AbstractLdapDao implements UserDao, Constants
+{
+    private static final String[] ATTRIBUTES = 
+        {
+            "*", "+"
+        };
+
+    private final LdapExternalUserDao externalUserDao;
+    private final LdapLocalUserDao localUserDao;
+    private final LdapHauskeysUserDao hauskeysUserDao;
+    
+    
+    public LdapUserDao( DirContext ctx, LdapExternalUserDao externalUserDao, LdapLocalUserDao localUserDao, 
+        LdapHauskeysUserDao hauskeysUserDao ) throws DataAccessException
+    {
+        super( ctx );
+        this.externalUserDao = externalUserDao;
+        this.localUserDao = localUserDao;
+        this.hauskeysUserDao = hauskeysUserDao;
+    }
+
+
+    public User load( String id ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( id );
+        Attributes attrs = null;
+
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = "Could not find " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NoPermissionException e )
+        {
+            String msg = "User load failed. Permission denied.";
+            log.error( msg, e );
+            throw new PermissionDeniedException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to lookup " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+
+        return ( User ) getEntryObject( null, attrs );
+    }
+
+
+    public boolean hasUser( String id ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( id );
+        Attributes attrs = null;
+
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+        }
+        catch ( NameNotFoundException e )
+        {
+            return false;
+        }
+        catch ( NoPermissionException e )
+        {
+            String msg = "User lookup failed. Permission denied.";
+            log.error( msg, e );
+            throw new PermissionDeniedException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to lookup " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+
+        return attrs != null;
+    }
+
+
+    public Object getEntryObject( Object extra, Attributes attrs )
+    {
+        try
+        {
+            if ( isExternalUser( attrs ) ) 
+            {
+                return externalUserDao.getEntryObject( extra, attrs );
+            }
+            else if ( isHauskeysUser( attrs ) )
+            {
+                return hauskeysUserDao.getEntryObject( extra, attrs );
+            }
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Failed to determine type of user", e );
+        }
+        
+        return localUserDao.getEntryObject( extra, attrs );
+    }
+
+
+    public Iterator iterator() throws DataAccessException
+    {
+        String base = "ou=Users";
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( ATTRIBUTES );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        try
+        {
+            return new JndiIterator( this, ctx.search( base, 
+                "(| (objectClass=referral) (objectClass=person) )", controls ), null );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to search " + base + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+    
+    
+    public String getRandomUniqueActivationKey() throws DataAccessException
+    {
+        String key = null;
+        int count = 0;
+        boolean isNotUnique = true;
+        while( isNotUnique && count < 10 )
+        {
+            // max length 16, min length 8 bytes
+            int length = 8 + RandomUtils.nextInt() % 8;
+            key = RandomStringUtils.randomNumeric( length );
+
+            NamingEnumeration list = null;
+            try
+            {
+                list = ctx.search( "ou=users", new BasicAttributes( "safehausActivationKey", key, true ) );
+                isNotUnique = list.hasMore();
+            }
+            catch ( NamingException e )
+            {
+                throw new DataAccessException( "Encountered failure while searching for activation keys." );
+            }
+            finally
+            {
+                if ( list != null ) { try { list.close(); }catch (Exception e){ log.error( "can't close naming enum" ); } };
+                count++;
+            }
+        }
+
+        return key;
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Private Utility Methods
+    // -----------------------------------------------------------------------
+
+    
+    private boolean isExternalUser( Attributes attrs ) throws NamingException
+    {
+        Attribute oc = attrs.get( OBJECT_CLASS_ID );
+        
+        if ( oc == null )
+        {
+            return false;
+        }
+        
+        if ( oc.contains( REFERRAL_OC ) )
+        {
+            return true;
+        }
+        
+        for ( NamingEnumeration ii = oc.getAll(); ii.hasMore(); /**/ )
+        {
+            String val = ( String ) ii.next();
+            if ( val.equalsIgnoreCase( REFERRAL_OC ) )
+            {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    
+    private boolean isHauskeysUser( Attributes attrs ) throws NamingException
+    {
+        String samType = LdapUtils.getSingleValued( APACHE_SAM_TYPE_ID, attrs );
+        
+        if ( samType == null )
+        {
+            return false;
+        }
+        else if ( Integer.parseInt( samType ) == 7 )
+        {
+            return true;
+        }
+        else
+        {
+            throw new IllegalStateException( "Unrecognized sam type value: " + samType );
+        }
+    }
+
+    
+    private String getRelativeDn( String uid )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "uid=" ).append( uid );
+        buf.append( ",ou=Users" );
+        return buf.toString();
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUtils.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUtils.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUtils.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapUtils.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,103 @@
+/*
+ *  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.admin.dao.ldap;
+
+
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+
+public class LdapUtils
+{
+    public static String getSingleValued( String id, Attributes attrs ) throws NamingException
+    {
+        if ( attrs.get( id ) == null )
+        {
+            return null;
+        }
+        
+        Object obj = attrs.get( id ).get();
+        if ( obj instanceof String )
+        {
+            return ( String ) obj;
+        }
+        
+        try
+        {
+            return new String( ( byte[] ) obj, "UTF-8" );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            IllegalStateException ise = new IllegalStateException( "Encoding not supported" );
+            ise.initCause( e );
+            throw ise;
+        }
+    }
+    
+    
+    public static String getPrincipal( String id, Attributes attrs ) throws NamingException
+    {
+        if ( attrs.get( id ) == null )
+        {
+            return null;
+        }
+        
+        return ( String ) new LdapDN( getSingleValued( id, attrs ) ).getRdn().getValue();
+    }
+
+
+    public static Date getDate( String id, Attributes attrs ) throws NamingException
+    {
+        if ( attrs.get( id ) == null )
+        {
+            return null;
+        }
+        
+        return DateUtils.getDate( getSingleValued( id, attrs ) );
+    }
+    
+    
+    public static boolean getBoolean( String id, Attributes attrs, boolean defaultValue ) throws NamingException
+    {
+        if ( attrs.get( id ) == null )
+        {
+            return defaultValue;
+        }
+        
+        return parseBoolean ( ( ( String ) attrs.get( id ).get() ).toLowerCase() );
+    }
+
+    
+    private static boolean parseBoolean( String bool )
+    {
+        if ( bool.equals( "true" ) )
+        {
+            return true;
+        }
+        
+        return false;
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/EntryModifierTest.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/EntryModifierTest.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/EntryModifierTest.java (added)
+++ directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/EntryModifierTest.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,221 @@
+/*
+ *  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.admin;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+
+public class EntryModifierTest extends TestCase
+{
+    public void testPermissionModifier0()
+    {
+        PermissionModifier modifier = new PermissionModifier( null, "testApp", "testPerm" );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertFalse( modifier.isUpdatableEntry() ); 
+        assertTrue( modifier.isNewEntry() );
+
+        modifier.setDescription( "description test" );
+        assertTrue( modifier.isUpdateNeeded() );
+    }
+
+    
+    public void testPermissionModifier1()
+    {
+        Permission archetype = new Permission( null, null, null, "", "archetypePerm", "first description" );
+        PermissionModifier modifier = new PermissionModifier( null, archetype );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertTrue( modifier.isUpdatableEntry() );
+        assertFalse( modifier.isNewEntry() );
+        
+        modifier.setDescription( "description test" );
+        assertTrue( modifier.isUpdateNeeded() );
+    }
+
+    
+    public void testRoleModifier0()
+    {
+        RoleModifier modifier = new RoleModifier( null, "testApp", "testRole" );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertFalse( modifier.isUpdatableEntry() );
+        assertTrue( modifier.isNewEntry() );
+        
+        // change the description
+        modifier.setDescription( "description test" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.setDescription( null );
+        assertFalse( modifier.isUpdateNeeded() );
+        
+        // add some grants
+        modifier.addGrant( "testPerm0" );
+        modifier.removeGrant( "testPerm0" );
+        assertFalse( modifier.isUpdateNeeded() );
+    }
+    
+    
+    public void testRoleModifier1()
+    {
+        Set grants = new HashSet();
+        grants.add( "bend" );
+        grants.add( "fold" );
+        grants.add( "spindle" );
+        Role archetype = new Role( null, null, null, "testApp", "testRole", null, grants );
+        
+        RoleModifier modifier = new RoleModifier( null, archetype );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertTrue( modifier.isUpdatableEntry() );
+        assertFalse( modifier.isNewEntry() );
+        
+        modifier.addGrant( "mutilate" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.setDescription( null );
+        modifier.removeGrant( "mutilate" );
+        assertFalse( modifier.isUpdateNeeded() );
+        
+        // add some grants
+        modifier.addGrant( "testPerm0" );
+        modifier.removeGrant( "testPerm0" );
+        assertFalse( modifier.isUpdateNeeded() );
+    }
+
+    
+    public void testRoleModifier2()
+    {
+        Set grants = new HashSet();
+        grants.add( "bend" );
+        grants.add( "fold" );
+        grants.add( "spindle" );
+        Role archetype = new Role( null, null, null, "testApp", "testRole", null, grants );
+        
+        RoleModifier modifier = new RoleModifier( null, archetype );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertTrue( modifier.isUpdatableEntry() );
+        assertFalse( modifier.isNewEntry() );
+        
+        modifier.addGrant( "mutilate" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.removeGrant( "fold" );
+        
+        modifier.setDescription( null );
+        modifier.removeGrant( "mutilate" );
+        modifier.addGrant( "fold" );
+        assertFalse( modifier.isUpdateNeeded() );
+        
+        // add some grants
+        modifier.addGrant( "testPerm0" );
+        modifier.removeGrant( "testPerm0" );
+        assertFalse( modifier.isUpdateNeeded() );
+    }
+    
+    
+    public void testProfileModifier0()
+    {
+        ProfileModifier modifier = new ProfileModifier( null, "testApp", "testProfile", "testUser" );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertFalse( modifier.isUpdatableEntry() );
+        assertTrue( modifier.isNewEntry() );
+
+        modifier.addDenial( "fold" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.removeDenial( "fold" );
+        assertFalse( modifier.isUpdateNeeded() );
+
+        modifier.addGrant( "twist" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.removeGrant( "twist" );
+        assertFalse( modifier.isUpdateNeeded() );
+    
+        modifier.addRole( "admin" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.removeRole( "admin" );
+        assertFalse( modifier.isUpdateNeeded() );
+        
+        modifier.setDescription( "test description" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.setDescription( null );
+        assertFalse( modifier.isUpdateNeeded() );
+    }
+
+    
+    public void testProfileModifier2()
+    {
+        Set grants = new HashSet();
+        grants.add( "spindle" );
+        grants.add( "mutilate" );
+        grants.add( "twist" );
+        Set denials = new HashSet();
+        denials.add( "fold" );
+        Set roles = new HashSet();
+        roles.add( "trusted" );
+        Profile archetype = new Profile( null, null, null, "testApp", "archetype", "akarasulu", 
+            "archetype profile", grants, denials, roles );
+        
+        ProfileModifier modifier = new ProfileModifier( null, archetype );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertTrue( modifier.isUpdatableEntry() );
+        assertFalse( modifier.isNewEntry() );
+
+        modifier.addDenial( "fold" );
+        assertFalse( modifier.isUpdateNeeded() );
+        modifier.removeDenial( "fold" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.addDenial( "fold" );
+
+        modifier.addGrant( "twist" );
+        assertFalse( modifier.isUpdateNeeded() );
+        modifier.removeGrant( "twist" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.addGrant( "twist" );
+    
+        modifier.addRole( "admin" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.removeRole( "admin" );
+        assertFalse( modifier.isUpdateNeeded() );
+        
+        modifier.setDescription( "test description" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.setDescription( "archetype profile" );
+        assertFalse( modifier.isUpdateNeeded() );
+    }
+    
+    
+    public void testGroupModifier()
+    {
+        Set members = new HashSet();
+        Group group = new Group( null, null, null, "testGroup", members );
+        assertEquals( "testGroup", group.getName() );
+        assertEquals( 0, group.getMembers().size() );
+        members.add( "testUser0" );
+        assertEquals( 0, group.getMembers().size() );
+        
+        GroupModifier modifier = new GroupModifier( null, group );
+        assertTrue( modifier.isUpdatableEntry() );
+        assertFalse( modifier.isNewEntry() );
+        assertFalse( modifier.isUpdateNeeded() );
+        assertTrue( modifier.isValid() );
+        modifier.addMember( "testUser0" );
+        assertTrue( modifier.isUpdateNeeded() );
+        modifier.removeMember( "testUser0" );
+        assertFalse( modifier.isUpdateNeeded() );
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/IntegrationTest.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/IntegrationTest.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/IntegrationTest.java (added)
+++ directory/trunks/triplesec/admin-api/src/test/java/org/safehaus/triplesec/admin/IntegrationTest.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,689 @@
+/*
+ *  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.admin;
+
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.safehaus.triplesec.admin.dao.DaoFactory;
+import org.safehaus.triplesec.admin.dao.PermissionDao;
+import org.safehaus.triplesec.admin.dao.ldap.LdapDaoFactory;
+import org.safehaus.triplesec.admin.dao.ldap.LdapPermissionDao;
+import org.safehaus.triplesec.integration.TriplesecIntegration;
+
+
+
+/**
+ * Test cases for JNDI data access object and state factories.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class IntegrationTest extends TriplesecIntegration
+{
+    private Properties props;
+    private DaoFactory factory;
+    private DirContext ctx;
+    private TriplesecAdmin admin;
+
+    
+    public IntegrationTest() throws Exception
+    {
+        super();
+    }
+
+    
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        
+        props = new Properties();
+        props.setProperty( DaoFactory.IMPLEMENTATION_CLASS, LdapDaoFactory.class.getName() );
+        props.setProperty( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        props.setProperty( "java.naming.provider.url", "ldap://localhost:" + getLdapPort() + "/dc=example,dc=com" );
+        props.setProperty( "java.naming.security.principal", "uid=admin,ou=system" );
+        props.setProperty( "java.naming.security.credentials", "secret" );
+        props.setProperty( "java.naming.security.authentication", "simple" );
+
+        factory = DaoFactory.createInstance( props );
+        ctx = new InitialDirContext( props );
+        admin = new TriplesecAdmin( props );
+        
+        assertNotNull( factory );
+    }
+
+    
+    /**
+     * Tests the following {@link LdapPermissionDao} methods:
+     * 
+     * <ul>
+     *   <li>{@link PermissionDao#add(String, String, String)}</li>
+     *   <li>{@link PermissionDao#delete(String, String)}</li>
+     *   <li>{@link PermissionDao#load(String, String)}</li>
+     *   <li>{@link PermissionDao#modify(String, String, String, ModificationItem[])}</li>
+     *   <li>{@link PermissionDao#rename(String, Permission)}</li>
+     *   <li>{@link PermissionDao#permissionIterator(String)}</li>
+     *   <li>{@link PermissionDao#permissionNameIterator(String)}</li>
+     * </ul>
+     */
+    public void testPermissionDao() throws Exception
+    {
+        PermissionDao dao = factory.getPermissionDao();
+        
+        // add a permission via add( String, String )
+        dao.add( "mockApplication", getName() + "0", null );
+        Attributes attrs = ctx.getAttributes( "permName=" + getName() 
+            + "0, ou=permissions, appName=mockApplication, ou=applications" );
+        assertEquals( getName() + "0", ( String ) attrs.get( "permName" ).get() );
+        assertNull( attrs.get( "description" ) );
+        
+        // add a permission via add( String, Permission )
+        PermissionModifier modifier = new PermissionModifier( dao, "mockApplication", getName() + "1" ); 
+        modifier.setDescription( "a non-null description" ).add();
+        attrs = ctx.getAttributes( "permName=" + getName() 
+            + "1, ou=permissions, appName=mockApplication, ou=applications" );
+        assertEquals( getName() + "1", ( String ) attrs.get( "permName" ).get() );
+        assertNotNull( attrs.get( "description" ) );
+        assertEquals( "a non-null description", ( String ) attrs.get( "description" ).get() );
+        dao.delete( "mockApplication", getName() + "1" );
+
+        // test the lookup of the newly added permission
+        Permission permission = dao.load( "mockApplication", getName() + "0" );
+        assertNotNull( permission );
+        assertEquals( getName() + "0", permission.getName() );
+        assertEquals( null, permission.getDescription() );
+        
+        // test the modification of the newly added permission
+        modifier = permission.modifier().setDescription( "updated description" );
+        modifier.modify();
+        permission = dao.load( "mockApplication", getName() + "0" );
+        assertNotNull( permission );
+        assertEquals( getName() + "0", permission.getName() );
+        assertEquals( "updated description", permission.getDescription() );
+        
+        // test the rename of the updated permission 
+        permission = dao.rename( getName()+ "0renamed", permission );
+        assertNotNull( permission );
+        assertEquals( getName() + "0renamed", permission.getName() );
+        assertEquals( "updated description", permission.getDescription() );
+        permission = dao.load( "mockApplication", getName()+ "0renamed" );
+        assertNotNull( permission );
+        assertEquals( getName() + "0renamed", permission.getName() );
+        assertEquals( "updated description", permission.getDescription() );
+        
+        // test the delete of the newly added permission
+        dao.delete( "mockApplication", getName() + "0renamed" );
+        try
+        {
+            permission = dao.load( "mockApplication", getName() + "0renamed" );
+            fail( "should never get here" );
+        }
+        catch ( NoSuchEntryException e )
+        {
+        }
+        
+        // test the permissionNameIterator() method 
+        Iterator iterator = dao.permissionNameIterator( "mockApplication" );
+        Set permNames = new HashSet();
+        while( iterator.hasNext() )
+        {
+            permNames.add( iterator.next() );
+        }
+        assertEquals( 10, permNames.size() );
+        assertFalse( permNames.contains( "bogus" ) );
+        assertTrue( permNames.contains( "mockPerm0" ) );
+        assertTrue( permNames.contains( "mockPerm1" ) );
+        assertTrue( permNames.contains( "mockPerm2" ) );
+        assertTrue( permNames.contains( "mockPerm3" ) );
+        assertTrue( permNames.contains( "mockPerm4" ) );
+        assertTrue( permNames.contains( "mockPerm5" ) );
+        assertTrue( permNames.contains( "mockPerm6" ) );
+        assertTrue( permNames.contains( "mockPerm7" ) );
+        assertTrue( permNames.contains( "mockPerm8" ) );
+        assertTrue( permNames.contains( "mockPerm9" ) );
+        
+        // test the permissionIterator() method
+        iterator = dao.permissionIterator( "mockApplication" );
+        Set perms = new HashSet();
+        while( iterator.hasNext() )
+        {
+            permission = ( Permission ) iterator.next();
+            perms.add( permission );
+            assertTrue( permNames.contains( permission.getName() ) );
+        }
+        assertEquals( 10, perms.size() );
+    }
+    
+    
+    public void testApplicationDao() throws Exception
+    {
+        // get and modify the application
+        Application app = admin.getApplication( "mockApplication" );
+        assertNotNull( app );
+        assertEquals( "mockApplication", app.getName() );
+        assertEquals( null, app.getDescription() );
+        assertEquals( "testing", app.getPassword() );
+        app = app.modifier().setDescription( "test" ).modify();
+        assertEquals( "test", app.getDescription() );
+        
+        // create a new application
+        app = admin.newApplication( "test" ).add();
+        app = app.modifier().setDescription( "test" ).setPassword( "secret" ).modify();
+        assertNotNull( app );
+        assertEquals( "test", app.getName() );
+        assertEquals( "test", app.getDescription() );
+        assertEquals( "secret", app.getPassword() );
+        app = admin.getApplication( "test" );
+        assertNotNull( app );
+        assertEquals( "test", app.getName() );
+        assertEquals( "test", app.getDescription() );
+        assertEquals( "secret", app.getPassword() );
+        
+        // rename the new application
+        app = app.modifier().rename( "newName" );
+        app = admin.getApplication( "newName" );
+        assertNotNull( app );
+        assertEquals( "newName", app.getName() );
+        assertEquals( "test", app.getDescription() );
+        assertEquals( "secret", app.getPassword() );
+        
+        // create a permission for the new application
+        Permission perm = app.modifier().newPermission( "testPerm" ).
+            setDescription( "test description" ).add();
+        assertEquals( "newName", perm.getApplicationName() );
+        assertEquals( "testPerm", perm.getName() );
+        assertEquals( "test description", perm.getDescription() );    
+        perm = app.getPermission( perm.getName() );
+        assertEquals( "newName", perm.getApplicationName() );
+        assertEquals( "testPerm", perm.getName() );
+        assertEquals( "test description", perm.getDescription() );    
+        
+        // delete the permission and make sure it's not there
+        perm.modifier().delete();
+        try
+        {
+            app.getPermission( perm.getName() );
+            fail( "should never get here" );
+        }
+        catch( DataAccessException e )
+        {
+        }
+        
+        // delete the application and make sure it's not there
+        app.modifier().delete();
+        try
+        {
+            admin.getApplication( "test" );
+        }
+        catch( DataAccessException e )
+        {
+        }
+    }
+    
+    
+    public void testProfileIteration() throws Exception
+    {
+        Application app = admin.getApplication( "mockApplication" );
+        app.modifier().newProfile( "extra", "lockedout" ).add();
+        for ( Iterator ii = app.profileIterator( "akarasulu" ); ii.hasNext(); /**/ )
+        {
+            Profile profile = ( Profile ) ii.next(); 
+            assertEquals( "akarasulu", profile.getUser() );
+        }
+        for ( Iterator ii = app.profileIterator( "extra" ); ii.hasNext(); /**/ )
+        {
+            Profile profile = ( Profile ) ii.next();
+            assertEquals( "extra", profile.getUser() );
+        }
+    }
+    
+    
+    public void testRoleDao() throws Exception
+    {
+        Application app = admin.getApplication( "mockApplication" );
+        
+        // create a new role after changing modifier's description and grants
+        Role role = app.modifier().newRole( "testRole" ).setDescription( "test role" )
+            .addGrant( "mockPerm0" ).addGrant( "mockPerm1" ).add();
+        assertNotNull( role );
+        assertEquals( "mockApplication", role.getApplicationName() );
+        assertEquals( "testRole", role.getName() );
+        assertEquals( "test role", role.getDescription() );
+        assertEquals( 2, role.getGrants().size() );
+        assertTrue( role.getGrants().contains( "mockPerm0" ) );
+        assertTrue( role.getGrants().contains( "mockPerm1" ) );
+        assertFalse( role.getGrants().contains( "bogus" ) );
+        
+        // lookup and confirm values again
+        role = app.getRole( "testRole" );
+        assertNotNull( role );
+        assertEquals( "mockApplication", role.getApplicationName() );
+        assertEquals( "testRole", role.getName() );
+        assertEquals( "test role", role.getDescription() );
+        assertEquals( 2, role.getGrants().size() );
+        assertTrue( role.getGrants().contains( "mockPerm0" ) );
+        assertTrue( role.getGrants().contains( "mockPerm1" ) );
+        assertFalse( role.getGrants().contains( "bogus" ) );
+        
+        // remove existing grant, add two new ones, and modify
+        role = role.modifier().removeGrant( "mockPerm1" ).addGrant( "mockPerm2" )
+            .addGrant( "mockPerm3" ).setDescription( "changed description" ).modify();
+        assertNotNull( role );
+        assertEquals( "changed description", role.getDescription() );
+        assertEquals( 3, role.getGrants().size() );
+        assertTrue( role.getGrants().contains( "mockPerm0" ) );
+        assertTrue( role.getGrants().contains( "mockPerm2" ) );
+        assertTrue( role.getGrants().contains( "mockPerm3" ) );
+        assertFalse( role.getGrants().contains( "bogus" ) );
+        
+        // rename the role, test values, look it up again and test values again
+        role = role.modifier().rename( "renamedRole" );
+        assertNotNull( role );
+        assertEquals( "mockApplication", role.getApplicationName() );
+        assertEquals( "renamedRole", role.getName() );
+        assertEquals( "changed description", role.getDescription() );
+        assertEquals( 3, role.getGrants().size() );
+        assertTrue( role.getGrants().contains( "mockPerm0" ) );
+        assertTrue( role.getGrants().contains( "mockPerm2" ) );
+        assertTrue( role.getGrants().contains( "mockPerm3" ) );
+        assertFalse( role.getGrants().contains( "bogus" ) );
+
+        role = app.getRole( "renamedRole" );
+        assertNotNull( role );
+        assertEquals( "mockApplication", role.getApplicationName() );
+        assertEquals( "renamedRole", role.getName() );
+        assertEquals( "changed description", role.getDescription() );
+        assertEquals( 3, role.getGrants().size() );
+        assertTrue( role.getGrants().contains( "mockPerm0" ) );
+        assertTrue( role.getGrants().contains( "mockPerm2" ) );
+        assertTrue( role.getGrants().contains( "mockPerm3" ) );
+        assertFalse( role.getGrants().contains( "bogus" ) );
+        
+        // delete the role
+        role.modifier().delete();
+        try
+        {
+            app.getRole( "renamedRole" );
+            fail( "should never get here" );
+        }
+        catch( NoSuchEntryException e )
+        {
+        }
+    }
+
+
+    public void testProfileDao() throws Exception
+    {
+        Application app = admin.getApplication( "mockApplication" );
+        
+        // create a new profile after changing modifier's description with permission and roles
+        Profile profile = app.modifier().newProfile( "testProfile", "testUser" ).setDescription( "test profile" )
+            .addGrant( "mockPerm0" ).addGrant( "mockPerm1" ).addDenial( "mockPerm4" ).addRole( "mockRole2" ).add();
+        assertNotNull( profile );
+        assertEquals( "mockApplication", profile.getApplicationName() );
+        assertEquals( "testProfile", profile.getId() );
+        assertEquals( "testUser", profile.getUser() );
+        assertEquals( "test profile", profile.getDescription() );
+        assertEquals( 2, profile.getGrants().size() );
+        assertTrue( profile.getGrants().contains( "mockPerm0" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm1" ) );
+        assertFalse( profile.getGrants().contains( "bogus" ) );
+        assertEquals( 1, profile.getDenials().size() );
+        assertTrue( profile.getDenials().contains( "mockPerm4" ) );
+        assertFalse( profile.getDenials().contains( "bogus" ) );
+        assertEquals( 1, profile.getRoles().size() );
+        assertTrue( profile.getRoles().contains( "mockRole2" ) );
+        assertFalse( profile.getRoles().contains( "bogus" ) );
+        
+        
+        // lookup and confirm values again
+        profile = app.getProfile( "testProfile" );
+        assertNotNull( profile );
+        assertEquals( "mockApplication", profile.getApplicationName() );
+        assertEquals( "testProfile", profile.getId() );
+        assertEquals( "testUser", profile.getUser() );
+        assertEquals( "test profile", profile.getDescription() );
+        assertEquals( 2, profile.getGrants().size() );
+        assertTrue( profile.getGrants().contains( "mockPerm0" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm1" ) );
+        assertFalse( profile.getGrants().contains( "bogus" ) );
+        assertEquals( 1, profile.getDenials().size() );
+        assertTrue( profile.getDenials().contains( "mockPerm4" ) );
+        assertFalse( profile.getDenials().contains( "bogus" ) );
+        assertEquals( 1, profile.getRoles().size() );
+        assertTrue( profile.getRoles().contains( "mockRole2" ) );
+        assertFalse( profile.getRoles().contains( "bogus" ) );
+        
+        // remove existing grant, add two new ones, remove existing grant, add a role and modify
+        profile = profile.modifier().removeGrant( "mockPerm1" ).addGrant( "mockPerm2" )
+            .addGrant( "mockPerm3" ).removeDenial( "mockPerm4" ).addRole( "mockRole3" )
+            .setDescription( "changed description" ).modify();
+        assertNotNull( profile );
+        assertEquals( "changed description", profile.getDescription() );
+        assertEquals( 0, profile.getDenials().size() );
+        assertEquals( 3, profile.getGrants().size() );
+        assertTrue( profile.getGrants().contains( "mockPerm0" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm2" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm3" ) );
+        assertFalse( profile.getGrants().contains( "bogus" ) );
+        assertEquals( 2, profile.getRoles().size() );
+        assertTrue( profile.getRoles().contains( "mockRole2" ) );
+        assertTrue( profile.getRoles().contains( "mockRole3" ) );
+        assertFalse( profile.getRoles().contains( "bogus" ) );
+        
+        // rename the profile, test values, look it up again and test values again
+        profile = profile.modifier().rename( "renamedProfile" );
+        assertNotNull( profile );
+        assertEquals( "mockApplication", profile.getApplicationName() );
+        assertEquals( "renamedProfile", profile.getId() );
+        assertEquals( "testUser", profile.getUser() );
+        assertEquals( "changed description", profile.getDescription() );
+        assertEquals( 0, profile.getDenials().size() );
+        assertEquals( 3, profile.getGrants().size() );
+        assertTrue( profile.getGrants().contains( "mockPerm0" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm2" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm3" ) );
+        assertFalse( profile.getGrants().contains( "bogus" ) );
+        assertEquals( 2, profile.getRoles().size() );
+        assertTrue( profile.getRoles().contains( "mockRole2" ) );
+        assertTrue( profile.getRoles().contains( "mockRole3" ) );
+        assertFalse( profile.getRoles().contains( "bogus" ) );
+
+        profile = app.getProfile( "renamedProfile" );
+        assertNotNull( profile );
+        assertEquals( "mockApplication", profile.getApplicationName() );
+        assertEquals( "renamedProfile", profile.getId() );
+        assertEquals( "testUser", profile.getUser() );
+        assertEquals( "changed description", profile.getDescription() );
+        assertEquals( 0, profile.getDenials().size() );
+        assertEquals( 3, profile.getGrants().size() );
+        assertTrue( profile.getGrants().contains( "mockPerm0" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm2" ) );
+        assertTrue( profile.getGrants().contains( "mockPerm3" ) );
+        assertFalse( profile.getGrants().contains( "bogus" ) );
+        assertEquals( 2, profile.getRoles().size() );
+        assertTrue( profile.getRoles().contains( "mockRole2" ) );
+        assertTrue( profile.getRoles().contains( "mockRole3" ) );
+        assertFalse( profile.getRoles().contains( "bogus" ) );
+        
+        // delete the profile
+        profile.modifier().delete();
+        try
+        {
+            app.getProfile( "renamedProfile" );
+            fail( "should never get here" );
+        }
+        catch( NoSuchEntryException e )
+        {
+        }
+    }
+    
+    
+    private String getKerberosKeyAsString( String id, String realm, String password ) throws Exception
+    {
+        StringBuffer buf = new StringBuffer();
+        String krb5PrincipalName = buf.append( id ).append( "@" ).append( realm.toUpperCase() ).toString();
+        buf.setLength( 0 );
+        KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName );
+        KerberosKey key = new KerberosKey( kerberosPrincipal, password.toCharArray(), "DES" );
+        return StringTools.utf8ToString( key.getEncoded() );
+    }
+    
+    
+    public void testLocalUserDao() throws Exception
+    {
+        assertFalse( admin.hasUser( "testLocalUser" ) );
+        LocalUserModifier modifier = admin.newLocalUser( "testLocalUser", "joe", "smith", "secret" );
+        modifier.setAddress1( "address1" ).setAddress2( "address2" ).setCity( "city" ).
+            setCompany( "company" ).setCountry( "country" ).setStateProvRegion( "stateProvRegion" )
+            .setZipPostalCode( "zipPostalCode" ).setEmail( "email" ).add();
+        assertTrue( admin.hasUser( "testLocalUser" ) );
+        
+        LocalUser user = ( LocalUser ) admin.getUser( "testLocalUser" );
+        assertNotNull( user );
+        assertEquals( "testLocalUser", user.getId() );
+        assertEquals( "joe", user.getFirstName() );
+        assertEquals( "smith", user.getLastName() );
+        assertEquals( "secret", user.getPassword() );
+        assertEquals( "address1", user.getAddress1() );
+        assertEquals( "address2", user.getAddress2() );
+        assertEquals( "city", user.getCity() );
+        assertEquals( "stateProvRegion", user.getStateProvRegion() );
+        assertEquals( "company", user.getCompany() );
+        assertEquals( "country", user.getCountry() );
+        assertEquals( "zipPostalCode", user.getZipPostalCode() );
+        assertEquals( "email", user.getEmail() );
+        assertFalse( user.isDisabled() );
+        
+        // now let's check and make sure the kerberos key is properly set
+        Attributes attrs = this.ctx.getAttributes( "uid=testLocalUser,ou=Users" );
+        assertEquals( getKerberosKeyAsString( "testLocalUser", "EXAMPLE.COM", "secret" ), 
+            attrs.get( "krb5Key" ).get() );
+        
+        user = user.modifier().setDescription( "test description" )
+            .setDisabled( true ).modify();
+        assertEquals( "test description", user.getDescription() );
+        assertEquals( "secret", user.getPassword() );
+        assertTrue( user.isDisabled() ); 
+        
+        user = user.modifier().setPassword( "reset" )
+            .setAddress1( "address1-" ).setAddress2( "address2-" ).setCity( "city-" )
+            .setCompany( "company-" ).setCountry( "country-" ).setStateProvRegion( "stateProvRegion-" )
+            .setZipPostalCode( "zipPostalCode-" ).setEmail( "email-" ).modify();
+        assertEquals( "reset", user.getPassword() );
+        user = ( LocalUser ) admin.getUser( "testLocalUser" );
+        assertNotNull( user );
+        assertEquals( "reset", user.getPassword() );
+        assertEquals( "address1-", user.getAddress1() );
+        assertEquals( "address2-", user.getAddress2() );
+        assertEquals( "city-", user.getCity() );
+        assertEquals( "stateProvRegion-", user.getStateProvRegion() );
+        assertEquals( "company-", user.getCompany() );
+        assertEquals( "country-", user.getCountry() );
+        assertEquals( "zipPostalCode-", user.getZipPostalCode() );
+        assertEquals( "email-", user.getEmail() );
+
+        // now let's check and make sure the kerberos key is properly set for the new password
+        attrs = this.ctx.getAttributes( "uid=testLocalUser,ou=Users" );
+        assertEquals( getKerberosKeyAsString( "testLocalUser", "EXAMPLE.COM", "reset" ), 
+            attrs.get( "krb5Key" ).get() );
+        assertEquals( "TRUE", attrs.get( Constants.KRB5_DISABLED_ID ).get() );
+
+        user = user.modifier().setDisabled( false ).modify();
+        attrs = this.ctx.getAttributes( "uid=testLocalUser,ou=Users" );
+        assertEquals( "FALSE", attrs.get( Constants.KRB5_DISABLED_ID ).get() );
+
+        user.modifier().delete();
+        assertFalse( admin.hasUser( "testLocalUser" ) );
+    }
+    
+    
+    public void testHauskeysUserDao() throws Exception
+    {
+        assertFalse( admin.hasUser( "testHauskeysUser" ) );
+        HauskeysUserModifier modifier = admin.newHauskeysUser( "testHauskeysUser", "joe", "smith", "secret" );
+        modifier.setAddress1( "address1" ).setAddress2( "address2" ).setCity( "city" ).
+        setCompany( "company" ).setCountry( "country" ).setStateProvRegion( "stateProvRegion" )
+        .setZipPostalCode( "zipPostalCode" ).setLabel( "testHauskeysUser" ).setNotifyBy( "sms" ).add();
+        assertTrue( admin.hasUser( "testHauskeysUser" ) );
+        
+        HauskeysUser user = ( HauskeysUser ) admin.getUser( "testHauskeysUser" );
+        assertNotNull( user );
+        assertEquals( "testHauskeysUser", user.getId() );
+        assertEquals( "joe", user.getFirstName() );
+        assertEquals( "smith", user.getLastName() );
+        assertEquals( "secret", user.getPassword() );
+        assertEquals( "secret", user.getPassword() );
+        assertEquals( "address1", user.getAddress1() );
+        assertEquals( "address2", user.getAddress2() );
+        assertEquals( "city", user.getCity() );
+        assertEquals( "stateProvRegion", user.getStateProvRegion() );
+        assertEquals( "company", user.getCompany() );
+        assertEquals( "country", user.getCountry() );
+        assertEquals( "zipPostalCode", user.getZipPostalCode() );
+        assertFalse( user.isDisabled() );
+        
+        // now let's check and make sure the kerberos key is properly set
+        Attributes attrs = this.ctx.getAttributes( "uid=testHauskeysUser,ou=Users" );
+        assertEquals( getKerberosKeyAsString( "testHauskeysUser", "EXAMPLE.COM", "secret" ), 
+            attrs.get( "krb5Key" ).get() );
+        
+        user = user.modifier().setDescription( "test description" )
+            .setDisabled( true ).modify();
+        assertEquals( "test description", user.getDescription() );
+        assertEquals( "secret", user.getPassword() );
+        assertTrue( user.isDisabled() ); 
+        
+        user = user.modifier().setPassword( "reset" )
+            .setAddress1( "address1-" ).setAddress2( "address2-" ).setCity( "city-" )
+            .setCompany( "company-" ).setCountry( "country-" ).setStateProvRegion( "stateProvRegion-" )
+            .setZipPostalCode( "zipPostalCode-" ).modify();
+        assertEquals( "reset", user.getPassword() );
+        user = ( HauskeysUser ) admin.getUser( "testHauskeysUser" );
+        assertNotNull( user );
+        assertEquals( "reset", user.getPassword() );
+        assertEquals( "address1-", user.getAddress1() );
+        assertEquals( "address2-", user.getAddress2() );
+        assertEquals( "city-", user.getCity() );
+        assertEquals( "stateProvRegion-", user.getStateProvRegion() );
+        assertEquals( "company-", user.getCompany() );
+        assertEquals( "country-", user.getCountry() );
+        assertEquals( "zipPostalCode-", user.getZipPostalCode() );
+
+        // now let's check and make sure the kerberos key is properly set for the new password
+        attrs = this.ctx.getAttributes( "uid=testHauskeysUser,ou=Users" );
+        assertEquals( getKerberosKeyAsString( "testHauskeysUser", "EXAMPLE.COM", "reset" ), 
+            attrs.get( "krb5Key" ).get() );
+        assertEquals( "TRUE", attrs.get( Constants.KRB5_DISABLED_ID ).get() );
+
+        user = user.modifier().setDisabled( false ).modify();
+        attrs = this.ctx.getAttributes( "uid=testHauskeysUser,ou=Users" );
+        assertEquals( "FALSE", attrs.get( Constants.KRB5_DISABLED_ID ).get() );
+
+        user.modifier().delete();
+        assertFalse( admin.hasUser( "testHauskeysUser" ) );
+    }
+    
+    
+    public void testExternalUserDao() throws Exception
+    {
+        final String uid = "testExternalUser";
+        final String referral = "ldap://localhost:10389/uid=someExternalUser,ou=Users,dc=example,dc=com";
+        assertFalse( admin.hasUser( uid ) );
+        ExternalUserModifier modifier = admin.newExternalUser( uid, referral );
+        modifier.add();
+        assertTrue( admin.hasUser( uid ) );
+        
+        ExternalUser user = ( ExternalUser ) admin.getUser( uid );
+        assertNotNull( user );
+        assertEquals( uid, user.getId() );
+        assertEquals( referral, user.getReferral() );
+        assertFalse( user.isDisabled() );
+        
+        user = user.modifier().setDescription( "test description" )
+            .setDisabled( true ).modify();
+        assertEquals( "test description", user.getDescription() );
+        assertTrue( user.isDisabled() ); 
+        
+        // now let's check and make sure the kerberos key is properly set for the new password
+        Attributes attrs = this.ctx.getAttributes( "uid=" + uid + ",ou=Users" );
+        assertEquals( "TRUE", attrs.get( Constants.KRB5_DISABLED_ID ).get() );
+
+        user = user.modifier().setDisabled( false ).modify();
+        attrs = this.ctx.getAttributes( "uid=" + uid + ",ou=Users" );
+        assertEquals( "FALSE", attrs.get( Constants.KRB5_DISABLED_ID ).get() );
+        
+        user.modifier().delete();
+        assertFalse( admin.hasUser( uid ) );
+    }
+    
+    
+    public void testGroupDao() throws Exception
+    {
+        GroupModifier modifier = admin.newGroup( "testGroup", "firstMember" );
+        modifier.add();
+        
+        Group group = admin.getGroup( "testGroup" );
+        assertEquals( "testGroup", group.getName() );
+        assertEquals( 1, group.getMembers().size() );
+        assertTrue( group.getMembers().contains( "firstMember" ) );
+        
+        group = group.modifier().addMember( "secondMember" ).addMember( "thirdMember" ).modify();
+        assertEquals( "testGroup", group.getName() );
+        assertEquals( 3, group.getMembers().size() );
+        assertTrue( group.getMembers().contains( "firstMember" ) );
+        assertTrue( group.getMembers().contains( "secondMember" ) );
+        assertTrue( group.getMembers().contains( "thirdMember" ) );
+        
+        group = group.modifier().removeMember( "secondMember" ).modify();
+        assertEquals( "testGroup", group.getName() );
+        assertEquals( 2, group.getMembers().size() );
+        assertTrue( group.getMembers().contains( "firstMember" ) );
+        assertTrue( group.getMembers().contains( "thirdMember" ) );
+        
+        group = group.modifier().addMember( "secondMember" ).removeMember( "thirdMember" ).modify();
+        assertEquals( 2, group.getMembers().size() );
+        assertTrue( group.getMembers().contains( "firstMember" ) );
+        assertTrue( group.getMembers().contains( "secondMember" ) );
+        
+        group = group.modifier().rename( "renamedGroup" );
+        assertEquals( "renamedGroup", group.getName() );
+        assertEquals( 2, group.getMembers().size() );
+        assertTrue( group.getMembers().contains( "firstMember" ) );
+        assertTrue( group.getMembers().contains( "secondMember" ) );
+        group = admin.getGroup( "renamedGroup" );
+        assertEquals( "renamedGroup", group.getName() );
+        assertEquals( 2, group.getMembers().size() );
+        assertTrue( group.getMembers().contains( "firstMember" ) );
+        assertTrue( group.getMembers().contains( "secondMember" ) );
+        try
+        {
+            group = admin.getGroup( "testGroup" );
+            fail();
+        }
+        catch ( NoSuchEntryException e )
+        {
+            assertEquals( "renamedGroup", group.getName() );
+            assertEquals( 2, group.getMembers().size() );
+            assertTrue( group.getMembers().contains( "firstMember" ) );
+            assertTrue( group.getMembers().contains( "secondMember" ) );
+        }
+        
+        group.modifier().delete();
+        try
+        {
+            group = admin.getGroup( "renamedGroup" );
+            fail();
+        }
+        catch ( NoSuchEntryException e )
+        {
+        }        
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/test/resources/log4j.properties?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/test/resources/log4j.properties (added)
+++ directory/trunks/triplesec/admin-api/src/test/resources/log4j.properties Tue Dec 12 07:23:31 2006
@@ -0,0 +1,18 @@
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+
+# with these we'll not get innundated when switching to DEBUG
+log4j.logger.org.apache.directory.shared.ldap.name=WARN
+log4j.logger.org.apache.directory.server.core.schema=ERROR
+log4j.logger.org.springframework=WARN
+log4j.logger.org.apache.directory.shared.codec=WARN
+log4j.logger.org.apache.directory.shared.asn1=WARN
+log4j.logger.org.apache.commons.digester.Digester=WARN
+log4j.logger.org.mortbay=WARN
+log4j.logger.org.apache.jasper=WARN
+log4j.logger.wicket=WARN
+log4j.logger.org.apache.commons=WARN
+log4j.logger.org.apache.myfaces=WARN