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 [4/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/SingleValuedField.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/SingleValuedField.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/SingleValuedField.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/SingleValuedField.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,108 @@
+/*
+ *  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 javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+
+public class SingleValuedField
+{
+    private final String id;    
+    private final String initial;
+    private String current;
+    
+    
+    public SingleValuedField( String id, String initial )
+    {
+        this.id = id;
+        this.initial = initial;
+        this.current = initial;
+    }
+    
+    
+    public String getInitialValue()
+    {
+        return initial;
+    }
+
+
+    public String getCurrentValue()
+    {
+        return current;
+    }
+
+
+    public boolean isUpdateNeeded()
+    {
+        if ( initial != null )
+        {
+            return ! initial.equals( current );
+        }
+        
+        return current != null;
+    }
+    
+    
+    public boolean setValue( String value )
+    {
+        if ( current != null && current.equals( value ) )
+        {
+            return false;
+        }
+        
+        // ignore replacing null with emtpy string which causes unnecesary noise
+        if ( current == null && value != null && value.length() == 0 )
+        {
+            return false;
+        }
+        
+        current = value;
+        return true;
+    }
+    
+    
+    public ModificationItem getModificationItem()
+    {
+        if ( initial == current )
+        {
+            return null;
+        }
+        
+        if ( initial == null && current != null )
+        {
+            return new ModificationItem( DirContext.ADD_ATTRIBUTE, new BasicAttribute( id, current ) );
+        }
+        
+        if ( initial != null && current == null )
+        {
+            return new ModificationItem( DirContext.REMOVE_ATTRIBUTE, new BasicAttribute( id ) );
+        }
+        
+        if ( ! initial.equals( current ) ) 
+        {
+            return new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute( id, current ) );
+        }
+        
+        return null;
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/TriplesecAdmin.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/TriplesecAdmin.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/TriplesecAdmin.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/TriplesecAdmin.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,204 @@
+/*
+ *  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 org.safehaus.triplesec.admin.dao.ApplicationDao;
+import org.safehaus.triplesec.admin.dao.DaoFactory;
+import org.safehaus.triplesec.admin.dao.ExternalUserDao;
+import org.safehaus.triplesec.admin.dao.GroupDao;
+import org.safehaus.triplesec.admin.dao.HauskeysUserDao;
+import org.safehaus.triplesec.admin.dao.LocalUserDao;
+import org.safehaus.triplesec.admin.dao.PermissionDao;
+import org.safehaus.triplesec.admin.dao.ProfileDao;
+import org.safehaus.triplesec.admin.dao.RoleDao;
+import org.safehaus.triplesec.admin.dao.UserDao;
+
+ 
+public class TriplesecAdmin
+{
+    private DaoFactory factory;
+    private ApplicationDao applicationDao;
+    private RoleDao roleDao;
+    private ProfileDao profileDao;
+    private PermissionDao permissionDao;
+    private GroupDao groupDao;
+    private ExternalUserDao externalUserDao;
+    private LocalUserDao localUserDao;
+    private HauskeysUserDao hauskeysUserDao;
+    private UserDao userDao;
+    
+    
+    public TriplesecAdmin( Properties props ) throws DataAccessException
+    {
+        factory = DaoFactory.createInstance( props );
+        applicationDao = factory.getApplicationDao();
+        permissionDao = factory.getPermissionDao();
+        roleDao = factory.getRoleDao();
+        profileDao = factory.getProfileDao();
+        groupDao = factory.getGroupDao();
+        externalUserDao = factory.getExternalUserDao();
+        localUserDao = factory.getLocalUserDao();
+        hauskeysUserDao = factory.getHauskeysUserDao();
+        userDao = factory.getUserDao();
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // User & Respective Modifier Read Operations
+    // -----------------------------------------------------------------------
+    
+    
+    public ExternalUserModifier newExternalUser( String id, String referral )
+    {
+        return new ExternalUserModifier( externalUserDao, id, referral );
+    }
+    
+    
+    public LocalUserModifier newLocalUser( String id, String firstName, String lastName, String password )
+    {
+        return new LocalUserModifier( localUserDao, id, firstName, lastName, password );
+    }
+    
+    
+    public HauskeysUserModifier newHauskeysUser( String id, String firstName, String lastName, String password )
+    {
+        return new HauskeysUserModifier( hauskeysUserDao, id, firstName, lastName, password );
+    }
+    
+    
+    public User getUser( String id ) throws DataAccessException
+    {
+        return userDao.load( id );
+    }
+    
+    
+    public boolean hasUser( String id ) throws DataAccessException
+    {
+        return userDao.hasUser( id );
+    }
+    
+    
+    public String getRandomUniqueActivationKey() throws DataAccessException
+    {
+        return userDao.getRandomUniqueActivationKey();
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Application and ApplicationModifier Read Operations
+    // -----------------------------------------------------------------------
+    
+    
+    public ApplicationModifier newApplication( String name )
+    {
+        return new ApplicationModifier( applicationDao, name, 
+            permissionDao, roleDao, profileDao );
+    }
+    
+    
+    public Application getApplication( String name ) throws DataAccessException
+    {
+        return applicationDao.load( name );
+    }
+    
+    
+    public boolean hasApplication( String name ) throws DataAccessException
+    {
+        return applicationDao.has( name );
+    }
+    
+
+    // -----------------------------------------------------------------------
+    // Group and GroupModifier Read Operations
+    // -----------------------------------------------------------------------
+    
+    
+    public GroupModifier newGroup( String name, String member )
+    {
+        Set members = new HashSet();
+        members.add( member );
+        return new GroupModifier( groupDao, name, members );
+    }
+    
+    
+    public GroupModifier newGroup( String name, Set members )
+    {
+        return new GroupModifier( groupDao, name, members );
+    }
+    
+    
+    public Group getGroup( String name ) throws DataAccessException
+    {
+        return groupDao.load( name );
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Iterator Access
+    // -----------------------------------------------------------------------
+
+    
+    public Iterator groupIterator() throws DataAccessException
+    {
+        return new ReadOnlyIterator( groupDao.iterator() );
+    }
+
+
+    public Iterator applicationIterator() throws DataAccessException
+    {
+        return new ReadOnlyIterator( applicationDao.applicationIterator() );
+    }
+
+
+    public Iterator externalUserIterator() throws DataAccessException
+    {
+        return new ReadOnlyIterator( externalUserDao.iterator() );
+    }
+
+
+    public Iterator localUserIterator() throws DataAccessException
+    {
+        return new ReadOnlyIterator( localUserDao.iterator() );
+    }
+
+
+    public Iterator hauskeysUserIterator() throws DataAccessException
+    {
+        return new ReadOnlyIterator( hauskeysUserDao.iterator() );
+    }
+    
+    
+    public Iterator userIterator() throws DataAccessException
+    {
+        return userDao.iterator();
+    }
+
+
+    public void close()
+    {
+        factory.close();
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/User.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/User.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/User.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/User.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,77 @@
+/*
+ *  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.Date;
+
+
+public abstract class User extends AdministeredEntity
+{
+    private final String id;
+    private final String description;
+    private final boolean disabled;
+    
+    
+    public User( String creatorsName, Date createTimestamp, String id, String description )
+    {
+        this( creatorsName, createTimestamp, null, null, id, description, false );
+    }
+    
+    
+    public User( String creatorsName, Date createTimestamp, String id, String description, boolean disabled )
+    {
+        this( creatorsName, createTimestamp, null, null, id, description, false );
+    }
+    
+    
+    public User( String creatorsName, Date createTimestamp, String modifiersName, 
+        Date modifyTimestamp, String id, String description, boolean disabled )
+    {
+        super( creatorsName, createTimestamp, modifiersName, modifyTimestamp );
+        this.id = id;
+        this.description = description;
+        this.disabled = disabled;
+    }
+
+
+    public String getId()
+    {
+        return id;
+    }
+
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+
+    public String toString()
+    {
+        return id;
+    }
+    
+    
+    public boolean isDisabled()
+    {
+        return disabled;
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ApplicationDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ApplicationDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ApplicationDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ApplicationDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,46 @@
+/*
+ *  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;
+
+
+import java.util.Iterator;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.Application;
+import org.safehaus.triplesec.admin.DataAccessException;
+
+
+public interface ApplicationDao
+{
+    Application add( String name, String description, String userPassword ) throws DataAccessException;
+    
+    Application modify( String name, ModificationItem[] mods ) throws DataAccessException;
+
+    Application load( String name ) throws DataAccessException;
+
+    Iterator applicationIterator() throws DataAccessException;
+    
+    void delete( Application application ) throws DataAccessException;
+
+    Application rename( Application application, String newName ) throws DataAccessException;
+
+    boolean has( String name ) throws DataAccessException;
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/DaoFactory.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/DaoFactory.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/DaoFactory.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/DaoFactory.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,162 @@
+/*
+ *  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;
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+import org.safehaus.triplesec.admin.ConfigurationException;
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public abstract class DaoFactory
+{
+    public static final String IMPLEMENTATION_CLASS = DaoFactory.class.getName();
+
+    private static final Class[] CONSTRUCTOR_TYPES = new Class[] { Properties.class };
+    private static final Logger log = LoggerFactory.getLogger( DaoFactory.class );
+
+    
+    public static DaoFactory createInstance( Properties props ) throws DataAccessException
+    {
+        // -------------------------------------------------------------------
+        // Error check settings
+        // -------------------------------------------------------------------
+        
+        if ( !props.containsKey( IMPLEMENTATION_CLASS ) )
+        {
+            String msg = "Implementation class property not found: " + IMPLEMENTATION_CLASS;
+            log.error( msg );
+            throw new ConfigurationException( msg );
+        }
+        String className = null;
+        className = props.getProperty( IMPLEMENTATION_CLASS );
+        if ( className == null || className.length() == 0 )
+        {
+            String msg = "No value for implementation class property: " + IMPLEMENTATION_CLASS;
+            log.error( msg );
+            throw new ConfigurationException( msg );
+        }
+        
+        // -------------------------------------------------------------------
+        // Lookup the implementation class
+        // -------------------------------------------------------------------
+        
+        Class clazz = null;
+        try
+        {
+            clazz = Class.forName( className );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            String msg = "Failed to find dao factory implementation class";
+            log.error( msg, e );
+            throw new ConfigurationException( msg );
+        }
+        
+        // -------------------------------------------------------------------
+        // Lookup the constructor which takes Properties argument
+        // -------------------------------------------------------------------
+        
+        Constructor constructor = null;
+        try
+        {
+            constructor = clazz.getConstructor( CONSTRUCTOR_TYPES );
+        }
+        catch ( SecurityException e )
+        {
+            String msg = "Lack perms to reflect on constructor";
+            log.error( msg, e );
+            throw new ConfigurationException( msg );
+        }
+        catch ( NoSuchMethodException e )
+        {
+            String msg = "No constructor with single Properties argument.";
+            log.error( msg, e );
+            throw new ConfigurationException( msg );
+        }
+
+        // -------------------------------------------------------------------
+        // Create new instance of DAO
+        // -------------------------------------------------------------------
+        
+        try
+        {
+            return ( DaoFactory ) constructor.newInstance( new Object[] { props } );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            // should never happen
+            String msg = "Impropert argument type";
+            log.error( msg, e );
+            throw new IllegalStateException( msg );
+        }
+        catch ( InstantiationException e )
+        {
+            String msg = "Implementation class " + className 
+                + " must be a concrete class to instantiate it";
+            log.error( msg, e );
+            throw new ConfigurationException( msg );
+        }
+        catch ( IllegalAccessException e )
+        {
+            String msg = className + " constructor must be public";
+            log.error( msg, e );
+            throw new ConfigurationException( msg );
+        }
+        catch ( InvocationTargetException e )
+        {
+            String msg = className + "'s constructor threw an exception during instantiation";
+            log.error( msg, e );
+            
+            if ( e.getCause() instanceof DataAccessException )
+            {
+                throw ( DataAccessException ) e.getCause();
+            }
+            
+            throw new ConfigurationException( msg );
+        }
+    }
+    
+    
+    public abstract PermissionDao getPermissionDao() throws DataAccessException;
+    
+    public abstract ApplicationDao getApplicationDao() throws DataAccessException;
+
+    public abstract RoleDao getRoleDao() throws DataAccessException;
+
+    public abstract ProfileDao getProfileDao() throws DataAccessException;
+
+    public abstract GroupDao getGroupDao() throws DataAccessException;
+
+    public abstract ExternalUserDao getExternalUserDao() throws DataAccessException;
+    
+    public abstract LocalUserDao getLocalUserDao() throws DataAccessException;
+    
+    public abstract HauskeysUserDao getHauskeysUserDao() throws DataAccessException;
+    
+    public abstract UserDao getUserDao() throws DataAccessException;
+
+    public abstract void close();
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ExternalUserDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ExternalUserDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ExternalUserDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ExternalUserDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,47 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.ExternalUser;
+
+
+public interface ExternalUserDao
+{
+    public ExternalUser add( String id, String description, String referral ) throws DataAccessException;
+
+    public ExternalUser rename( String newId, ExternalUser archetype ) throws DataAccessException;
+
+    public ExternalUser modify( String creatorsName, Date createTimestamp, String id, 
+        String description, String referral, boolean disabled, ModificationItem[] mods ) throws DataAccessException;
+
+    public void delete( String id ) throws DataAccessException;
+
+    public ExternalUser load( String id ) throws DataAccessException;
+    
+    public Iterator iterator() throws DataAccessException;
+}
+ 
\ No newline at end of file

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/GroupDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/GroupDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/GroupDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/GroupDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,48 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.Group;
+
+
+public interface GroupDao
+{
+    public Group add( String name, Set members ) throws DataAccessException;
+
+    public Group rename( String newName, Group archetype ) throws DataAccessException;
+
+    public Group modify( String creatorsName, Date createTimestamp, String name, Set members, 
+        ModificationItem[] mods ) throws DataAccessException;
+
+    public void delete( String name ) throws DataAccessException;
+
+    public Group load( String name ) throws DataAccessException;
+    
+    public Iterator iterator() throws DataAccessException;
+}
+ 
\ No newline at end of file

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/HauskeysUserDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/HauskeysUserDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/HauskeysUserDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/HauskeysUserDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,57 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.HauskeysUser;
+
+
+public interface HauskeysUserDao
+{
+    public HauskeysUser add( String id, String description, String firstName, 
+        String lastName, String password, String mobile, String email, String notifyBy, 
+        String mobileCarrier, String tokenPin, String midletName, String failuresInEpoch, 
+        String activationKey, String realm, String secret, String label, String movingFactor, 
+        String address1, String address2, String city, String stateProvRegion, String zipPostalCode, 
+        String country, String company, boolean disabled ) throws DataAccessException;
+
+    public HauskeysUser rename( String newName, HauskeysUser archetype ) throws DataAccessException;
+
+    public HauskeysUser modify( String creatorsName, Date createTimestamp, String id, 
+        String description, String firstName, String lastName, String password, String mobile, 
+        String email, String notifyBy, String mobileCarrier, String tokenPin, String midletName, 
+        String failuresInEpoch, String activationKey, String realm, String secret, String label, 
+        String movingFactor, String address1, String address2, String city, String stateProvRegion, 
+        String zipPostalCode, String country, String company, boolean disabled, 
+        ModificationItem[] mods ) throws DataAccessException;
+
+    public void delete( String name ) throws DataAccessException;
+
+    public HauskeysUser load( String name ) throws DataAccessException;
+    
+    public Iterator iterator() throws DataAccessException;
+}
+ 
\ No newline at end of file

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/LocalUserDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/LocalUserDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/LocalUserDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/LocalUserDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,53 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.LocalUser;
+
+
+public interface LocalUserDao
+{
+    public LocalUser add( String id, String description, String firstName, 
+        String lastName, String password, String address1, String address2, String city, String stateProvRegion, 
+        String zipPostalCode, String country, String company, String email, boolean disabled ) 
+        throws DataAccessException;
+
+    public LocalUser rename( String newId, LocalUser archetype ) throws DataAccessException;
+
+    public LocalUser modify( String creatorsName, Date createTimestamp, String id, 
+        String description, String pasword, String firstName, String lastName, 
+        String address1, String address2, String city, String stateProvRegion, 
+        String zipPostalCode, String country, String company, String email, 
+        boolean disabled, ModificationItem[] mods ) throws DataAccessException;
+
+    public void delete( String id ) throws DataAccessException;
+
+    public LocalUser load( String id ) throws DataAccessException;
+    
+    public Iterator iterator() throws DataAccessException;
+}
+ 
\ No newline at end of file

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/PermissionDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/PermissionDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/PermissionDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/PermissionDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,58 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.Permission;
+
+
+public interface PermissionDao
+{
+    public abstract Permission add( String applicationName, String name, String description ) 
+        throws DataAccessException;
+
+    public abstract void delete( String applicationName, String name ) 
+        throws DataAccessException;
+
+    public abstract Permission modify( String creatorsName, Date createTimestamp, String applicationName, 
+        String name, String description, ModificationItem[] mods )
+        throws DataAccessException;
+
+    public abstract Permission rename( String newName, Permission permission )
+        throws DataAccessException;
+
+    public abstract Permission load( String applicationName, String name ) 
+        throws DataAccessException;
+
+    public abstract boolean has( String applicationName, String name ) 
+        throws DataAccessException;
+
+    public abstract Iterator permissionNameIterator( String applicationName ) 
+        throws DataAccessException;
+    
+    public abstract Iterator permissionIterator( String applicationName ) 
+        throws DataAccessException;
+}
\ No newline at end of file

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ProfileDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ProfileDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ProfileDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ProfileDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,54 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.Profile;
+
+
+public interface ProfileDao
+{
+    Iterator profileIterator( String applicationName ) 
+        throws DataAccessException;
+
+    Iterator profileIterator( String applicationName, String user ) 
+        throws DataAccessException;
+
+    Profile load( String applicationName, String id ) 
+        throws DataAccessException;
+
+    Profile add( String applicationName, String id, String user, String description, 
+        Set grants, Set denials, Set roles ) throws DataAccessException;
+
+    Profile rename( String newId, Profile archetype ) throws DataAccessException;
+
+    Profile modify( String creatorsName, Date createTimestamp, String applicationName, String id, 
+        String user, String description, Set grants, Set denials, Set roles, boolean disabled, ModificationItem[] mods ) 
+        throws DataAccessException;
+
+    void delete( String name, String id ) throws DataAccessException;
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/RoleDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/RoleDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/RoleDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/RoleDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,52 @@
+/*
+ *  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;
+
+
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.directory.ModificationItem;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.Role;
+
+
+public interface RoleDao
+{
+    Iterator roleIterator( String applicationName ) 
+        throws DataAccessException;
+
+    Role load( String applicationName, String name ) 
+        throws DataAccessException;
+
+    Role add( String applicationName, String name, String currentValue, Set currentValues ) 
+        throws DataAccessException;
+
+    Role rename( String newName, Role archetype ) 
+        throws DataAccessException;
+
+    Role modify( String creatorsName, Date createTimestamp, String applicationName, String name, 
+        String description, Set grants, ModificationItem[] mods )  throws DataAccessException;
+
+    void delete( String applicationName, String name ) 
+        throws DataAccessException;
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/UserDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/UserDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/UserDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/UserDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,38 @@
+/*
+ *  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;
+
+
+import java.util.Iterator;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.User;
+
+
+public interface UserDao
+{
+    User load( String id ) throws DataAccessException;
+    
+    Iterator iterator() throws DataAccessException;
+
+    String getRandomUniqueActivationKey() throws DataAccessException;
+
+    boolean hasUser( String id ) throws DataAccessException;
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/AbstractLdapDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/AbstractLdapDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/AbstractLdapDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/AbstractLdapDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,119 @@
+/*
+ *  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 javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.safehaus.triplesec.admin.Constants;
+import org.safehaus.triplesec.admin.DataAccessException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public abstract class AbstractLdapDao implements LdapDao, Constants
+{
+    protected final Logger log;
+    protected final DirContext ctx;
+    protected final String realm;
+    protected final String baseUrl;
+    protected final String principalName;
+
+    
+    public AbstractLdapDao( DirContext ctx ) throws DataAccessException
+    {
+        this.ctx = ctx;
+        log = LoggerFactory.getLogger( getClass() );
+        String name = null;
+        String principal = null;
+        String tmpRealm = 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();
+            }
+            tmpRealm = inferRealm( name );
+        }
+        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;
+            realm = tmpRealm;
+        }
+    }
+
+    
+    private String inferRealm( String dn ) throws NamingException
+    {
+        if ( dn == null || dn.equals( "" ) )
+        {
+            return "";
+        }
+        
+        StringBuffer buf = new StringBuffer();
+        LdapDN ldapName = new LdapDN( dn );
+        for ( int ii = ldapName.size() - 1; ii >= 0 ; ii-- )
+        {
+            String comp = ldapName.get( ii );
+            if ( NamespaceTools.getRdnAttribute( comp ).equalsIgnoreCase( DOMAIN_COMPONENT_ID ) )
+            {
+                buf.append( NamespaceTools.getRdnValue( comp ) );
+                
+                if ( ii != 0 )
+                {
+                    buf.append( "." );
+                }
+            }
+        }
+        
+        return buf.toString();
+    }
+    
+    
+    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/JndiIterator.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/JndiIterator.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/JndiIterator.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/JndiIterator.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,154 @@
+/*
+ *  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.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchResult;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An iterator over a NamingEnumeration containing the results of an LDAP 
+ * search. 
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class JndiIterator implements Iterator
+{
+    private final static Logger log = LoggerFactory.getLogger( JndiIterator.class );
+    private final NamingEnumeration underlying;
+    private final LdapDao dao;
+    private final String id;
+
+    private Object prefetched;
+    private String prefetchedRdn;
+    private String lastRdn;
+    private Object extra;
+    
+    
+    JndiIterator( LdapDao dao, String id, NamingEnumeration underlying, Object extra ) throws NamingException
+    {
+        this.id = id;
+        this.dao = dao;
+        this.extra = extra;
+        this.underlying = underlying;
+        prefetch();
+    }
+
+    
+    JndiIterator( LdapDao dao, NamingEnumeration underlying, Object extra ) throws NamingException
+    {
+        this.id = null;
+        this.dao = dao;
+        this.extra = extra;
+        this.underlying = underlying;
+        prefetch();
+    }
+
+    
+    private void prefetch() throws NamingException
+    {
+        if ( underlying.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) underlying.next();
+            if ( id != null )
+            {
+                if ( result.getAttributes().get( id ) != null )
+                {
+                    prefetched = result.getAttributes().get( id ).get();
+                }
+            }
+            else 
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Iterator prefetched " + result.getName() + " with attributes:\n" 
+                        + result.getAttributes() );
+                }
+                prefetched = dao.getEntryObject( extra, result.getAttributes() );
+            }
+    
+            prefetchedRdn = result.getName();
+        }
+        else
+        {
+            prefetched = null;
+        }
+    }
+    
+    
+    public boolean hasNext()
+    {
+        return prefetched != null;
+    }
+
+    
+    public Object next()
+    {
+        Object retval = prefetched;
+        lastRdn = prefetchedRdn;
+        
+        try
+        {
+            prefetch();
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Premature truncation of underlying naming enumeration.", e );
+            if ( underlying != null ) 
+            {
+                try
+                {
+                    underlying.close();
+                }
+                catch ( NamingException e1 )
+                {
+                    log.error( "Failed to properly close the underlying naming enumeration.", e1 );
+                }
+            }
+        }
+        return retval;
+    }
+
+    
+    public void remove()
+    {
+        if ( lastRdn == null )
+        {
+            throw new IllegalStateException( "next() has not been called." );
+        }
+        
+        if ( dao != null )
+        {
+            dao.deleteEntry( lastRdn );
+        }
+        else
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapApplicationDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapApplicationDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapApplicationDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapApplicationDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,398 @@
+/*
+ *  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 org.apache.directory.shared.ldap.name.LdapDN;
+import org.safehaus.triplesec.admin.Application;
+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.dao.ApplicationDao;
+import org.safehaus.triplesec.admin.dao.PermissionDao;
+import org.safehaus.triplesec.admin.dao.ProfileDao;
+import org.safehaus.triplesec.admin.dao.RoleDao;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.ContextNotEmptyException;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+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 java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+
+public class LdapApplicationDao implements ApplicationDao, LdapDao, Constants, Serializable
+{
+    private static final long serialVersionUID = 280058609541343711L;
+    private static final String[] ATTRIBUTES = new String[] { 
+        DESCRIPTION_ID, APP_NAME_ID, PASSWORD_ID, CREATORS_NAME_ID , MODIFIERS_NAME_ID, 
+        CREATE_TIMESTAMP_ID, MODIFY_TIMESTAMP_ID };
+    private static final Logger log = LoggerFactory.getLogger( LdapPermissionDao.class );
+    private final String principalName;
+    private final DirContext ctx;
+    private final String baseUrl;
+    private final PermissionDao permissionDao;
+    private final RoleDao roleDao;
+    private final ProfileDao profileDao;
+    
+    
+    public LdapApplicationDao( DirContext ctx, PermissionDao permissionDao, 
+        RoleDao roleDao, ProfileDao profileDao ) throws DataAccessException
+    {
+        this.ctx = ctx;
+        this.permissionDao = permissionDao;
+        this.roleDao = roleDao;
+        this.profileDao = profileDao;
+
+        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 Application add( String appName, String description, String userPassword ) throws DataAccessException
+    {
+        BasicAttributes attrs = new BasicAttributes( "objectClass", "policyApplication", true );
+        attrs.put( APP_NAME_ID, appName );
+        if ( description != null )
+        {
+            attrs.put( "description", description );
+        }
+        if ( userPassword != null )
+        {
+            attrs.put( "userPassword", userPassword );
+        }
+        
+        String rdn = getRelativeDn( appName );
+        try
+        {
+            DirContext appCtx = ctx.createSubcontext( rdn, attrs );
+            attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
+            attrs.put( "ou", "Permissions" );
+            appCtx.createSubcontext( "ou=Permissions", attrs );
+            attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
+            attrs.put( "ou", "Roles" );
+            appCtx.createSubcontext( "ou=Roles", attrs );
+            attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
+            attrs.put( "ou", "Profiles" );
+            appCtx.createSubcontext( "ou=Profiles", attrs );
+            return new Application( principalName, new Date( System.currentTimeMillis() ), 
+                this, appName, description, userPassword, 
+                permissionDao, roleDao, profileDao );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            log.error( "Cannot create application " + 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 Application modify( String appName, ModificationItem[] mods ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( appName );
+        
+        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 load( appName );
+    }
+
+
+    public Application load( String appName ) throws DataAccessException
+    {
+        String description = null;
+        String userPassword = null;
+        String creatorsName = null;
+        Date createTimestamp = null;
+        String modifiersName = null;
+        Date modifyTimestamp = null;
+        String rdn = getRelativeDn( appName );
+        Attributes attrs = null;
+        
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+            description = LdapUtils.getSingleValued( "description", attrs );
+            creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs );
+            createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs );
+            modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs );
+            modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs );
+            userPassword = LdapUtils.getSingleValued( "userPassword", 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 Application( creatorsName, createTimestamp, modifiersName, modifyTimestamp, 
+            this, appName, description, userPassword, permissionDao, roleDao, profileDao );
+    }
+
+
+    public boolean has( String appName ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( appName );
+        Attributes attrs = null;
+        
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+        }
+        catch ( NameNotFoundException e )
+        {
+            return false;
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to lookup " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+
+        return attrs != null;
+    }
+
+
+    public Iterator applicationIterator() throws DataAccessException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( ATTRIBUTES );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        try
+        {
+            return new JndiIterator( this, ctx.search( "ou=applications", 
+                "(& (appName=*) (objectClass=policyApplication) )", controls ), null );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to search ou=applications under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+
+    public void delete( Application application ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( application.getName() );
+
+        try
+        {
+            DirContext appCtx = ( DirContext ) ctx.lookup( rdn );
+            appCtx.destroySubcontext( "ou=Permissions" );
+            appCtx.destroySubcontext( "ou=Roles" );
+            appCtx.destroySubcontext( "ou=Profiles" );
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( ContextNotEmptyException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            msg += ".  Other entities exist below " + application.getName();
+            msg += ".  Delete all permissions, roles and profiles before deleting the app.";
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            msg += ".  Other entities depend on " + application.getName();
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NameNotFoundException e )
+        {
+            String msg = rdn + " under " + baseUrl + " does not exist!";
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+
+    public Application rename( Application app, String newName ) throws DataAccessException
+    {
+        String oldRdn = getRelativeDn( app.getName() );
+        String newRdn = getRelativeDn( newName );
+        
+        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 permission 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 Application( app.getCreatorsName(), app.getCreateTimestamp(), app.getModifiersName(), 
+            app.getModifyTimestamp(), this, newName, app.getDescription(), app.getPassword(),
+            permissionDao, roleDao, profileDao );
+    }
+    
+    
+    private String getRelativeDn( String appName )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "appName=" ).append( appName );
+        buf.append( ",ou=Applications" );
+        return buf.toString();
+    }
+
+
+    public Object getEntryObject( Object extra, Attributes attrs )
+    {
+        String appName = null;
+        String description = null;
+        String userPassword = null;
+        String creatorsName = null;
+        Date createTimestamp = null;
+        String modifiersName = null;
+        Date modifyTimestamp = null;
+        
+        try
+        {
+            appName = ( String ) attrs.get( APP_NAME_ID ).get();
+            description = LdapUtils.getSingleValued( "description", attrs );
+            creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs );
+            createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs );
+            modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs );
+            modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs );
+            userPassword = LdapUtils.getSingleValued( "userPassword", attrs );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to produce object for attributes: " + attrs;
+            log.error( msg, e );
+        }
+        
+        return new Application( creatorsName, createTimestamp, modifiersName, modifyTimestamp, 
+            this, appName, description, userPassword, 
+            permissionDao, roleDao, profileDao );
+    }
+
+
+    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/LdapDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,31 @@
+/*
+ *  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 javax.naming.directory.Attributes;
+
+
+public interface LdapDao
+{
+    Object getEntryObject( Object extra, Attributes attrs );
+
+    void deleteEntry( String rdn );
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDaoFactory.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDaoFactory.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDaoFactory.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapDaoFactory.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,131 @@
+/*
+ *  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.Properties;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.InitialLdapContext;
+
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.dao.ApplicationDao;
+import org.safehaus.triplesec.admin.dao.DaoFactory;
+import org.safehaus.triplesec.admin.dao.ExternalUserDao;
+import org.safehaus.triplesec.admin.dao.GroupDao;
+import org.safehaus.triplesec.admin.dao.HauskeysUserDao;
+import org.safehaus.triplesec.admin.dao.LocalUserDao;
+import org.safehaus.triplesec.admin.dao.PermissionDao;
+import org.safehaus.triplesec.admin.dao.ProfileDao;
+import org.safehaus.triplesec.admin.dao.RoleDao;
+import org.safehaus.triplesec.admin.dao.UserDao;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class LdapDaoFactory extends DaoFactory
+{
+    private static final Logger log = LoggerFactory.getLogger( LdapDaoFactory.class );
+    private DirContext ctx;
+    
+
+    public LdapDaoFactory( Properties props ) throws DataAccessException
+    {
+        try
+        {
+            ctx = new InitialLdapContext( props, null );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to initialize LDAP context: " + e.getMessage();
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+    
+    
+    public PermissionDao getPermissionDao() throws DataAccessException
+    {
+        return new LdapPermissionDao( ctx );
+    }
+    
+    
+    public ApplicationDao getApplicationDao() throws DataAccessException
+    {
+        return new LdapApplicationDao( ctx, getPermissionDao(), getRoleDao(), getProfileDao() );
+    }
+
+    
+    public RoleDao getRoleDao() throws DataAccessException
+    {
+        return new LdapRoleDao( ctx ); 
+    }
+
+    
+    public ProfileDao getProfileDao() throws DataAccessException
+    {
+        return new LdapProfileDao( ctx ); 
+    }
+
+
+    public GroupDao getGroupDao() throws DataAccessException
+    {
+        return new LdapGroupDao( ctx ); 
+    }
+    
+    
+    public ExternalUserDao getExternalUserDao() throws DataAccessException
+    {
+        return new LdapExternalUserDao( ctx ); 
+    }
+    
+    
+    public LocalUserDao getLocalUserDao() throws DataAccessException
+    {
+        return new LdapLocalUserDao( ctx ); 
+    }
+
+
+    public HauskeysUserDao getHauskeysUserDao() throws DataAccessException
+    {
+        return new LdapHauskeysUserDao( ctx );
+    }
+
+
+    public UserDao getUserDao() throws DataAccessException
+    {
+        return new LdapUserDao( ctx, ( LdapExternalUserDao ) getExternalUserDao(), 
+            ( LdapLocalUserDao ) getLocalUserDao(), ( LdapHauskeysUserDao ) getHauskeysUserDao() );
+    }
+    
+    
+    public void close() 
+    {
+        try
+        {
+            ctx.close();
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Failed while trying to close context to triplesec server.", e );
+        }
+    }
+}

Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapExternalUserDao.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapExternalUserDao.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapExternalUserDao.java (added)
+++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapExternalUserDao.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,320 @@
+/*
+ *  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.Date;
+import java.util.Iterator;
+
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.Attributes;
+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.safehaus.triplesec.admin.Constants;
+import org.safehaus.triplesec.admin.ConstraintViolationException;
+import org.safehaus.triplesec.admin.DataAccessException;
+import org.safehaus.triplesec.admin.ExternalUser;
+import org.safehaus.triplesec.admin.EntryAlreadyExistsException;
+import org.safehaus.triplesec.admin.NoSuchEntryException;
+import org.safehaus.triplesec.admin.PermissionDeniedException;
+import org.safehaus.triplesec.admin.dao.ExternalUserDao;
+
+
+public class LdapExternalUserDao extends AbstractLdapDao implements ExternalUserDao, Constants
+{
+    private static final String[] ATTRIBUTES = { "*", "+" };
+
+    
+    public LdapExternalUserDao( DirContext ctx ) throws DataAccessException
+    {
+        super( ctx );
+    }
+
+
+    public ExternalUser add( String id, String description, String referral ) throws DataAccessException
+    {
+        BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, "referral", true );
+        attrs.get( OBJECT_CLASS_ID ).add( EXTENSIBLE_OBJECT_OC );
+        attrs.get( OBJECT_CLASS_ID ).add( UID_OBJECT_OC );
+        attrs.put( REF_ID, referral );
+        attrs.put( UID_ID, id );
+        
+        if ( description != null )
+        {
+            attrs.put( DESCRIPTION_ID, description );
+        }
+        
+        String rdn = getRelativeDn( id );
+        try
+        {
+            ctx.createSubcontext( rdn, attrs );
+            return new ExternalUser( principalName, new Date( System.currentTimeMillis() ), this, 
+                id, description, referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            log.error( "Cannot create external user " + 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 ExternalUser rename( String newId, ExternalUser archetype ) throws DataAccessException
+    {
+        String oldRdn = getRelativeDn( archetype.getId() );
+        String newRdn = getRelativeDn( newId );
+        
+        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 group 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 ( NoPermissionException e )
+        {
+            String msg = "Rename failed. Permission denied.";
+            log.error( msg, e );
+            throw new PermissionDeniedException( 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 ExternalUser( archetype.getCreatorsName(), archetype.getCreateTimestamp(), principalName, 
+            new Date( System.currentTimeMillis() ), this, newId, archetype.getDescription(), 
+            archetype.getReferral(), archetype.isDisabled() );
+    }
+
+
+    public ExternalUser modify( String creatorsName, Date createTimestamp, String id, String description, 
+        String referral, boolean disabled, ModificationItem[] mods ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( id );
+        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 ( NoPermissionException e )
+        {
+            String msg = "Modify failed. Permission denied to " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new PermissionDeniedException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Could not modify " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new NoSuchEntryException( msg );
+        }
+        
+        return new ExternalUser( creatorsName, createTimestamp, this.principalName, 
+            new Date( System.currentTimeMillis() ), this, id, description, referral, disabled );
+    }
+
+
+    public void delete( String id ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( id );
+
+        try
+        {
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( SchemaViolationException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            msg += ".  Other entities depend on " + id;
+            log.error( msg, e );
+            throw new ConstraintViolationException( msg );
+        }
+        catch ( NoPermissionException e )
+        {
+            String msg = "Delete failed. Permission denied to delete " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new PermissionDeniedException( msg );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Could not delete " + rdn + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+
+    public ExternalUser load( String id ) throws DataAccessException
+    {
+        String rdn = getRelativeDn( id );
+        Attributes attrs = null;
+        String description = null;
+        String referral = null;
+        String creatorsName = null;
+        String modifiersName = null;
+        Date createTimestamp = null;
+        Date modifyTimestamp = null;
+        boolean disabled = false;
+        
+        try
+        {
+            attrs = ctx.getAttributes( rdn, ATTRIBUTES );
+            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 );
+            description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs );
+            referral = ( String ) attrs.get( REF_ID ).get();
+            disabled = LdapUtils.getBoolean( KRB5_DISABLED_ID, attrs, false );
+        }
+        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 ExternalUser( creatorsName, createTimestamp, modifiersName, modifyTimestamp, 
+            this, id, description, referral, disabled );
+    }
+
+
+    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)", controls ), null );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "Failed to search " + base + " under " + baseUrl;
+            log.error( msg, e );
+            throw new DataAccessException( msg );
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Private Utility Methods
+    // -----------------------------------------------------------------------
+
+    
+    private String getRelativeDn( String uid )
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "uid=" ).append( uid );
+        buf.append( ",ou=Users" );
+        return buf.toString();
+    }
+
+
+    // -----------------------------------------------------------------------
+    // LdapDao method implementations
+    // -----------------------------------------------------------------------
+
+    
+    public Object getEntryObject( Object extra, Attributes attrs )
+    {
+        String uid = null;
+        String description = null;
+        String referral = null;
+        String creatorsName = null;
+        String modifiersName = null;
+        Date createTimestamp = null;
+        Date modifyTimestamp = null;
+        boolean disabled = false;
+        
+        try
+        {
+            uid = ( String ) attrs.get( UID_ID ).get();
+            description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs );
+            referral = ( String ) attrs.get( REF_ID ).get();
+            disabled = LdapUtils.getBoolean( KRB5_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 ExternalUser( creatorsName, createTimestamp, modifiersName, modifyTimestamp, 
+            this, uid, description, referral, disabled );
+    }
+}