You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by sm...@apache.org on 2014/10/22 17:44:36 UTC

[17/51] [partial] Rename packages from org.openldap.fortress to org.apache.directory.fortress.core. Change default suffix to org.apache. Switch default ldap api from unbound to apache ldap.

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AdminRoleDAO.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AdminRoleDAO.java b/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AdminRoleDAO.java
new file mode 100755
index 0000000..20a1648
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AdminRoleDAO.java
@@ -0,0 +1,694 @@
+/*
+ *   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.apache.directory.fortress.core.rbac.dao.apache;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.ldap.client.api.LdapConnection;
+
+import org.apache.directory.fortress.core.CreateException;
+import org.apache.directory.fortress.core.FinderException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.ObjectFactory;
+import org.apache.directory.fortress.core.RemoveException;
+import org.apache.directory.fortress.core.UpdateException;
+import org.apache.directory.fortress.core.ldap.ApacheDsDataProvider;
+import org.apache.directory.fortress.core.rbac.AdminRole;
+import org.apache.directory.fortress.core.rbac.AdminRoleP;
+import org.apache.directory.fortress.core.rbac.AdminRoleUtil;
+import org.apache.directory.fortress.core.rbac.Graphable;
+import org.apache.directory.fortress.core.rbac.Role;
+import org.apache.directory.fortress.core.util.attr.VUtil;
+import org.apache.directory.fortress.core.util.time.CUtil;
+
+
+/**
+ * The AdminRoleDAO is called by {@link AdminRoleP} and processes data via its entity {@link AdminRole}.
+ * <p/>
+ * The Fortress AdminRoleDAO uses the following other Fortress structural and aux object classes:
+ * <h4>1. ftRls Structural objectclass is used to store the AdminRole information like name, and temporal constraints</h4>
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass	( 1.3.6.1.4.1.38088.2.1</code>
+ * <li> <code>NAME 'ftRls'</code>
+ * <li> <code>DESC 'Fortress Role Object Class'</code>
+ * <li> <code>SUP organizationalrole</code>
+ * <li> <code>STRUCTURAL</code>
+ * <li> <code>MUST ( ftId $ ftRoleName )</code>
+ * <li> <code>MAY ( description $ ftCstr ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>2. ftProperties AUXILIARY Object Class is used to store client specific name/value pairs on target entity</h4>
+ * <code># This aux object class can be used to store custom attributes.</code><br />
+ * <code># The properties collections consist of name/value pairs and are not constrainted by Fortress.</code><br />
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.3.2</code>
+ * <li> <code>NAME 'ftProperties'</code>
+ * <li> <code>DESC 'Fortress Properties AUX Object Class'</code>
+ * <li> <code>AUXILIARY</code>
+ * <li> <code>MAY ( ftProps ) ) </code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>3. ftPools Auxiliary object class store the ARBAC Perm and User OU assignments on AdminRole entity</h4>
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.3.3</code>
+ * <li> <code>NAME 'ftPools'</code>
+ * <li> <code>DESC 'Fortress Pools AUX Object Class'</code>
+ * <li> <code>AUXILIARY</code>
+ * <li> <code>MAY ( ftOSU $ ftOSP ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>4. ftMods AUXILIARY Object Class is used to store Fortress audit variables on target entity</h4>
+ * <ul>
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.3.4</code>
+ * <li> <code>NAME 'ftMods'</code>
+ * <li> <code>DESC 'Fortress Modifiers AUX Object Class'</code>
+ * <li> <code>AUXILIARY</code>
+ * <li> <code>MAY (</code>
+ * <li> <code>ftModifier $</code>
+ * <li> <code>ftModCode $</code>
+ * <li> <code>ftModId ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <p/>
+ * This class is thread safe.
+ *
+ * @author Shawn McKinney
+ */
+public final class AdminRoleDAO extends ApacheDsDataProvider implements org.apache.directory.fortress.core.rbac.dao.AdminRoleDAO
+{
+    private static final String ROLE_OCCUPANT = "roleOccupant";
+    private static final String ROLE_OSP = "ftOSP";
+    private static final String ROLE_OSU = "ftOSU";
+    private static final String ROLE_RANGE = "ftRange";
+    private static final String POOLS_AUX_OBJECT_CLASS_NAME = "ftPools";
+    private static final String ADMIN_ROLE_OBJ_CLASS[] =
+        {
+            GlobalIds.TOP,
+            GlobalIds.ROLE_OBJECT_CLASS_NM,
+            GlobalIds.PROPS_AUX_OBJECT_CLASS_NAME,
+            POOLS_AUX_OBJECT_CLASS_NAME,
+            GlobalIds.FT_MODIFIER_AUX_OBJECT_CLASS_NAME
+    };
+    private static final String ROLE_NM = "ftRoleName";
+    private static final String[] ROLE_NM_ATR =
+        {
+            ROLE_NM
+    };
+
+    private static final String[] ROLE_ATRS =
+        {
+            GlobalIds.FT_IID,
+            ROLE_NM,
+            GlobalIds.DESC,
+            GlobalIds.CONSTRAINT,
+            ROLE_OCCUPANT,
+            ROLE_OSP,
+            ROLE_OSU,
+            ROLE_RANGE,
+            GlobalIds.PARENT_NODES
+    };
+
+
+    /**
+     * Create a new AdminRole entity using supplied data.  Required attribute is {@link AdminRole#name}.
+     * This data will be stored in the {@link GlobalIds#ADMIN_ROLE_ROOT} container.
+     *
+     * @param entity record contains AdminRole data.  Null attributes will be ignored.
+     * @return input record back to client.
+     * @throws org.apache.directory.fortress.core.CreateException in the event LDAP errors occur.
+     */
+    public final AdminRole create( AdminRole entity ) throws CreateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            Entry entry = new DefaultEntry( dn );
+
+            entry.add( GlobalIds.OBJECT_CLASS, ADMIN_ROLE_OBJ_CLASS );
+            entity.setId();
+            entry.add( GlobalIds.FT_IID, entity.getId() );
+            entry.add( ROLE_NM, entity.getName() );
+
+            // description field is optional on this object class:
+            if ( VUtil.isNotNullOrEmpty( entity.getDescription() ) )
+            {
+                entry.add( GlobalIds.DESC, entity.getDescription() );
+            }
+
+            // CN attribute is required for this object class:
+            entry.add( GlobalIds.CN, entity.getName() );
+            entry.add( GlobalIds.CONSTRAINT, CUtil.setConstraint( entity ) );
+            loadAttrs( entity.getOsP(), entry, ROLE_OSP );
+            loadAttrs( entity.getOsU(), entry, ROLE_OSU );
+            String szRaw = entity.getRoleRangeRaw();
+
+            if ( VUtil.isNotNullOrEmpty( szRaw ) )
+            {
+                entry.add( ROLE_RANGE, szRaw );
+            }
+
+            // These multi-valued attributes are optional.  The utility function will return quietly if no items are loaded into collection:
+            loadAttrs( entity.getParents(), entry, GlobalIds.PARENT_NODES );
+
+            ld = getAdminConnection();
+            add( ld, entry, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "create role [" + entity.getName() + "] caught LdapException=" + e.getMessage();
+            throw new CreateException( GlobalErrIds.ARLE_ADD_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * Update existing AdminRole entity using supplied data.  Required attribute is {@link AdminRole#name}.
+     * This data will be stored in the {@link GlobalIds#ADMIN_ROLE_ROOT} container.
+     *
+     * @param entity record contains AdminRole data.  Null attributes will be ignored.
+     * @return input record back to client.
+     * @throws UpdateException in the event LDAP errors occur.
+     */
+    public final AdminRole update( AdminRole entity ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+
+            if ( VUtil.isNotNullOrEmpty( entity.getDescription() ) )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE,
+                    GlobalIds.DESC, entity.getDescription() ) );
+            }
+
+            if ( VUtil.isNotNullOrEmpty( entity.getOccupants() ) )
+            {
+                for ( String name : entity.getOccupants() )
+                {
+                    mods.add( new DefaultModification(
+                        ModificationOperation.REPLACE_ATTRIBUTE, ROLE_OCCUPANT, name ) );
+                }
+            }
+
+            if ( entity.isTemporalSet() )
+            {
+                String szRawData = CUtil.setConstraint( entity );
+                if ( VUtil.isNotNullOrEmpty( szRawData ) )
+                {
+                    mods.add( new DefaultModification(
+                        ModificationOperation.REPLACE_ATTRIBUTE, GlobalIds.CONSTRAINT, szRawData ) );
+                }
+            }
+
+            loadAttrs( entity.getOsU(), mods, ROLE_OSU );
+            loadAttrs( entity.getOsP(), mods, ROLE_OSP );
+            String szRaw = entity.getRoleRangeRaw();
+
+            if ( VUtil.isNotNullOrEmpty( szRaw ) )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE, ROLE_RANGE, szRaw ) );
+            }
+
+            loadAttrs( entity.getParents(), mods, GlobalIds.PARENT_NODES );
+
+            if ( mods.size() > 0 )
+            {
+                ld = getAdminConnection();
+                modify( ld, dn, mods, entity );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "update name [" + entity.getName() + "] caught LdapException=" + e.getMessage();
+            throw new UpdateException( GlobalErrIds.ARLE_UPDATE_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     *
+     * @param entity
+     * @throws UpdateException
+     */
+    public final void deleteParent( AdminRole entity ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, GlobalIds.PARENT_NODES ) );
+            ld = getAdminConnection();
+            modify( ld, dn, mods, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "deleteParent name [" + entity.getName() + "] caught LdapException=" + e.getMessage();
+            throw new UpdateException( GlobalErrIds.ARLE_REMOVE_PARENT_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+    }
+
+
+    /**
+     * This method will add the supplied DN as a role occupant to the target record.
+     * This data will be stored in the {@link GlobalIds#ADMIN_ROLE_ROOT} container.
+     *
+     * @param entity record contains {@link AdminRole#name}.  Null attributes will be ignored.
+     * @param userDn contains the DN for userId who is being assigned.
+     * @return input record back to client.
+     * @throws UpdateException in the event LDAP errors occur.
+     */
+    public final AdminRole assign( AdminRole entity, String userDn ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, ROLE_OCCUPANT, userDn ) );
+            ld = getAdminConnection();
+            modify( ld, dn, mods, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "assign role name [" + entity.getName() + "] user dn [" + userDn + "] caught LdapException="
+                + e.getMessage();
+            throw new UpdateException( GlobalErrIds.ARLE_USER_ASSIGN_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * This method will remove the supplied DN as a role occupant to the target record.
+     * This data will be stored in the {@link GlobalIds#ADMIN_ROLE_ROOT} container.
+     *
+     * @param entity record contains {@link AdminRole#name}.  Null attributes will be ignored.
+     * @param userDn contains the DN for userId who is being deassigned.
+     * @return input record back to client.
+     * @throws UpdateException in the event LDAP errors occur.
+     */
+    public final AdminRole deassign( AdminRole entity, String userDn ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification(
+                ModificationOperation.REMOVE_ATTRIBUTE, ROLE_OCCUPANT, userDn ) );
+            ld = getAdminConnection();
+            modify( ld, dn, mods, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "deassign role name [" + entity.getName() + "] user dn [" + userDn
+                + "] caught LdapException=" + e.getMessage();
+            throw new UpdateException( GlobalErrIds.ARLE_USER_DEASSIGN_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return entity;
+    }
+
+
+    /**
+     * This method will completely remove the AdminRole from the directory.  It will use {@link AdminRole#name} as key.
+     * This operation is performed on the {@link GlobalIds#ADMIN_ROLE_ROOT} container.
+     *
+     * @param role record contains {@link AdminRole#name}.
+     * @throws RemoveException in the event LDAP errors occur.
+     */
+    public final void remove( AdminRole role ) throws RemoveException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( role );
+
+        try
+        {
+            ld = getAdminConnection();
+            delete( ld, dn, role );
+        }
+        catch ( LdapException e )
+        {
+            String error = "remove role name=" + role.getName() + " LdapException=" + e.getMessage();
+            throw new RemoveException( GlobalErrIds.ARLE_DELETE_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+    }
+
+
+    /**
+     * This method will retrieve the AdminRole from {@link GlobalIds#ADMIN_ROLE_ROOT} container by name.
+     *
+     * @param adminRole maps to {@link AdminRole#name}.
+     * @return AdminRole back to client.
+     * @throws FinderException in the event LDAP errors occur.
+     */
+    public final AdminRole getRole( AdminRole adminRole ) throws FinderException
+    {
+        AdminRole entity = null;
+        LdapConnection ld = null;
+        String dn = getDn( adminRole );
+
+        try
+        {
+            ld = getAdminConnection();
+            Entry findEntry = read( ld, dn, ROLE_ATRS );
+            entity = unloadLdapEntry( findEntry, 0, adminRole.getContextId() );
+
+            if ( entity == null )
+            {
+                String warning = "getRole name [" + adminRole.getName() + "] no entry found dn [" + dn + "]";
+                throw new FinderException( GlobalErrIds.ARLE_NOT_FOUND, warning );
+            }
+        }
+        catch ( LdapNoSuchObjectException e )
+        {
+            String warning = "getRole name [" + adminRole.getName() + "] Obj COULD NOT FIND ENTRY for dn [" + dn
+                + "]";
+            throw new FinderException( GlobalErrIds.ARLE_NOT_FOUND, warning );
+        }
+        catch ( LdapException e )
+        {
+            String error = "getRole dn [" + dn + "] LEXCD=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_READ_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * @param adminRole
+     * @return
+     * @throws FinderException
+     *
+     */
+    public final List<AdminRole> findRoles( AdminRole adminRole ) throws FinderException
+    {
+        List<AdminRole> roleList = new ArrayList<>();
+        LdapConnection ld = null;
+        String roleRoot = getRootDn( adminRole.getContextId(), GlobalIds.ADMIN_ROLE_ROOT );
+        String filter;
+
+        try
+        {
+            String searchVal = encodeSafeText( adminRole.getName(), GlobalIds.ROLE_LEN );
+            filter = GlobalIds.FILTER_PREFIX + GlobalIds.ROLE_OBJECT_CLASS_NM + ")("
+                + ROLE_NM + "=" + searchVal + "*))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, roleRoot,
+                SearchScope.ONELEVEL, filter, ROLE_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                roleList.add( unloadLdapEntry( searchResults.getEntry(), sequence++, adminRole.getContextId() ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "findRoles name [" + adminRole.getName() + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "findRoles name [" + adminRole.getName() + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return roleList;
+    }
+
+
+    /**
+     * @param adminRole
+     * @param limit
+     * @return
+     * @throws FinderException
+     *
+     */
+    public final List<String> findRoles( AdminRole adminRole, int limit ) throws FinderException
+    {
+        List<String> roleList = new ArrayList<>();
+        LdapConnection ld = null;
+        String roleRoot = getRootDn( adminRole.getContextId(), GlobalIds.ADMIN_ROLE_ROOT );
+        String filter;
+        String searchVal = null;
+
+        try
+        {
+            searchVal = encodeSafeText( adminRole.getName(), GlobalIds.ROLE_LEN );
+            filter = GlobalIds.FILTER_PREFIX + GlobalIds.ROLE_OBJECT_CLASS_NM + ")("
+                + ROLE_NM + "=" + searchVal + "*))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, roleRoot,
+                SearchScope.ONELEVEL, filter, ROLE_NM_ATR, false, GlobalIds.BATCH_SIZE, limit );
+
+            while ( searchResults.next() )
+            {
+                Entry entry = searchResults.getEntry();
+                roleList.add( getAttribute( entry, ROLE_NM ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "findRoles name [" + searchVal + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "findRoles name [" + searchVal + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return roleList;
+    }
+
+
+    /**
+     * @param userDn
+     * @return
+     * @throws FinderException
+     */
+    public final List<String> findAssignedRoles( String userDn, String contextId ) throws FinderException
+    {
+        List<String> roleNameList = new ArrayList<>();
+        LdapConnection ld = null;
+        String roleRoot = getRootDn( contextId, GlobalIds.ADMIN_ROLE_ROOT );
+
+        try
+        {
+            String filter = GlobalIds.FILTER_PREFIX + GlobalIds.ROLE_OBJECT_CLASS_NM + ")";
+            filter += "(" + ROLE_OCCUPANT + "=" + userDn + "))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, roleRoot,
+                SearchScope.ONELEVEL, filter, ROLE_NM_ATR, false, GlobalIds.BATCH_SIZE );
+
+            while ( searchResults.next() )
+            {
+                roleNameList.add( getAttribute( searchResults.getEntry(), ROLE_NM ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "findAssignedRoles userDn [" + userDn + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_OCCUPANT_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "findAssignedRoles userDn [" + userDn + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_OCCUPANT_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return roleNameList;
+    }
+
+
+    /**
+      *
+      * @param contextId
+      * @return
+      * @throws FinderException
+      */
+    public final List<Graphable> getAllDescendants( String contextId )
+        throws FinderException
+    {
+        String[] DESC_ATRS =
+            { ROLE_NM, GlobalIds.PARENT_NODES };
+        List<Graphable> descendants = new ArrayList<>();
+        LdapConnection ld = null;
+        String roleRoot = getRootDn( contextId, GlobalIds.ADMIN_ROLE_ROOT );
+        String filter = null;
+
+        try
+        {
+            filter = GlobalIds.FILTER_PREFIX + GlobalIds.ROLE_OBJECT_CLASS_NM + ")("
+                + GlobalIds.PARENT_NODES + "=*))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, roleRoot,
+                SearchScope.ONELEVEL, filter, DESC_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                descendants.add( unloadDescendants( searchResults.getEntry(), sequence++, contextId ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "getAllDescendants filter [" + filter + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "getAllDescendants filter [" + filter + "] caught LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return descendants;
+    }
+
+
+    /**
+    *
+    * @param le
+    * @param sequence
+    * @param contextId
+    * @return
+     * @throws LdapInvalidAttributeValueException 
+    * @throws LdapException
+    */
+    private Graphable unloadDescendants( Entry le, long sequence, String contextId )
+        throws LdapInvalidAttributeValueException
+    {
+        Role entity = new ObjectFactory().createRole();
+        entity.setSequenceId( sequence );
+        entity.setName( getAttribute( le, ROLE_NM ) );
+        entity.setParents( getAttributeSet( le, GlobalIds.PARENT_NODES ) );
+        return entity;
+    }
+
+
+    /**
+     * @param le
+     * @return
+     * @throws LdapInvalidAttributeValueException 
+     * @throws LdapException
+     */
+    private AdminRole unloadLdapEntry( Entry le, long sequence, String contextId )
+        throws LdapInvalidAttributeValueException
+    {
+        AdminRole entity = new ObjectFactory().createAdminRole();
+        entity.setSequenceId( sequence );
+        entity.setId( getAttribute( le, GlobalIds.FT_IID ) );
+        entity.setName( getAttribute( le, ROLE_NM ) );
+        entity.setDescription( getAttribute( le, GlobalIds.DESC ) );
+        entity.setOccupants( getAttributes( le, ROLE_OCCUPANT ) );
+        entity.setOsP( getAttributeSet( le, ROLE_OSP ) );
+        entity.setOsU( getAttributeSet( le, ROLE_OSU ) );
+        unloadTemporal( le, entity );
+        entity.setRoleRangeRaw( getAttribute( le, ROLE_RANGE ) );
+        //entity.setParents(AdminRoleUtil.getParents(entity.getName().toUpperCase(), contextId));
+        entity.setParents( getAttributeSet( le, GlobalIds.PARENT_NODES ) );
+        entity.setChildren( AdminRoleUtil.getChildren( entity.getName().toUpperCase(), contextId ) );
+        return entity;
+    }
+
+
+    private String getDn( AdminRole adminRole )
+    {
+        return GlobalIds.CN + "=" + adminRole.getName() + ","
+            + getRootDn( adminRole.getContextId(), GlobalIds.ADMIN_ROLE_ROOT );
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AuditDAO.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AuditDAO.java b/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AuditDAO.java
new file mode 100755
index 0000000..a7f2cdb
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/AuditDAO.java
@@ -0,0 +1,784 @@
+/*
+ *   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.apache.directory.fortress.core.rbac.dao.apache;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.ldap.client.api.LdapConnection;
+
+import org.apache.directory.fortress.core.FinderException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.ObjectFactory;
+import org.apache.directory.fortress.core.cfg.Config;
+import org.apache.directory.fortress.core.ldap.ApacheDsDataProvider;
+import org.apache.directory.fortress.core.rbac.AuthZ;
+import org.apache.directory.fortress.core.rbac.Bind;
+import org.apache.directory.fortress.core.rbac.Mod;
+import org.apache.directory.fortress.core.rbac.UserAudit;
+import org.apache.directory.fortress.core.util.attr.AttrHelper;
+import org.apache.directory.fortress.core.util.attr.VUtil;
+
+
+/**
+ * This class performs data access for OpenLDAP synch repl log data
+ * <p/>
+ * <h3>1. Binds</h3>
+ * <p/>
+ * The auditBind Structural object class is used to store authentication events that can later be queried via ldap API.<br />
+ * <code># The Bind class includes the reqVersion attribute which contains the LDAP</code>
+ * <code># protocol version specified in the Bind as well as the reqMethod attribute</code>
+ * <code># which contains the Bind Method used in the Bind. This will be the string</code>
+ * <code># SIMPLE for LDAP Simple Binds or SASL(mech) for SASL Binds. Note that unless</code>
+ * <code># configured as a global overlay, only Simple Binds using DNs that reside in</code>
+ * <code># the current database will be logged:</code>
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass (  1.3.6.1.4.1.4203.666.11.5.2.6 NAME 'auditBind'</code>
+ * <li> <code>DESC 'Bind operation'</code>
+ * <li> <code>SUP auditObject STRUCTURAL</code>
+ * <li> <code>MUST ( reqVersion $ reqMethod ) )</code>
+ * <li> ------------------------------------------
+ * </ul>
+ * <h3>2. Authorizations</h3>
+ * <code>For  the  Search class the reqScope attribute contains the scope of the</code><br />
+ * <code>original search request, using the values specified for  the  LDAP  URL</code><br />
+ * <code>format. I.e.  base, one, sub, or subord.  The reqDerefAliases attribute</code><br />
+ * <code>is one of never, finding, searching, or always,  denoting  how  aliases</code><br />
+ * <code>will  be  processed during the search.  The reqAttrsOnly attribute is a</code><br />
+ * <code>Boolean value showing TRUE if only attribute names were  requested,  or</code><br />
+ * <code>FALSE  if  attributes  and  their values were requested.  The reqFilter</code><br />
+ * <code>attribute carries the filter used in the search request.   The  reqAttr</code><br />
+ * <code>attribute  lists  the  requested attributes if specific attributes were</code><br />
+ * <code>requested.  The reqEntries attribute is the integer count of  how  many</code><br />
+ * <code>entries  were  returned  by  this search request.  The reqSizeLimit and</code><br />
+ * <code>reqTimeLimit attributes indicate what  limits  were  requested  on  the</code><br />
+ * <code>search operation.</code><br />
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass  (  1.3.6.1.4.1.4203.666.11.5.2.11</code>
+ * <li> <code>NAME 'auditSearch'</code>
+ * <li> <code>DESC 'Search operation'</code>
+ * <li> <code>SUP auditReadObject STRUCTURAL</code>
+ * <li> <code>MUST ( reqScope $ reqDerefAliases $ reqAttrsOnly )</code>
+ * <li> <code>MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $</code>
+ * <li> <code>reqTimeLimit ) )</code>
+ * <li> ------------------------------------------
+ * </ul>
+ * <p/>
+ * <p/>
+ * <h3>3. Modifications</h3>
+ * The auditModify Structural object class is used to store Fortress update and delete events that can later be queried via ldap API.<br />
+ * The deletions can be recorded in this manner and associated with Fortress context because deletions will perform a modification first
+ * if audit is enabled.
+ * <p/>
+ * <code>The Modify operation contains a description  of  modifications  in  the</code><br />
+ * <code>reqMod  attribute,  which  was  already  described  above  in  the  Add</code><br />
+ * <code>operation. It may optionally  contain  the  previous  contents  of  any</code><br />
+ * <code>modified  attributes  in the reqOld attribute, using the same format as</code><br />
+ * <code>described above for the Delete operation.  The reqOld attribute is only</code><br />
+ * <code>populated  if  the  entry  being modified matches the configured logold</code><br />
+ * <code>filter.</code><br />
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass (  1.3.6.1.4.1.4203.666.11.5.2.9</code>
+ * <li> <code>NAME 'auditModify'</code>
+ * <li> <code>DESC 'Modify operation'</code>
+ * <li> <code>SUP auditWriteObject STRUCTURAL</code>
+ * <li> <code>MAY reqOld MUST reqMod )</code>
+ * <li> ------------------------------------------
+ * </ul>
+ * <p/>
+ * Note this class used descriptions pulled from man pages on slapd access log.
+ * <p/>
+ * This class is thread safe.
+ *
+ * @author Shawn McKinney
+ */
+public final class AuditDAO extends ApacheDsDataProvider implements org.apache.directory.fortress.core.rbac.dao.AuditDAO
+{
+    private static final String CREATETIMESTAMP = "createTimestamp";
+    private static final String CREATORSNAME = "creatorsName";
+    private static final String ENTRYCSN = "entryCSN";
+    private static final String ENTRYDN = "entryDN";
+    private static final String ENTRYUUID = "entryUUID";
+    private static final String HASSUBORDINATES = "hasSubordinates";
+    private static final String MODIFIERSNAME = "modifiersName";
+    private static final String MODIFYTIMESTAMP = "modifyTimestamp";
+    private static final String OBJECTCLASS = "objectClass";
+    private static final String REQUAUTHZID = "reqAuthzID";
+    private static final String REQCONTROLS = "reqControls";
+    private static final String REQDN = "reqDN";
+    private static final String REQEND = "reqEnd";
+    private static final String REQMETHOD = "reqMethod";
+    private static final String REQRESULT = "reqResult";
+    private static final String REQSESSION = "reqSession";
+    private static final String REQSTART = "reqStart";
+    private static final String REQTYPE = "reqType";
+    private static final String REQVERSION = "reqVersion";
+    private static final String REQMOD = "reqMod";
+    private static final String STRUCTURALOBJECTCLASS = "structuralObjectClass";
+    private static final String SUBSCHEMAENTRY = "subschemaSubentry";
+    private static final String REQATTR = "reqAttr";
+    private static final String REQATTRSONLY = "reqAttrsOnly";
+    private static final String REQDREFALIASES = "reqDerefAliases";
+    private static final String REQENTRIES = "reqEntries";
+    private static final String REQFILTER = "reqFilter";
+    private static final String REQSCOPE = "reqScope";
+    private static final String REQSIZELIMIT = "reqSizeLimit";
+    private static final String REQTIMELIMIT = "reqTimeLimit";
+    private static final String REQASSERTION = "reqAssertion";
+    private static final String ACCESS_BIND_CLASS_NM = "auditBind";
+    //private static final String ACCESS_AUTHZ_CLASS_NM = "auditSearch";
+    private static final String ACCESS_AUTHZ_CLASS_NM = "auditCompare";
+    private static final String ACCESS_MOD_CLASS_NM = "auditModify";
+    private static final String ACCESS_ADD_CLASS_NM = "auditAdd";
+    private static final String AUDIT_ROOT = "audit.root";
+
+    private static final String[] AUDIT_AUTHZ_ATRS =
+        {
+            CREATETIMESTAMP, CREATORSNAME, ENTRYCSN, ENTRYDN, ENTRYUUID, HASSUBORDINATES, MODIFIERSNAME,
+            MODIFYTIMESTAMP, OBJECTCLASS, REQATTR, REQATTRSONLY, REQUAUTHZID, REQCONTROLS, REQDN, REQDREFALIASES,
+            REQEND, REQENTRIES, REQFILTER, REQRESULT, REQSCOPE, REQSESSION, REQSIZELIMIT, REQSTART, REQTIMELIMIT,
+            REQTYPE, STRUCTURALOBJECTCLASS, SUBSCHEMAENTRY
+    };
+
+    private static final String[] AUDIT_BIND_ATRS =
+        {
+            CREATETIMESTAMP, CREATORSNAME, ENTRYCSN, ENTRYDN, ENTRYUUID, HASSUBORDINATES, MODIFIERSNAME,
+            MODIFYTIMESTAMP, OBJECTCLASS, REQUAUTHZID, REQCONTROLS, REQDN, REQEND, REQMETHOD, REQRESULT,
+            REQSESSION, REQSTART, REQTYPE, REQVERSION, STRUCTURALOBJECTCLASS, SUBSCHEMAENTRY
+    };
+
+    private static final String[] AUDIT_MOD_ATRS =
+        {
+            OBJECTCLASS, REQUAUTHZID, REQDN, REQEND, REQRESULT, REQSESSION, REQSTART, REQTYPE, REQMOD
+    };
+
+
+    /**
+     * This method returns failed authentications where the userid is not present in the directory.  This
+     * is possible because Fortress performs read on user before the bind.
+     * User:
+     * dn: reqStart=20101014235402.000000Z, cn=log
+     * reqStart: 20101014235402.000000Z
+     * reqEnd: 20101014235402.000001Z
+     * reqAuthzID: cn=Manager,dc=jts,dc=com
+     * reqDerefAliases: never
+     * reqSession: 84
+     * reqAttrsOnly: FALSE
+     * reqSizeLimit: -1
+     * objectClass: auditSearch
+     * reqResult: 32
+     * reqAttr: ftId
+     * reqAttr: uid
+     * reqAttr: userpassword
+     * reqAttr: description
+     * reqAttr: ou
+     * reqAttr: cn
+     * reqAttr: sn
+     * reqAttr: ftRoleCstr
+     * reqAttr: ftCstr
+     * reqAttr: ftRoleAsgn
+     * reqAttr: pwdReset
+     * reqAttr: pwdAccountLockedTime
+     * reqAttr: ftProps
+     * reqEntries: 0
+     * reqFilter: (|(objectClass=*)(?objectClass=ldapSubentry))
+     * reqType: search
+     * reqDN: uid=foo,ou=People,dc=jts,dc=com        /cal/cal2.jsp
+     * reqTimeLimit: -1
+     * reqScope: base
+     *
+     * @param audit
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    public final List<AuthZ> searchInvalidAuthNs( UserAudit audit ) throws FinderException
+    {
+        List<AuthZ> auditList = new ArrayList<>();
+        LdapConnection ld = null;
+        String auditRoot = Config.getProperty( AUDIT_ROOT );
+        String userRoot = Config.getProperty( GlobalIds.USER_ROOT );
+
+        try
+        {
+            // use wildcard for user if not passed in:
+            //reqDN: uid=foo,ou=People,dc=jts,dc=com
+            //(&
+            //  (objectclass=auditSearch)
+            //      (reqDN=uid=*,ou=People,dc=jts,dc=com)
+            //      (reqAuthzID=cn=Manager,dc=jts,dc=com)
+            //      (reqEntries=0)
+            // )
+
+            String filter = GlobalIds.FILTER_PREFIX + ACCESS_AUTHZ_CLASS_NM + ")(";
+            String userId;
+
+            if ( VUtil.isNotNullOrEmpty( audit.getUserId() ) )
+            {
+                userId = audit.getUserId();
+                filter += REQDN + "=" + GlobalIds.UID + "=" + userId + "," + userRoot + ")(" +
+                    REQUAUTHZID + "=" + "cn=Manager," + Config.getProperty( GlobalIds.SUFFIX ) + ")";
+            }
+            else
+            {
+                // pull back all failed authN attempts for all users:
+                filter += REQATTR + "=" + GlobalIds.UID + ")(" +
+                    REQUAUTHZID + "=" + "cn=Manager," + Config.getProperty( GlobalIds.SUFFIX ) + ")";
+            }
+
+            if ( audit.isFailedOnly() )
+            {
+                filter += "(" + REQENTRIES + "=" + 0 + ")";
+            }
+
+            if ( audit.getBeginDate() != null )
+            {
+                String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                filter += "(" + REQEND + ">=" + szTime + ")";
+            }
+
+            filter += ")";
+
+            //log.warn("filter=" + filter);
+            ld = getLogConnection();
+            SearchCursor searchResults = search( ld, auditRoot,
+                SearchScope.ONELEVEL, filter, AUDIT_AUTHZ_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                AuthZ authZ = getAuthzEntityFromLdapEntry( searchResults.getEntry(), sequence++ );
+                // todo: fix this workaround. This search will return failed role assign searches as well.  
+                // Work around is to remove the ou=People failed searches from user failed searches on authN.
+                if ( !AttrHelper.getAuthZId( authZ.getReqDN() ).equalsIgnoreCase( "People" ) )
+                    auditList.add( authZ );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "LdapException in AuditDAO.searchAuthZs id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_AUTHN_INVALID_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "LdapException in AuditDAO.searchAuthZs id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_AUTHN_INVALID_FAILED, error, e );
+        }
+        finally
+        {
+            closeLogConnection( ld );
+        }
+
+        return auditList;
+    }
+
+
+    /**
+     * @param audit
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    public final List<AuthZ> searchAuthZs( UserAudit audit ) throws FinderException
+    {
+        List<AuthZ> auditList = new ArrayList<>();
+        LdapConnection ld = null;
+        String auditRoot = Config.getProperty( AUDIT_ROOT );
+        String permRoot = getRootDn( audit.isAdmin(), audit.getContextId() );
+        String userRoot = getRootDn( audit.getContextId(), GlobalIds.USER_ROOT );
+
+        try
+        {
+            String reqDn = PermDAO.getOpRdn( audit.getOpName(), audit.getObjId() ) + "," + GlobalIds.POBJ_NAME + "="
+                + audit.getObjName() + "," + permRoot;
+            String filter = GlobalIds.FILTER_PREFIX + ACCESS_AUTHZ_CLASS_NM + ")(" + REQDN + "=" +
+                reqDn + ")(" + REQUAUTHZID + "=" + GlobalIds.UID + "=" + audit.getUserId() + "," + userRoot + ")";
+
+            if ( audit.isFailedOnly() )
+            {
+                filter += "(!(" + REQRESULT + "=" + 6 + "))";
+            }
+
+            if ( audit.getBeginDate() != null )
+            {
+                String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                filter += "(" + REQEND + ">=" + szTime + ")";
+            }
+
+            filter += ")";
+
+            //System.out.println("filter=" + filter);
+            ld = getLogConnection();
+            SearchCursor searchResults = search( ld, auditRoot,
+                SearchScope.ONELEVEL, filter, AUDIT_AUTHZ_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                auditList.add( getAuthzEntityFromLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "LdapException in AuditDAO.searchAuthZs id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_AUTHZ_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "LdapException in AuditDAO.searchAuthZs id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_AUTHZ_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeLogConnection( ld );
+        }
+
+        return auditList;
+    }
+
+
+    private String getRootDn( boolean isAdmin, String contextId )
+    {
+        String dn;
+
+        if ( isAdmin )
+        {
+            dn = getRootDn( contextId, GlobalIds.ADMIN_PERM_ROOT );
+        }
+        else
+        {
+            dn = getRootDn( contextId, GlobalIds.PERM_ROOT );
+        }
+
+        return dn;
+    }
+
+
+    /**
+     * @param audit
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    public final List<AuthZ> getAllAuthZs( UserAudit audit ) throws FinderException
+    {
+        List<AuthZ> auditList = new ArrayList<>();
+        LdapConnection ld = null;
+        String auditRoot = Config.getProperty( AUDIT_ROOT );
+        String userRoot = getRootDn( audit.getContextId(), GlobalIds.USER_ROOT );
+
+        try
+        {
+            String filter = GlobalIds.FILTER_PREFIX + ACCESS_AUTHZ_CLASS_NM + ")(";
+
+            if ( audit.getUserId() != null && audit.getUserId().length() > 0 )
+            {
+                filter += REQUAUTHZID + "=" + GlobalIds.UID + "=" + audit.getUserId() + "," + userRoot + ")";
+            }
+            else
+            {
+                // have to limit the query to only authorization entries.
+                // TODO: determine why the cn=Manager user is showing up in this search:
+                filter += REQUAUTHZID + "=*)(!(" + REQUAUTHZID + "=cn=Manager," + Config.getProperty( GlobalIds.SUFFIX )
+                    + "))";
+
+                // TODO: fix this so filter by only the Fortress AuthZ entries and not the others:
+                if ( audit.isFailedOnly() )
+                {
+                    filter += "(!(" + REQRESULT + "=" + 6 + "))";
+                }
+            }
+
+            if ( audit.getBeginDate() != null )
+            {
+                String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                filter += "(" + REQEND + ">=" + szTime + ")";
+            }
+
+            filter += ")";
+
+            //log.warn("filter=" + filter);
+            ld = getLogConnection();
+            SearchCursor searchResults = search( ld, auditRoot,
+                SearchScope.ONELEVEL, filter, AUDIT_AUTHZ_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                auditList.add( getAuthzEntityFromLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "LdapException in AuditDAO.getAllAuthZs id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_AUTHZ_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "LdapException in AuditDAO.getAllAuthZs id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_AUTHZ_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeLogConnection( ld );
+        }
+
+        return auditList;
+    }
+
+
+    /**
+     * @param audit
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    public final List<Bind> searchBinds( UserAudit audit ) throws FinderException
+    {
+        List<Bind> auditList = new ArrayList<>();
+        LdapConnection ld = null;
+        String auditRoot = Config.getProperty( AUDIT_ROOT );
+        String userRoot = getRootDn( audit.getContextId(), GlobalIds.USER_ROOT );
+
+        try
+        {
+            String filter;
+
+            if ( audit.getUserId() != null && audit.getUserId().length() > 0 )
+            {
+                filter = GlobalIds.FILTER_PREFIX + ACCESS_BIND_CLASS_NM + ")(" +
+                    REQDN + "=" + GlobalIds.UID + "=" + audit.getUserId() + "," + userRoot + ")";
+
+                if ( audit.isFailedOnly() )
+                {
+                    filter += "(" + REQRESULT + ">=" + 1 + ")";
+                }
+
+                if ( audit.getBeginDate() != null )
+                {
+                    String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                    filter += "(" + REQEND + ">=" + szTime + ")";
+                }
+
+                filter += ")";
+            }
+            else
+            {
+                filter = GlobalIds.FILTER_PREFIX + ACCESS_BIND_CLASS_NM + ")";
+
+                if ( audit.isFailedOnly() )
+                {
+                    filter += "(" + REQRESULT + ">=" + 1 + ")";
+                }
+
+                if ( audit.getBeginDate() != null )
+                {
+                    String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                    filter += "(" + REQEND + ">=" + szTime + ")";
+                }
+
+                filter += ")";
+            }
+
+            //log.warn("filter=" + filter);
+            ld = getLogConnection();
+            SearchCursor searchResults = search( ld, auditRoot,
+                SearchScope.ONELEVEL, filter, AUDIT_BIND_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                auditList.add( getBindEntityFromLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "LdapException in AuditDAO.searchBinds id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_BIND_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "LdapException in AuditDAO.searchBinds id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_BIND_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeLogConnection( ld );
+        }
+
+        return auditList;
+    }
+
+
+    /**
+     * @param audit
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    public final List<Mod> searchUserMods( UserAudit audit ) throws FinderException
+    {
+        List<Mod> modList = new ArrayList<>();
+        LdapConnection ld = null;
+        String auditRoot = Config.getProperty( AUDIT_ROOT );
+
+        String userRoot = getRootDn( audit.getContextId(), GlobalIds.USER_ROOT );
+
+        try
+        {
+            String filter = GlobalIds.FILTER_PREFIX + ACCESS_MOD_CLASS_NM + ")(" +
+                REQDN + "=" + GlobalIds.UID + "=" + audit.getUserId() + "," + userRoot + ")";
+
+            if ( audit.getBeginDate() != null )
+            {
+                String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                filter += "(" + REQEND + ">=" + szTime + ")";
+            }
+
+            filter += ")";
+            //log.warn("filter=" + filter);
+            ld = getLogConnection();
+            SearchCursor searchResults = search( ld, auditRoot,
+                SearchScope.ONELEVEL, filter, AUDIT_MOD_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                modList.add( getModEntityFromLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "searchUserMods caught LdapException id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_MOD_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "searchUserMods caught LdapException id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_MOD_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeLogConnection( ld );
+        }
+
+        return modList;
+    }
+
+
+    /**
+     * @param audit
+     * @return
+     * @throws FinderException
+     */
+    public final List<Mod> searchAdminMods( UserAudit audit ) throws FinderException
+    {
+        List<Mod> modList = new ArrayList<>();
+        LdapConnection ld = null;
+        String auditRoot = Config.getProperty( AUDIT_ROOT );
+
+        try
+        {
+            String filter = "(&(|(objectclass=" + ACCESS_MOD_CLASS_NM + ")";
+            filter += "(objectclass=" + ACCESS_ADD_CLASS_NM + "))";
+
+            if ( VUtil.isNotNullOrEmpty( audit.getDn() ) )
+            {
+                filter += "(" + REQDN + "=" + audit.getDn() + ")";
+            }
+
+            if ( VUtil.isNotNullOrEmpty( audit.getObjName() ) )
+            {
+                filter += "(|(" + REQMOD + "=" + GlobalIds.FT_MODIFIER_CODE + ":= " + audit.getObjName() + ".";
+
+                if ( VUtil.isNotNullOrEmpty( audit.getOpName() ) )
+                {
+                    filter += audit.getOpName();
+                }
+
+                filter += "*)";
+                filter += "(" + REQMOD + "=" + GlobalIds.FT_MODIFIER_CODE + ":+ " + audit.getObjName() + ".";
+
+                if ( VUtil.isNotNullOrEmpty( audit.getOpName() ) )
+                {
+                    filter += audit.getOpName();
+                }
+
+                filter += "*))";
+            }
+
+            if ( VUtil.isNotNullOrEmpty( audit.getInternalUserId() ) )
+            {
+                filter += "(|(" + REQMOD + "=" + GlobalIds.FT_MODIFIER + ":= " + audit.getInternalUserId() + ")";
+                filter += "(" + REQMOD + "=" + GlobalIds.FT_MODIFIER + ":+ " + audit.getInternalUserId() + "))";
+            }
+
+            if ( audit.getBeginDate() != null )
+            {
+                String szTime = AttrHelper.encodeGeneralizedTime( audit.getBeginDate() );
+                filter += "(" + REQEND + ">=" + szTime + ")";
+            }
+
+            if ( audit.getEndDate() != null )
+            {
+                String szTime = AttrHelper.encodeGeneralizedTime( audit.getEndDate() );
+                filter += "(" + REQEND + "<=" + szTime + ")";
+            }
+
+            filter += ")";
+            //log.warn("filter=" + filter);
+            ld = getLogConnection();
+            SearchCursor searchResults = search( ld, auditRoot,
+                SearchScope.ONELEVEL, filter, AUDIT_MOD_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                modList.add( getModEntityFromLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "searchAdminMods caught LdapException id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_MOD_ADMIN_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "searchAdminMods caught LdapException id=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.AUDT_MOD_ADMIN_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeLogConnection( ld );
+        }
+
+        return modList;
+    }
+
+
+    /**
+     * @param le
+     * @return
+     * @throws LdapInvalidAttributeValueException 
+     * @throws LdapException
+     */
+    private Bind getBindEntityFromLdapEntry( Entry le, long sequence ) throws LdapInvalidAttributeValueException
+    {
+
+        Bind auditBind = new ObjectFactory().createBind();
+        auditBind.setSequenceId( sequence );
+        auditBind.setCreateTimestamp( getAttribute( le, CREATETIMESTAMP ) );
+        auditBind.setCreatorsName( getAttribute( le, CREATORSNAME ) );
+        auditBind.setEntryCSN( getAttribute( le, ENTRYCSN ) );
+        auditBind.setEntryDN( getAttribute( le, ENTRYDN ) );
+        auditBind.setEntryUUID( getAttribute( le, ENTRYUUID ) );
+        auditBind.setHasSubordinates( getAttribute( le, HASSUBORDINATES ) );
+        auditBind.setModifiersName( getAttribute( le, MODIFIERSNAME ) );
+        auditBind.setModifyTimestamp( getAttribute( le, MODIFYTIMESTAMP ) );
+        auditBind.setObjectClass( getAttribute( le, OBJECTCLASS ) );
+        auditBind.setReqAuthzID( getAttribute( le, REQUAUTHZID ) );
+        auditBind.setReqControls( getAttribute( le, REQCONTROLS ) );
+        auditBind.setReqDN( getAttribute( le, REQDN ) );
+        auditBind.setReqEnd( getAttribute( le, REQEND ) );
+        auditBind.setReqMethod( getAttribute( le, REQMETHOD ) );
+        auditBind.setReqResult( getAttribute( le, REQRESULT ) );
+        auditBind.setReqSession( getAttribute( le, REQSESSION ) );
+        auditBind.setReqStart( getAttribute( le, REQSTART ) );
+        auditBind.setReqType( getAttribute( le, REQTYPE ) );
+        auditBind.setReqVersion( getAttribute( le, REQVERSION ) );
+        auditBind.setStructuralObjectClass( getAttribute( le, STRUCTURALOBJECTCLASS ) );
+
+        return auditBind;
+    }
+
+
+    /**
+     * @param le
+     * @return
+     * @throws LdapInvalidAttributeValueException 
+     * @throws LdapException
+     */
+    private AuthZ getAuthzEntityFromLdapEntry( Entry le, long sequence ) throws LdapInvalidAttributeValueException
+    {
+
+        // these attrs also on audit bind OC:
+        AuthZ authZ = new ObjectFactory().createAuthZ();
+        authZ.setSequenceId( sequence );
+        authZ.setCreateTimestamp( getAttribute( le, CREATETIMESTAMP ) );
+        authZ.setCreatorsName( getAttribute( le, CREATORSNAME ) );
+        authZ.setEntryCSN( getAttribute( le, ENTRYCSN ) );
+        authZ.setEntryDN( getAttribute( le, ENTRYDN ) );
+        authZ.setEntryUUID( getAttribute( le, ENTRYUUID ) );
+        authZ.setHasSubordinates( getAttribute( le, HASSUBORDINATES ) );
+        authZ.setModifiersName( getAttribute( le, MODIFIERSNAME ) );
+        authZ.setModifyTimestamp( getAttribute( le, MODIFYTIMESTAMP ) );
+        authZ.setObjectClass( getAttribute( le, OBJECTCLASS ) );
+        authZ.setReqAuthzID( getAttribute( le, REQUAUTHZID ) );
+        authZ.setReqControls( getAttribute( le, REQCONTROLS ) );
+        authZ.setReqDN( getAttribute( le, REQDN ) );
+        authZ.setReqEnd( getAttribute( le, REQEND ) );
+        authZ.setReqResult( getAttribute( le, REQRESULT ) );
+        authZ.setReqSession( getAttribute( le, REQSESSION ) );
+        authZ.setReqStart( getAttribute( le, REQSTART ) );
+        authZ.setReqType( getAttribute( le, REQTYPE ) );
+        authZ.setStructuralObjectClass( getAttribute( le, STRUCTURALOBJECTCLASS ) );
+
+        // these attrs only on audit search OC:
+        authZ.setReqAttr( getAttribute( le, REQATTR ) );
+        authZ.setReqAttrsOnly( getAttribute( le, REQATTRSONLY ) );
+        authZ.setReqDerefAliases( getAttribute( le, REQDREFALIASES ) );
+        authZ.setReqEntries( getAttribute( le, REQENTRIES ) );
+        authZ.setReqFilter( getAttribute( le, REQFILTER ) );
+        authZ.setReqScope( getAttribute( le, REQSCOPE ) );
+        authZ.setReqSizeLimit( getAttribute( le, REQSIZELIMIT ) );
+        authZ.setReqTimeLimit( getAttribute( le, REQTIMELIMIT ) );
+
+        return authZ;
+    }
+
+
+    private Mod getModEntityFromLdapEntry( Entry le, long sequence ) throws LdapInvalidAttributeValueException
+    {
+        Mod mod = new ObjectFactory().createMod();
+        mod.setSequenceId( sequence );
+        mod.setObjectClass( getAttribute( le, OBJECTCLASS ) );
+        mod.setReqAuthzID( getAttribute( le, REQUAUTHZID ) );
+        mod.setReqDN( getAttribute( le, REQDN ) );
+        mod.setReqEnd( getAttribute( le, REQEND ) );
+        mod.setReqResult( getAttribute( le, REQRESULT ) );
+        mod.setReqSession( getAttribute( le, REQSESSION ) );
+        mod.setReqStart( getAttribute( le, REQSTART ) );
+        mod.setReqType( getAttribute( le, REQTYPE ) );
+        mod.setReqMod( getAttributes( le, REQMOD ) );
+
+        return mod;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/OrgUnitDAO.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/OrgUnitDAO.java b/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/OrgUnitDAO.java
new file mode 100755
index 0000000..2641be5
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/rbac/dao/apache/OrgUnitDAO.java
@@ -0,0 +1,706 @@
+/*
+ *   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.apache.directory.fortress.core.rbac.dao.apache;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.directory.fortress.core.CreateException;
+import org.apache.directory.fortress.core.FinderException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.ObjectFactory;
+import org.apache.directory.fortress.core.RemoveException;
+import org.apache.directory.fortress.core.UpdateException;
+import org.apache.directory.fortress.core.ldap.ApacheDsDataProvider;
+import org.apache.directory.fortress.core.rbac.Graphable;
+import org.apache.directory.fortress.core.rbac.OrgUnit;
+import org.apache.directory.fortress.core.rbac.PsoUtil;
+import org.apache.directory.fortress.core.rbac.UsoUtil;
+
+
+/**
+ * This class provides dataaccess to the OrgUnit datasets in LDAP.
+ * <p/>
+ * The OrgUnitDAO maintains the following structural and aux object classes:
+ * <h4>1. organizationalUnit Structural Object Class is used to store basic attributes like ou and description</h4>
+ * <ul>
+ * <li>  ------------------------------------------
+ * <li> <code>objectclass ( 2.5.6.5 NAME 'organizationalUnit'</code>
+ * <li> <code>DESC 'RFC2256: an organizational unit'</code>
+ * <li> <code>SUP top STRUCTURAL</code>
+ * <li> <code>MUST ou</code>
+ * <li> <code>MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $</code>
+ * <li> <code>x121Address $ registeredAddress $ destinationIndicator $</code>
+ * <li> <code>preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $</code>
+ * <li> <code>telephoneNumber $ internationaliSDNNumber $</code>
+ * <li> <code>facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $</code>
+ * <li> <code>postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>2. ftOrgUnit Structural objectclass is used to store the OrgUnit internal id</h4>
+ * <ul>                                                              org.apache.directory.fortress.arbac.
+ * <li>  ------------------------------------------
+ * <li> <code> objectclass	( 1.3.6.1.4.1.38088.2.6</code>
+ * <li> <code>NAME 'ftOrgUnit'</code>
+ * <li> <code>DESC 'Fortress OrgUnit Class'</code>
+ * <li> <code>SUP organizationalunit</code>
+ * <li> <code>STRUCTURAL</code>
+ * <li> <code>MUST ( ftId ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <h4>3. ftMods AUXILIARY Object Class is used to store Fortress audit variables on target entity</h4>
+ * <ul>
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.3.4</code>
+ * <li> <code>NAME 'ftMods'</code>
+ * <li> <code>DESC 'Fortress Modifiers AUX Object Class'</code>
+ * <li> <code>AUXILIARY</code>
+ * <li> <code>MAY (</code>
+ * <li> <code>ftModifier $</code>
+ * <li> <code>ftModCode $</code>
+ * <li> <code>ftModId ) )</code>
+ * <li>  ------------------------------------------
+ * </ul>
+ * <p/>
+ * This class is thread safe.
+ *
+ * @author Shawn McKinney
+ * @created September 18, 2010
+ */
+public final class OrgUnitDAO extends ApacheDsDataProvider implements org.apache.directory.fortress.core.rbac.dao.OrgUnitDAO
+{
+    private static final String CLS_NM = OrgUnitDAO.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+    private static final String ORGUNIT_OBJECT_CLASS_NM = "ftOrgUnit";
+
+    private static final String ORGUNIT_OBJ_CLASS[] =
+        {
+            GlobalIds.TOP, ORGUNIT_OBJECT_CLASS_NM, GlobalIds.FT_MODIFIER_AUX_OBJECT_CLASS_NAME
+    };
+    private static final String[] ORGUNIT_ATRS =
+        {
+            GlobalIds.FT_IID, GlobalIds.OU, GlobalIds.DESC, GlobalIds.PARENT_NODES
+    };
+
+    private static final String[] ORGUNIT_ATR =
+        {
+            GlobalIds.OU
+    };
+
+
+    /**
+     * @param entity
+     * @return
+     * @throws org.apache.directory.fortress.core.CreateException
+     *
+     */
+    public final OrgUnit create( OrgUnit entity ) throws CreateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            Entry entry = new DefaultEntry( dn );
+            entry.add( GlobalIds.OBJECT_CLASS, ORGUNIT_OBJ_CLASS );
+            entity.setId();
+            entry.add( GlobalIds.FT_IID, entity.getId() );
+
+            if ( entity.getDescription() != null && entity.getDescription().length() > 0 )
+            {
+                entry.add( GlobalIds.DESC, entity.getDescription() );
+            }
+
+            // organizational name requires OU attribute:
+            entry.add( GlobalIds.OU, entity.getName() );
+
+            // These multi-valued attributes are optional.  The utility function will return quietly if no items are loaded into collection:
+            loadAttrs( entity.getParents(), entry, GlobalIds.PARENT_NODES );
+
+            ld = getAdminConnection();
+            add( ld, entry, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "create orgUnit name [" + entity.getName() + "] type [" + entity.getType()
+                + "] root [" + dn + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( entity.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_ADD_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_ADD_FAILED_USER;
+
+            }
+
+            throw new CreateException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * @param entity
+     * @return
+     * @throws org.apache.directory.fortress.core.UpdateException
+     *
+     */
+    public final OrgUnit update( OrgUnit entity ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+
+            if ( entity.getDescription() != null && entity.getDescription().length() > 0 )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE, GlobalIds.DESC, entity.getDescription() ) );
+            }
+
+            loadAttrs( entity.getParents(), mods, GlobalIds.PARENT_NODES );
+
+            if ( mods.size() > 0 )
+            {
+                ld = getAdminConnection();
+                modify( ld, dn, mods, entity );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "update orgUnit name [" + entity.getName() + "] type [" + entity.getType()
+                + "] root [" + dn + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( entity.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_UPDATE_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_UPDATE_FAILED_USER;
+            }
+
+            throw new UpdateException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * @param entity
+     * @throws org.apache.directory.fortress.core.UpdateException
+     *
+     */
+    public final void deleteParent( OrgUnit entity ) throws UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, GlobalIds.PARENT_NODES ) );
+            ld = getAdminConnection();
+            modify( ld, dn, mods, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "deleteParent orgUnit name [" + entity.getName() + "] type [" + entity.getType()
+                + "] root [" + dn + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( entity.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_REMOVE_PARENT_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_REMOVE_PARENT_FAILED_USER;
+            }
+
+            throw new UpdateException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+    }
+
+
+    /**
+     * @param entity
+     * @return
+     * @throws org.apache.directory.fortress.core.RemoveException
+     *
+     */
+    public final OrgUnit remove( OrgUnit entity ) throws RemoveException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            ld = getAdminConnection();
+            delete( ld, dn, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "remove orgUnit name [" + entity.getName() + "] type [" + entity.getType()
+                + "] root [" + dn + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( entity.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_DELETE_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_DELETE_FAILED_USER;
+            }
+
+            throw new RemoveException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return entity;
+    }
+
+
+    /**
+     * @param entity
+     * @return
+     * @throws FinderException
+     *
+     */
+    public final OrgUnit findByKey( OrgUnit entity ) throws FinderException
+    {
+        OrgUnit oe = null;
+        LdapConnection ld = null;
+        String dn = getDn( entity );
+
+        try
+        {
+            ld = getAdminConnection();
+            Entry findEntry = read( ld, dn, ORGUNIT_ATRS );
+
+            if ( findEntry == null )
+            {
+                String warning = "findByKey orgUnit name [" + entity.getName() + "] type ["
+                    + entity.getType() + "] COULD NOT FIND ENTRY for dn [" + dn + "]";
+                int errCode;
+
+                if ( entity.getType() == OrgUnit.Type.PERM )
+                {
+                    errCode = GlobalErrIds.ORG_NOT_FOUND_PERM;
+                }
+                else
+                {
+                    errCode = GlobalErrIds.ORG_NOT_FOUND_USER;
+                }
+
+                throw new FinderException( errCode, warning );
+            }
+
+            oe = getEntityFromLdapEntry( findEntry, 0, entity.getContextId() );
+        }
+        catch ( LdapNoSuchObjectException e )
+        {
+            String warning = "findByKey orgUnit name [" + entity.getName() + "] type ["
+                + entity.getType() + "] COULD NOT FIND ENTRY for dn [" + dn + "]";
+            int errCode;
+
+            if ( entity.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_NOT_FOUND_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_NOT_FOUND_USER;
+            }
+            throw new FinderException( errCode, warning );
+        }
+        catch ( LdapException e )
+        {
+            String error = "findByKey orgUnitName [" + entity.getName() + "] type [" + entity.getType()
+                + "] dn [" + dn + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( entity.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_READ_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_READ_FAILED_USER;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return oe;
+    }
+
+
+    /**
+     * @param orgUnit
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    public final List<OrgUnit> findOrgs( OrgUnit orgUnit ) throws FinderException
+    {
+        List<OrgUnit> orgUnitList = new ArrayList<>();
+        LdapConnection ld = null;
+        String orgUnitRoot = getOrgRoot( orgUnit );
+
+        try
+        {
+            String searchVal = encodeSafeText( orgUnit.getName(), GlobalIds.ROLE_LEN );
+            String filter = GlobalIds.FILTER_PREFIX + ORGUNIT_OBJECT_CLASS_NM + ")("
+                + GlobalIds.OU + "=" + searchVal + "*))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, orgUnitRoot,
+                SearchScope.ONELEVEL, filter, ORGUNIT_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                orgUnitList
+                    .add( getEntityFromLdapEntry( searchResults.getEntry(), sequence++, orgUnit.getContextId() ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "findOrgs search val [" + orgUnit.getName() + "] type [" + orgUnit.getType()
+                + "] root [" + orgUnitRoot + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( orgUnit.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_SEARCH_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_SEARCH_FAILED_USER;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "findOrgs search val [" + orgUnit.getName() + "] type [" + orgUnit.getType()
+                + "] root [" + orgUnitRoot + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( orgUnit.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_SEARCH_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_SEARCH_FAILED_USER;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return orgUnitList;
+    }
+
+
+    /**
+     *
+     * @param orgUnit
+     * @return
+     * @throws FinderException
+     */
+    public final Set<String> getOrgs( OrgUnit orgUnit ) throws FinderException
+    {
+        Set<String> ouSet = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+        LdapConnection ld = null;
+        String orgUnitRoot = getOrgRoot( orgUnit );
+
+        try
+        {
+            String filter = "(objectclass=" + ORGUNIT_OBJECT_CLASS_NM + ")";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, orgUnitRoot,
+                SearchScope.ONELEVEL, filter, ORGUNIT_ATR, false, GlobalIds.BATCH_SIZE );
+
+            while ( searchResults.next() )
+            {
+                ouSet.add( getAttribute( searchResults.getEntry(), GlobalIds.OU ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "getOrgs type [" + orgUnit.getType() + "] root [" + orgUnitRoot
+                + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( orgUnit.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_GET_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_GET_FAILED_USER;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "getOrgs type [" + orgUnit.getType() + "] root [" + orgUnitRoot
+                + "] caught LdapException=" + e;
+            int errCode;
+
+            if ( orgUnit.getType() == OrgUnit.Type.PERM )
+            {
+                errCode = GlobalErrIds.ORG_GET_FAILED_PERM;
+            }
+            else
+            {
+                errCode = GlobalErrIds.ORG_GET_FAILED_USER;
+            }
+
+            throw new FinderException( errCode, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return ouSet;
+    }
+
+
+    /**
+      *
+      * @param orgUnit
+      * @return
+      * @throws FinderException
+      */
+    public final List<Graphable> getAllDescendants( OrgUnit orgUnit ) throws FinderException
+    {
+        String orgUnitRoot = getOrgRoot( orgUnit );
+        String[] DESC_ATRS =
+            { GlobalIds.OU, GlobalIds.PARENT_NODES };
+        List<Graphable> descendants = new ArrayList<>();
+        LdapConnection ld = null;
+        String filter = null;
+
+        try
+        {
+            filter = GlobalIds.FILTER_PREFIX + ORGUNIT_OBJECT_CLASS_NM + ")("
+                + GlobalIds.PARENT_NODES + "=*))";
+            ld = getAdminConnection();
+            SearchCursor searchResults = search( ld, orgUnitRoot,
+                SearchScope.ONELEVEL, filter, DESC_ATRS, false, GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                descendants.add( unloadDescendants( searchResults.getEntry(), sequence++, orgUnit.getContextId() ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "getAllDescendants filter [" + filter + "] caught LdapException="
+                + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        catch ( CursorException e )
+        {
+            String error = "getAllDescendants filter [" + filter + "] caught LdapException="
+                + e.getMessage();
+            throw new FinderException( GlobalErrIds.ARLE_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return descendants;
+    }
+
+
+    /**
+     * @param orgUnit
+     * @return
+     */
+    private String getDn( OrgUnit orgUnit )
+    {
+        String dn = null;
+
+        switch ( orgUnit.type )
+        {
+            case USER:
+                dn = GlobalIds.OU + "=" + orgUnit.getName() + ","
+                    + getRootDn( orgUnit.getContextId(), GlobalIds.OSU_ROOT );
+                break;
+
+            case PERM:
+                dn = GlobalIds.OU + "=" + orgUnit.getName() + ","
+                    + getRootDn( orgUnit.getContextId(), GlobalIds.PSU_ROOT );
+                break;
+
+            default:
+                String warning = "getDn invalid type";
+                LOG.warn( warning );
+                break;
+        }
+
+        return dn;
+    }
+
+
+    /**
+     *
+     * @param orgUnit
+     * @return
+     */
+    private String getOrgRoot( OrgUnit orgUnit )
+    {
+        String dn = null;
+
+        switch ( orgUnit.type )
+        {
+            case USER:
+                dn = getRootDn( orgUnit.getContextId(), GlobalIds.OSU_ROOT );
+                break;
+
+            case PERM:
+                dn = getRootDn( orgUnit.getContextId(), GlobalIds.PSU_ROOT );
+                break;
+
+            default:
+                String warning = "getOrgRootDn invalid type";
+                LOG.warn( warning );
+                break;
+        }
+
+        return dn;
+    }
+
+
+    /**
+    *
+    * @param le
+    * @param sequence
+    * @param contextId
+    * @return
+     * @throws LdapInvalidAttributeValueException 
+    * @throws LdapException
+    */
+    private Graphable unloadDescendants( Entry le, long sequence, String contextId )
+        throws LdapInvalidAttributeValueException
+    {
+        OrgUnit entity = new ObjectFactory().createOrgUnit();
+        entity.setSequenceId( sequence );
+        entity.setName( getAttribute( le, GlobalIds.OU ) );
+        entity.setParents( getAttributeSet( le, GlobalIds.PARENT_NODES ) );
+
+        return entity;
+    }
+
+
+    /**
+     *
+     * @param le
+     * @param sequence
+     * @param contextId
+     * @return
+     * @throws LdapInvalidAttributeValueException 
+     * @throws LdapException
+     */
+    private OrgUnit getEntityFromLdapEntry( Entry le, long sequence, String contextId )
+        throws LdapInvalidAttributeValueException
+    {
+        OrgUnit entity = new ObjectFactory().createOrgUnit();
+        entity.setSequenceId( sequence );
+        entity.setId( getAttribute( le, GlobalIds.FT_IID ) );
+        entity.setName( getAttribute( le, GlobalIds.OU ) );
+        entity.setDescription( getAttribute( le, GlobalIds.DESC ) );
+        String dn = le.getDn().getName();
+
+        if ( dn.contains( getRootDn( contextId, GlobalIds.PSU_ROOT ) ) )
+        {
+            entity.setType( OrgUnit.Type.PERM );
+            //entity.setParents(PsoUtil.getParents(entity.getName().toUpperCase(), contextId));
+            entity.setChildren( PsoUtil.getChildren( entity.getName().toUpperCase(), contextId ) );
+        }
+        else if ( dn.contains( getRootDn( contextId, GlobalIds.OSU_ROOT ) ) )
+        {
+            entity.setType( OrgUnit.Type.USER );
+            //entity.setParents(UsoUtil.getParents(entity.getName().toUpperCase(), contextId));
+            entity.setChildren( UsoUtil.getChildren( entity.getName().toUpperCase(), contextId ) );
+        }
+
+        entity.setParents( getAttributeSet( le, GlobalIds.PARENT_NODES ) );
+
+        return entity;
+    }
+}
\ No newline at end of file