directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From smckin...@apache.org
Subject [42/51] [partial] directory-fortress-core git commit: FC-109 - rename rbac package to impl
Date Tue, 02 Jun 2015 18:37:07 GMT
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/GroupDAO.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/GroupDAO.java b/src/main/java/org/apache/directory/fortress/core/impl/GroupDAO.java
new file mode 100755
index 0000000..11cfed5
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/GroupDAO.java
@@ -0,0 +1,508 @@
+/*
+
+ *   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.impl;
+
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.api.ldap.model.constants.SchemaConstants;
+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.fortress.core.model.Group;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.fortress.core.FinderException;
+import org.apache.directory.fortress.core.model.ObjectFactory;
+import org.apache.directory.fortress.core.UpdateException;
+import org.apache.directory.fortress.core.util.Config;
+import org.apache.directory.fortress.core.ldap.ApacheDsDataProvider;
+import org.apache.directory.fortress.core.model.User;
+import org.apache.directory.fortress.core.util.attr.AttrHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.directory.fortress.core.CreateException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.RemoveException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Contains the Group node for LDAP Directory Information Tree.
+ * This class is thread safe.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+final class GroupDAO extends ApacheDsDataProvider
+{
+    private static final String CLS_NM = GroupDAO.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+    private static final String GROUP_OBJECT_CLASS = "group.objectclass";
+    private static final String GROUP_OBJECT_CLASS_IMPL = Config.getProperty( GROUP_OBJECT_CLASS );
+    private static final String GROUP_PROTOCOL_ATTR = "group.protocol";
+    private static final String GROUP_PROTOCOL_ATTR_IMPL = Config.getProperty( GROUP_PROTOCOL_ATTR );
+    private static final String GROUP_PROPERTY_ATTR = "group.properties";
+    private static final String GROUP_PROPERTY_ATTR_IMPL = Config.getProperty( GROUP_PROPERTY_ATTR );
+    private static final String GROUP_OBJ_CLASS[] =
+        { SchemaConstants.TOP_OC, GROUP_OBJECT_CLASS_IMPL };
+    private static final String[] GROUP_ATRS =
+        {
+            SchemaConstants.CN_AT,
+            SchemaConstants.DESCRIPTION_AT,
+            GROUP_PROTOCOL_ATTR_IMPL,
+            GROUP_PROPERTY_ATTR_IMPL,
+            SchemaConstants.MEMBER_AT };
+
+
+    /**
+     * Package private default constructor.
+     */
+    GroupDAO()
+    {
+    }
+
+
+    /**
+     * @param group
+     * @throws org.apache.directory.fortress.core.CreateException
+     *
+     */
+    Group create( Group group ) throws CreateException
+    {
+        LdapConnection ld = null;
+        String nodeDn = getDn( group.getName(), group.getContextId() );
+
+        try
+        {
+            LOG.debug( "create group dn [{}]", nodeDn );
+            Entry myEntry = new DefaultEntry( nodeDn );
+            myEntry.add( SchemaConstants.OBJECT_CLASS_AT, GROUP_OBJ_CLASS );
+            myEntry.add( SchemaConstants.CN_AT, group.getName() );
+            myEntry.add( GROUP_PROTOCOL_ATTR_IMPL, group.getProtocol() );
+            loadAttrs( group.getMembers(), myEntry, SchemaConstants.MEMBER_AT );
+            loadProperties( group.getProperties(), myEntry, GROUP_PROPERTY_ATTR_IMPL, '=' );
+
+            if ( StringUtils.isNotEmpty( group.getDescription() ) )
+            {
+                myEntry.add( SchemaConstants.DESCRIPTION_AT, group.getDescription() );
+            }
+
+            ld = getAdminConnection();
+            add( ld, myEntry );
+        }
+        catch ( LdapException e )
+        {
+            String error = "create group node dn [" + nodeDn + "] caught LDAPException=" + e.getMessage();
+            throw new CreateException( GlobalErrIds.GROUP_ADD_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return group;
+    }
+
+
+    /**
+     * @param group
+     * @return
+     * @throws org.apache.directory.fortress.core.CreateException
+     *
+     */
+    Group update( Group group ) throws FinderException, UpdateException
+    {
+        LdapConnection ld = null;
+        String nodeDn = getDn( group.getName(), group.getContextId() );
+
+        try
+        {
+            LOG.debug( "update group dn [{}]", nodeDn );
+            List<Modification> mods = new ArrayList<Modification>();
+
+            if ( StringUtils.isNotEmpty( group.getDescription() ) )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE, SchemaConstants.DESCRIPTION_AT, group.getDescription() ) );
+            }
+
+            if ( StringUtils.isNotEmpty( group.getProtocol() ) )
+            {
+                mods.add( new DefaultModification(
+                    ModificationOperation.REPLACE_ATTRIBUTE, GROUP_PROTOCOL_ATTR_IMPL, group.getProtocol() ) );
+            }
+
+            loadAttrs( group.getMembers(), mods, SchemaConstants.MEMBER_AT );
+            loadProperties( group.getProperties(), mods, GROUP_PROPERTY_ATTR_IMPL, true, '=' );
+
+            if ( mods.size() > 0 )
+            {
+                ld = getAdminConnection();
+                modify( ld, nodeDn, mods, group );
+            }
+        }
+        catch ( LdapException e )
+        {
+            String error = "update group node dn [" + nodeDn + "] caught LDAPException=" + e.getMessage();
+            throw new UpdateException( GlobalErrIds.GROUP_UPDATE_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return get( group );
+    }
+
+
+    Group add( Group group, String key, String value ) throws FinderException, CreateException
+    {
+        LdapConnection ld = null;
+        String nodeDn = getDn( group.getName(), group.getContextId() );
+
+        try
+        {
+            LOG.debug( "add group property dn [{}], key [{}], value [{}]", nodeDn, key, value );
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification(
+                ModificationOperation.ADD_ATTRIBUTE, GROUP_PROPERTY_ATTR_IMPL, key + "=" + value ) );
+            ld = getAdminConnection();
+            modify( ld, nodeDn, mods, group );
+        }
+        catch ( LdapException e )
+        {
+            String error = "update group property node dn [" + nodeDn + "] caught LDAPException=" + e.getMessage();
+            throw new CreateException( GlobalErrIds.GROUP_ADD_PROPERTY_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return get( group );
+    }
+
+
+    Group delete( Group group, String key, String value ) throws FinderException, RemoveException
+    {
+        LdapConnection ld = null;
+        String nodeDn = getDn( group.getName(), group.getContextId() );
+
+        try
+        {
+            LOG.debug( "delete group property dn [{}], key [{}], value [{}]", nodeDn, key, value );
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification(
+                ModificationOperation.REMOVE_ATTRIBUTE, GROUP_PROPERTY_ATTR_IMPL, key + "=" + value ) );
+            ld = getAdminConnection();
+            modify( ld, nodeDn, mods, group );
+        }
+        catch ( LdapException e )
+        {
+            String error = "delete group property node dn [" + nodeDn + "] caught LDAPException=" + e.getMessage();
+            throw new RemoveException( GlobalErrIds.GROUP_DELETE_PROPERTY_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return get( group );
+    }
+
+
+    /**
+     * This method will remove group node from diretory.
+     *
+     * @param group
+     * @throws org.apache.directory.fortress.core.RemoveException
+     *
+     */
+    Group remove( Group group ) throws RemoveException
+    {
+        LdapConnection ld = null;
+        String nodeDn = getDn( group.getName(), group.getContextId() );
+        LOG.debug( "remove group dn [{}]", nodeDn );
+        try
+        {
+            ld = getAdminConnection();
+            deleteRecursive( ld, nodeDn );
+        }
+        catch ( CursorException e )
+        {
+            String error = "remove group node dn [" + nodeDn + "] caught CursorException="
+                + e.getMessage();
+            throw new RemoveException( GlobalErrIds.GROUP_DELETE_FAILED, error, e );
+        }
+        catch ( LdapException e )
+        {
+            String error = "remove group node dn [" + nodeDn + "] caught LDAPException=" + e.getMessage();
+            throw new RemoveException( GlobalErrIds.GROUP_DELETE_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return group;
+    }
+
+
+    /**
+     * @param entity
+     * @param userDn
+     * @return
+     * @throws org.apache.directory.fortress.core.UpdateException
+     *
+     */
+    Group assign( Group entity, String userDn ) throws FinderException, UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity.getName(), entity.getContextId() );
+        LOG.debug( "assign group property dn [{}], member dn [{}]", dn, userDn );
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification(
+                ModificationOperation.ADD_ATTRIBUTE, SchemaConstants.MEMBER_AT, userDn ) );
+            ld = getAdminConnection();
+            modify( ld, dn, mods, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "assign group name [" + entity.getName() + "] user dn [" + userDn + "] caught " +
+                "LDAPException=" + e.getMessage();
+            throw new UpdateException( GlobalErrIds.GROUP_USER_ASSIGN_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return get( entity );
+    }
+
+
+    /**
+     * @param entity
+     * @param userDn
+     * @return
+     * @throws org.apache.directory.fortress.core.UpdateException
+     *
+     */
+    Group deassign( Group entity, String userDn ) throws FinderException, UpdateException
+    {
+        LdapConnection ld = null;
+        String dn = getDn( entity.getName(), entity.getContextId() );
+        LOG.debug( "deassign group property dn [{}], member dn [{}]", dn, userDn );
+
+        try
+        {
+            List<Modification> mods = new ArrayList<Modification>();
+            mods.add( new DefaultModification(
+                ModificationOperation.REMOVE_ATTRIBUTE, SchemaConstants.MEMBER_AT, userDn ) );
+
+            ld = getAdminConnection();
+            modify( ld, dn, mods, entity );
+        }
+        catch ( LdapException e )
+        {
+            String error = "deassign group name [" + entity.getName() + "] user dn [" + userDn + "] caught " +
+                "LDAPException=" + e.getMessage();
+            throw new UpdateException( GlobalErrIds.GROUP_USER_DEASSIGN_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return get( entity );
+    }
+
+
+    /**
+     * @param group
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    Group get( Group group ) throws FinderException
+    {
+        Group entity = null;
+        LdapConnection ld = null;
+        String dn = getDn( group.getName(), group.getContextId() );
+
+        try
+        {
+            ld = getAdminConnection();
+            Entry findEntry = read( ld, dn, GROUP_ATRS );
+            if ( findEntry == null )
+            {
+                String warning = "No Group entry found dn [" + dn + "]";
+                throw new FinderException( GlobalErrIds.GROUP_NOT_FOUND, warning );
+            }
+            entity = unloadLdapEntry( findEntry, 0 );
+        }
+        catch ( LdapNoSuchObjectException e )
+        {
+            String warning = "read Obj COULD NOT FIND ENTRY for dn [" + dn + "]";
+            throw new FinderException( GlobalErrIds.GROUP_NOT_FOUND, warning, e );
+        }
+        catch ( LdapException e )
+        {
+            String error = "read dn [" + dn + "] LdapException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.GROUP_READ_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+        return entity;
+    }
+
+
+    /**
+     * @param group
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    List<Group> find( Group group ) throws FinderException
+    {
+        List<Group> groupList = new ArrayList<>();
+        LdapConnection ld = null;
+        SearchCursor searchResults;
+        String groupRoot = getRootDn( group.getContextId(), GlobalIds.GROUP_ROOT );
+        String filter = null;
+
+        try
+        {
+            String searchVal = encodeSafeText( group.getName(), GlobalIds.ROLE_LEN );
+            filter = GlobalIds.FILTER_PREFIX + GROUP_OBJECT_CLASS_IMPL + ")(" + SchemaConstants.CN_AT + "=" + searchVal
+                + "*))";
+            ld = getAdminConnection();
+            searchResults = search( ld, groupRoot, SearchScope.ONELEVEL, filter, GROUP_ATRS, false,
+                GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+            while ( searchResults.next() )
+            {
+                groupList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( CursorException e )
+        {
+            String error = "find filter [" + filter + "] caught CursorException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.GROUP_SEARCH_FAILED, error, e );
+        }
+        catch ( LdapException e )
+        {
+            String error = "find filter [" + filter + "] caught LDAPException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.GROUP_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return groupList;
+    }
+
+
+    /**
+     * @param user
+     * @return
+     * @throws org.apache.directory.fortress.core.FinderException
+     *
+     */
+    List<Group> find( User user ) throws FinderException
+    {
+        List<Group> groupList = new ArrayList<>();
+        LdapConnection ld = null;
+        SearchCursor searchResults;
+        String groupRoot = getRootDn( user.getContextId(), GlobalIds.GROUP_ROOT );
+        String filter = null;
+
+        try
+        {
+            encodeSafeText( user.getUserId(), GlobalIds.USERID_LEN );
+            filter = GlobalIds.FILTER_PREFIX + GROUP_OBJECT_CLASS_IMPL + ")(" + SchemaConstants.MEMBER_AT + "="
+                + user.getDn() + "))";
+            ld = getAdminConnection();
+            searchResults = search( ld, groupRoot, SearchScope.ONELEVEL, filter, GROUP_ATRS, false,
+                GlobalIds.BATCH_SIZE );
+            long sequence = 0;
+
+            while ( searchResults.next() )
+            {
+                groupList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+            }
+        }
+        catch ( CursorException e )
+        {
+            String error = "find filter [" + filter + "] caught CursorException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.GROUP_SEARCH_FAILED, error, e );
+        }
+        catch ( LdapException e )
+        {
+            String error = "find filter [" + filter + "] caught LDAPException=" + e.getMessage();
+            throw new FinderException( GlobalErrIds.GROUP_SEARCH_FAILED, error, e );
+        }
+        finally
+        {
+            closeAdminConnection( ld );
+        }
+
+        return groupList;
+    }
+
+
+    /**
+     * @param le
+     * @param sequence
+     * @return
+     * @throws LdapException
+     */
+    private Group unloadLdapEntry( Entry le, long sequence )
+        throws LdapInvalidAttributeValueException
+    {
+        Group entity = new ObjectFactory().createGroup();
+        entity.setName( getAttribute( le, SchemaConstants.CN_AT ) );
+        entity.setDescription( getAttribute( le, SchemaConstants.DESCRIPTION_AT ) );
+        entity.setProtocol( getAttribute( le, GROUP_PROTOCOL_ATTR_IMPL ) );
+        entity.setMembers( getAttributes( le, SchemaConstants.MEMBER_AT ) );
+        entity.setMemberDn( true );
+        entity.setProperties( AttrHelper.getProperties( getAttributes( le, GROUP_PROPERTY_ATTR_IMPL ), '=' ) );
+        entity.setSequenceId( sequence );
+
+        return entity;
+    }
+
+
+    private String getDn( String name, String contextId )
+    {
+        return SchemaConstants.CN_AT + "=" + name + "," + getRootDn( contextId, GlobalIds.GROUP_ROOT );
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/GroupMgrImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/GroupMgrImpl.java b/src/main/java/org/apache/directory/fortress/core/impl/GroupMgrImpl.java
new file mode 100755
index 0000000..770c2b9
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/GroupMgrImpl.java
@@ -0,0 +1,258 @@
+/*
+ *   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.impl;
+
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GroupMgr;
+import org.apache.directory.fortress.core.ReviewMgr;
+import org.apache.directory.fortress.core.ReviewMgrFactory;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.model.Group;
+import org.apache.directory.fortress.core.model.User;
+import org.apache.directory.fortress.core.util.ObjUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This Manager impl supplies CRUD methods used to manage groups stored within the ldap directory.
+ * LDAP group nodes are used for utility and security functions within various systems and apps.
+ * <p/>
+ * This class is thread safe.
+ * <p/>
+
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class GroupMgrImpl extends Manageable implements GroupMgr
+{
+    private static final String CLS_NM = GroupMgrImpl.class.getName();
+    private static final GroupP GROUP_P = new GroupP();
+
+    /**
+     * Create a new group node.  Must have a name and at least one member.
+     *
+     * @param group contains {@link org.apache.directory.fortress.core.model.Group}.
+     * @return {@link org.apache.directory.fortress.core.model.Group} containing entity just added.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    @Override
+    public Group add( Group group ) throws org.apache.directory.fortress.core.SecurityException
+    {
+        String methodName = "add";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        if(!group.isMemberDn())
+        {
+            loadUserDns( group );
+        }
+
+        return GROUP_P.add( group );
+    }
+
+    /**
+     * Modify existing group node.  The name is required.  Does not update members or properties.
+     * Use {@link GroupMgr#add( Group group, String key, String value )}, {@link GroupMgr#delete( Group group, String key, String value )},
+     * {@link GroupMgr#assign( Group group, String member) }, or {@link GroupMgr#deassign( Group group, String member) } for multi-occurring attributes.
+     *
+     * @param group contains {@link Group}.
+     * @return {@link Group} containing entity just modified.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    @Override
+    public Group update( Group group ) throws SecurityException
+    {
+        String methodName = "update";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        return GROUP_P.update( group );
+    }
+
+    /**
+     * Delete existing group node.  The name is required.
+     *
+     * @param group contains {@link Group}.
+     * @return {@link Group} containing entity just removed.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    @Override
+    public Group delete( Group group ) throws SecurityException
+    {
+        String methodName = "delete";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        return GROUP_P.delete( group );
+    }
+
+    /**
+     * Add a property to an existing group node.  The name is required.
+     *
+     * @param group contains {@link Group}.
+     * @param key contains the property key.
+     * @param value contains contains the property value.
+     * @return {@link Group} containing entity just modified.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    public Group add( Group group, String key, String value ) throws SecurityException
+    {
+        String methodName = "addProperty";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        return GROUP_P.add( group, key, value );
+    }
+
+    /**
+     * Delete existing group node.  The name is required.
+     *
+     * @param group contains {@link Group}.
+     * @param key contains the property key.
+     * @param value contains contains the property value.
+     * @return {@link Group} containing entity just modified.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    public Group delete( Group group, String key, String value ) throws SecurityException
+    {
+        String methodName = "deleteProperty";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        return GROUP_P.delete( group, key, value );
+    }
+
+    /**
+     * Read an existing group node.  The name is required.
+     *
+     * @param group contains {@link Group} with name field set with an existing group name.
+     * @return {@link Group} containing entity found.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    @Override
+    public Group read( Group group ) throws SecurityException
+    {
+        String methodName = "read";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        return GROUP_P.read( group );
+    }
+
+    /**
+     * Search using a full or partial group node.  The name is required.
+     *
+     * @param group contains {@link Group}.
+     * @return List of type {@link Group} containing entities found.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    @Override
+    public List<Group> find( Group group ) throws SecurityException
+    {
+        String methodName = "find";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        
+        return GROUP_P.search( group );
+    }
+
+    /**
+     * Search for groups by userId.  Member (maps to userId) and is required.
+     *
+     * @param user contains userId that maps to Group member attribute.
+     * @return {@link Group} containing entity just added.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event system error.
+     */
+    public List<Group> find( User user ) throws SecurityException
+    {
+        String methodName = "findWithUsers";
+        assertContext(CLS_NM, methodName, user, GlobalErrIds.USER_NULL);
+        checkAccess(CLS_NM, methodName);
+        loadUserDn( user );
+        
+        return GROUP_P.search( user );
+    }
+
+    /**
+     * Assign a user to an existing group node.  The name is required and userDn are required.
+     *
+     * @param group contains {@link Group}.
+     * @param member is the relative distinguished name (rdn) of an existing user in ldap.
+     * @return {@link Group} containing entity to assign.
+     * @throws org.apache.directory.fortress.core.SecurityException in the event entry already present or other system error.
+     */
+    @Override
+    public Group assign( Group group, String member ) throws SecurityException
+    {
+        String methodName = "assign";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        ReviewMgr reviewMgr = ReviewMgrFactory.createInstance();
+        User user = reviewMgr.readUser( new User( member ) );
+        
+        return GROUP_P.assign( group, user.getDn() );
+    }
+
+    /**
+     * Deassign a user from an existing group node.  The name is required and userDn are required.
+     *
+     * @param group contains {@link Group}.
+     * @param member is the relative distinguished name (rdn) of an existing user in ldap.
+     * @return {@link Group} containing entity to deassign
+     * @throws org.apache.directory.fortress.core.SecurityException in the event entry already present or other system error.
+     */
+    @Override
+    public Group deassign( Group group, String member ) throws SecurityException
+    {
+        String methodName = "deassign";
+        assertContext(CLS_NM, methodName, group, GlobalErrIds.GROUP_NULL);
+        checkAccess(CLS_NM, methodName);
+        ReviewMgr reviewMgr = ReviewMgrFactory.createInstance();
+        User user = reviewMgr.readUser( new User( member ) );
+        
+        return GROUP_P.deassign( group, user.getDn() );
+    }
+
+    private void loadUserDns( Group group ) throws SecurityException
+    {
+        if( ObjUtil.isNotNullOrEmpty( group.getMembers() ))
+        {
+            ReviewMgr reviewMgr = ReviewMgrFactory.createInstance();
+            List<String> userDns = new ArrayList<String>();
+            
+            for( String member : group.getMembers() )
+            {
+                User user = reviewMgr.readUser( new User( member ) );
+                userDns.add( user.getDn() );
+            }
+            
+            group.setMembers( userDns );
+        }
+    }
+
+    private void loadUserDn( User inUser ) throws SecurityException
+    {
+        ReviewMgr reviewMgr = ReviewMgrFactory.createInstance();
+        User outUser = reviewMgr.readUser( inUser );
+        inUser.setDn( outUser.getDn() );
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/GroupP.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/GroupP.java b/src/main/java/org/apache/directory/fortress/core/impl/GroupP.java
new file mode 100755
index 0000000..47cc241
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/GroupP.java
@@ -0,0 +1,230 @@
+/*
+ *   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.impl;
+
+
+import org.apache.directory.api.util.Strings;
+import org.apache.directory.fortress.core.ValidationException;
+import org.apache.directory.fortress.core.model.Group;
+import org.apache.directory.fortress.core.model.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.model.VUtil;
+
+import java.util.List;
+
+
+/**
+ * Process module for the group node of Fortress directory structure.
+ * This class is thread safe.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+final class GroupP
+{
+    private static final String CLS_NM = GroupP.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+    private static GroupDAO gDao = new GroupDAO();
+
+
+    /**
+     * Add a group node to the Directory Information Tree (DIT).
+     *
+     * @param group contains the group entity for target node.
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *          in event of validation or system error.
+     */
+    Group add( Group group ) throws SecurityException
+    {
+        validate( group );
+
+        return gDao.create( group );
+    }
+
+
+    /**
+     * Modify a group node within the Directory Information Tree (DIT).
+     *
+     * @param group contains the group entity for target node.
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *          in event of validation or system error.
+     */
+    Group update( Group group ) throws SecurityException
+    {
+        validate( group );
+
+        return gDao.update( group );
+    }
+
+
+    /**
+     * Remove the group node.
+     *
+     * @param group contains the group entity for target node.
+     * @throws SecurityException in event of validation or system error.
+     */
+    Group delete( Group group ) throws SecurityException
+    {
+        return gDao.remove( group );
+    }
+
+
+    /**
+     * Add a new property to an existing Group
+     *
+     * @param group
+     * @param key
+     * @param value
+     * @return
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *
+     */
+    Group add( Group group, String key, String value ) throws SecurityException
+    {
+        return gDao.add( group, key, value );
+    }
+
+
+    /**
+     * Remove an existing property value from an existing Group
+     *
+     * @param group
+     * @param key
+     * @param value
+     * @return
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *
+     */
+    Group delete( Group group, String key, String value ) throws SecurityException
+    {
+        return gDao.delete( group, key, value );
+    }
+
+
+    /**
+     * Method will add the "member" attribute on LDAP entry which represents a Group assignment.
+     *
+     * @param entity contains the group name targeted.
+     * @param userDn String contains the dn for the user entry that is being assigned the RBAC Role.
+     * @return Group containing copy of input data.
+     * @throws SecurityException in the event of data validation or DAO system error.
+     */
+    Group assign( Group entity, String userDn ) throws SecurityException
+    {
+        return gDao.assign( entity, userDn );
+    }
+
+
+    /**
+     * Method will remove the "member" attribute on LDAP entry which represents a Group assignment.
+     *
+     * @param entity contains the role name targeted.
+     * @param userDn String contains the dn for the user entry that is being assigned the RBAC Role.
+     * @return Role containing copy of input data.
+     * @throws SecurityException in the event of data validation or DAO system error.
+     */
+    Group deassign( Group entity, String userDn ) throws SecurityException
+    {
+        return gDao.deassign( entity, userDn );
+    }
+
+
+    /**
+     * Return a fully populated Group entity for a given name.  If matching record not found a
+     * SecurityException will be thrown.
+     *
+     * @param group contains full group name for entry in directory.
+     * @return Group entity containing all attributes associated.
+     * @throws SecurityException in the event not found or DAO search error.
+     */
+    Group read( Group group ) throws SecurityException
+    {
+        return gDao.get( group );
+    }
+
+
+    /**
+     * Takes a search string that contains full or partial Group name in directory.
+     *
+     * @param group contains full or partial name.
+     * @return List of type Group containing fully populated matching entities.  If no records found this will be empty.
+     * @throws SecurityException in the event of DAO search error.
+     */
+    List<Group> search( Group group ) throws SecurityException
+    {
+        return gDao.find( group );
+    }
+
+
+    /**
+     * Takes a search string that contains full or partial Group name in directory.
+     *
+     * @param user contains full dn for existing user.
+     * @return List of type Group containing fully populated matching entities.  If no records found this will be empty.
+     * @throws SecurityException in the event of DAO search error.
+     */
+    List<Group> search( User user ) throws SecurityException
+    {
+        return gDao.find( user );
+    }
+
+
+    /**
+     * Method will perform simple validations to ensure the integrity of the {@link Group} entity targeted for insertion
+     * or deletion in directory.
+     *
+     * @param entity contains the enum type to validate
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *          thrown in the event the attribute is null.
+     */
+    private void validate( Group entity ) throws SecurityException
+    {
+        if ( Strings.isEmpty( entity.getName() ) )
+        {
+            String error = "validate name validation failed, null or empty value";
+            LOG.warn( error );
+            throw new ValidationException( GlobalErrIds.GROUP_NAME_NULL, error );
+        }
+
+        if ( entity.getName().length() > GlobalIds.OU_LEN )
+        {
+            String name = entity.getName();
+            String error = "validate name [" + name + "] invalid length [" + entity.getName().length() + "]";
+            LOG.warn( error );
+            throw new ValidationException( GlobalErrIds.GROUP_NAME_INVLD, error );
+        }
+
+        if ( entity.getProtocol().length() > GlobalIds.OU_LEN )
+        {
+            String error = "validate protocol [" + entity.getProtocol() + "] invalid length [" + entity.getProtocol()
+                .length() + "]";
+            LOG.warn( error );
+            throw new ValidationException( GlobalErrIds.GROUP_PROTOCOL_INVLD, error );
+        }
+
+        if ( !Strings.isEmpty( entity.getDescription() ) )
+        {
+            VUtil.description( entity.getDescription() );
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/HierUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/HierUtil.java b/src/main/java/org/apache/directory/fortress/core/impl/HierUtil.java
new file mode 100755
index 0000000..516c90f
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/HierUtil.java
@@ -0,0 +1,762 @@
+/*
+ *   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.impl;
+
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.directory.fortress.core.model.Graphable;
+import org.apache.directory.fortress.core.model.Hier;
+import org.apache.directory.fortress.core.model.Relationship;
+import org.apache.directory.fortress.core.util.ObjUtil;
+import org.jgrapht.graph.SimpleDirectedGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.ValidationException;
+
+
+/**
+ * This utility performs base hierarchical processing using this software <a href="http://www.jgrapht.org/">JGraphT</a></li>.
+ * </p>
+ * It is used to provide hierarchical processing APIs for the following data sets:
+ * <ol>
+ * <li>RBAC Role relations are stored in {@code cn=Hierarchies,ou=Roles,ou=RBAC} ldap node and cached as singleton in {@link RoleUtil}</li>
+ * <li>ARBAC Admin Role relations are stored in {@code cn=Hierarchies,ou=AdminRoles,ou=ARBAC} ldap node and cached as singleton in {@link AdminRoleUtil}</li>
+ * <li>User Organizational Unit relations are stored in {@code cn=Hierarchies,ou=OS-U,ou=ARBAC} node and cached as {@link org.apache.directory.fortress.core.impl.UsoUtil}</li>
+ * <li>Permission Organizational Unit relations are stored in {@code cn=Hierarchies,ou=OS-P,ou=ARBAC} node and cached as {@link org.apache.directory.fortress.core.impl.PsoUtil}</li>
+ * </ol>
+ * This class...
+ * <ol>
+ * <li>manipulates data that is stored as singleton inside other classes with vertices of {@code String}, and edges, as {@link org.apache.directory.fortress.core.model.Relationship}s</li>
+ * <li>utilizes open source library, see <a href="http://www.jgrapht.org/">JGraphT</a>.</li>
+ * <li>processes general hierarchical data structure i.e. allows multiple inheritance with parents.</li>
+ * <li>constructs and parses simple directed graphs.</li>
+ * </ol>
+ * Static methods on this class are intended for use by other Fortress classes, and cannot be directly invoked by outside programs.
+ * <p/>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+final class HierUtil
+{
+    /**
+     * Constants used within this class:
+     */
+    private static final String CLS_NM = HierUtil.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+    private static final String VERTEX = "Vertex";
+
+    /** A lock used internally to protect the access to the locks map */
+    private static final ReadWriteLock getLockLock = new ReentrantReadWriteLock();
+
+
+    /**
+     * The 'Type' attribute corresponds to what type of hierarchy is being referred to.
+     */
+    static enum Type
+    {
+        ROLE,
+        ARLE,
+        USO,
+        PSO
+    }
+
+    private static final Map<String, ReadWriteLock> synchMap = new HashMap<String, ReadWriteLock>();
+
+
+    /**
+     * Private constructor
+     *
+     */
+    private HierUtil()
+    {
+    }
+
+    /**
+     *
+     * @param contextId
+     * @param type
+     * @return
+     */
+    static ReadWriteLock getLock( String contextId, Type type )
+    {
+        String syncKey = getSynchKey( contextId, type );
+     
+        try
+        {
+            getLockLock.readLock().lock();
+            ReadWriteLock synchObj = synchMap.get( syncKey );
+            
+            if ( synchObj == null )
+            {
+                // Not found, we will create a new one and store it into the map
+                try
+                {
+                    getLockLock.readLock().unlock();
+                    getLockLock.writeLock().lock();
+
+                    // Retry immediately to get the lock from the map, it might have been updated by
+                    // another thread while this thread was blocked on the write lock 
+                    synchObj = synchMap.get( syncKey );
+                    
+                    if ( synchObj == null )
+                    {
+                        synchObj = new ReentrantReadWriteLock();
+                        synchMap.put( syncKey, synchObj );
+                    }
+
+                    getLockLock.readLock().lock();
+                }
+                finally
+                {
+                    getLockLock.writeLock().unlock();
+                }
+            }
+            
+            return synchObj;
+        }
+        finally
+        {
+            getLockLock.readLock().unlock();
+        }
+    }
+
+
+    /**
+     *
+     * @param contextId
+     * @param type
+     * @return
+     */
+    private static String getSynchKey( String contextId, Type type )
+    {
+        return type.toString() + ":" + contextId;
+    }
+
+
+    /**
+     * This api is used to determine parentage for Hierarchical processing.
+     * It evaluates three relationship expressions:
+     * <ol>
+     * <li>If child equals parent</li>
+     * <li>If mustExist true and parent-child relationship exists</li>
+     * <li>If mustExist false and parent-child relationship does not exist</li>
+     * </ol>
+     * Method will throw {@link org.apache.directory.fortress.core.ValidationException} if rule check fails meaning caller failed validation
+     * attempt to add/remove hierarchical relationship failed.
+     *
+     * @param graph     contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param child     contains name of child.
+     * @param parent    contains name of parent.
+     * @param mustExist boolean is used to specify if relationship must be true.
+     * @throws org.apache.directory.fortress.core.ValidationException
+     *          in the event it fails one of the 3 checks.
+     */
+    static void validateRelationship( SimpleDirectedGraph<String, Relationship> graph, String child, String parent,
+        boolean mustExist )
+        throws ValidationException
+    {
+        // Ensure the two nodes aren't the same:
+        if ( child.equalsIgnoreCase( parent ) )
+        {
+            String error = "validateRelationship child [" + child + "] same as parent [" + parent + "]";
+            throw new ValidationException( GlobalErrIds.HIER_REL_INVLD, error );
+        }
+        Relationship rel = new Relationship( child.toUpperCase(), parent.toUpperCase() );
+        // Ensure there is a valid child to parent relationship.
+        if ( mustExist && !isRelationship( graph, rel ) )
+        {
+            String error = "validateRelationship child [" + child + "] does not have parent [" + parent + "]";
+            throw new ValidationException( GlobalErrIds.HIER_REL_NOT_EXIST, error );
+        }
+        // Ensure the child doesn't already have the parent as an ascendant.
+        else if ( !mustExist && isAscendant( child, parent, graph ) )
+        {
+            String error = "validateRelationship child [" + child + "] already has parent [" + parent + "]";
+            throw new ValidationException( GlobalErrIds.HIER_REL_EXIST, error );
+        }
+        // Prevent cycles by making sure the child isn't an ascendant of parent.
+        else if ( !mustExist && isDescedant( parent, child, graph ) )
+        {
+            String error = "validateRelationship child [" + child + "] is parent of [" + parent + "]";
+            throw new ValidationException( GlobalErrIds.HIER_REL_CYCLIC, error );
+        }
+    }
+
+
+    /**
+     * This method Convert from logical, {@code org.jgrapht.graph.SimpleDirectedGraph} to ldap entity, {@link org.apache.directory.fortress.core.model.Hier}.
+     * The conversion iterates over all edges in the graph and loads the corresponding {@link Relationship} data
+     * into the ldap entity.  The ldap entity stores this data physically in the {@code ftRels} attribute of {@code ftHier} object class.
+     *
+     * @param graph contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return reference to hierarchical ldap entity {@link org.apache.directory.fortress.core.model.Hier}.
+     */
+    static Hier toHier( SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Hier he = new Hier();
+        Set<Relationship> eSet = graph.edgeSet();
+        for ( Relationship edge : eSet )
+        {
+            he.setRelationship( edge );
+        }
+        return he;
+    }
+
+
+    /**
+     * This method converts from physical ldap entity format, {@link Hier} to logical {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     *
+     * @param hier contains parent-child relationship in preparation to storing in ldap {@code ftRels} attribute of {@code ftHier} object class.
+     * @return {@code org.jgrapht.graph.SimpleDirectedGraph} containing the vertices of {@code String}, and edges, as {@link Relationship}s that correspond to relational data.
+     */
+    private static SimpleDirectedGraph<String, Relationship> toGraph( Hier hier )
+    {
+        LOG.debug( "toGraph" );
+        SimpleDirectedGraph<String, Relationship> graph =
+            new SimpleDirectedGraph<>( Relationship.class );
+        List<Relationship> edges = hier.getRelationships();
+        if ( edges != null && edges.size() > 0 )
+        {
+            for ( Relationship edge : edges )
+            {
+                String child = edge.getChild();
+                String parent = edge.getParent();
+
+                try
+                {
+                    graph.addVertex( child );
+                    graph.addVertex( parent );
+                    graph.addEdge( child, parent, edge );
+                }
+                catch (java.lang.IllegalArgumentException e)
+                {
+                    String error = "toGraph child: " + child + " parent: " + parent + " caught IllegalArgumentException=" + e;
+                    LOG.error( error );
+                }
+
+                LOG.debug( "toGraph child={}, parent={}", child, parent );
+            }
+        }
+        return graph;
+    }
+
+
+    /**
+     * This method is synchronized and adds an edge and its associated vertices to simple directed graph stored in static memory of this process.
+     *
+     * @param graph synchronized parameter contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param relation contains parent-child relationship targeted for addition.
+     * @return {@code org.jgrapht.graph.SimpleDirectedGraph} containing the vertices of {@code String}, and edges, as {@link Relationship}s that correspond to relational data.
+     */
+    private static void addEdge( SimpleDirectedGraph<String, Relationship> graph, Relationship relation )
+    {
+        LOG.debug( "addEdge" );
+        synchronized ( graph )
+        {
+            graph.addVertex( relation.getChild().toUpperCase() );
+            graph.addVertex( relation.getParent().toUpperCase() );
+            graph.addEdge( relation.getChild().toUpperCase(), relation.getParent().toUpperCase(), relation );
+        }
+    }
+
+
+    /**
+     * This method is synchronized and removes an edge from a simple directed graph stored in static memory of this process.
+     *
+     * @param graph synchronized parameter contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param relation contains parent-child relationship targeted for removal.
+     * @return {@code org.jgrapht.graph.SimpleDirectedGraph} containing the vertices of {@code String}, and edges, as {@link Relationship}s that correspond to relational data.
+     */
+    private static void removeEdge( SimpleDirectedGraph<String, Relationship> graph, Relationship relation )
+    {
+        LOG.debug( "removeEdge" );
+        synchronized ( graph )
+        {
+            graph.removeEdge( relation );
+        }
+    }
+
+
+    /**
+     * Return number of children (direct descendants) a given parent node has.
+     *
+     * @param name  contains the vertex of graph to gather descendants from.
+     * @param graph contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return int value contains the number of children of a given parent vertex.
+     */
+    static int numChildren( String name, SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Map<String, String> vx = new HashMap<>();
+        vx.put( VERTEX, name.toUpperCase() );
+        return numChildren( vx, graph );
+    }
+
+
+    /**
+     * Determine if parent-child relationship exists in supplied digraph.
+     *
+     * @param graph contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param rel   contains parent and child names.
+     * @return boolean value.  true indicates parent-child relationship exists in digraph.
+     */
+    private static boolean isRelationship( SimpleDirectedGraph<String, Relationship> graph, Relationship rel )
+    {
+        return graph.containsEdge( rel );
+    }
+
+
+    /**
+     * Determine how many children a given parent node has.
+     *
+     * @param vertex of parent.
+     * @param graph  contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return
+     */
+    private static int numChildren( Map<String, String> vertex, SimpleDirectedGraph<String, Relationship> graph )
+    {
+        int numChildren = 0;
+        try
+        {
+            String v = vertex.get( VERTEX );
+            if ( v == null )
+            {
+                //log.debug("getDescendants vertex is null");
+                return 0;
+            }
+            LOG.debug( "hasChildren [{}]", v );
+            numChildren = graph.inDegreeOf( v );
+        }
+        catch ( java.lang.IllegalArgumentException e )
+        {
+            // vertex is leaf.
+        }
+        return numChildren;
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical graph and return all of the ascendants of a given node.
+     *
+     * @param childName maps to vertex to determine parentage.
+     * @param graph     contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return Set of names that are parents of given child.
+     */
+    static Set<String> getAscendants( String childName, SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Map<String, String> vx = new HashMap<>();
+        // TreeSet will return in sorted order:
+        // create Set with case insensitive comparator:
+        Set<String> parents = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+        vx.put( VERTEX, childName.toUpperCase() );
+        getAscendants( vx, graph, parents );
+        return parents;
+    }
+
+
+    /**
+     * Utility function recursively traverses a given digraph to build a set of all ascendant names.
+     *
+     * @param vertex     contains the position of the cursor for traversal of graph.
+     * @param graph      contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param ascendants contains the result set of ascendant names.
+     * @return value contains the vertex of current position.
+     */
+    private static String getAscendants( Map<String, String> vertex, SimpleDirectedGraph<String, Relationship> graph,
+        Set<String> ascendants )
+    {
+        String v = vertex.get( VERTEX );
+        if ( v == null )
+        {
+            return null;
+        }
+        else if ( graph == null )
+        {
+            return null;
+        }
+        LOG.debug( "getAscendants [{}]", v);
+        Set<Relationship> edges;
+        try
+        {
+            edges = graph.outgoingEdgesOf( v );
+
+        }
+        catch ( java.lang.IllegalArgumentException iae )
+        {
+            // vertex is leaf.
+            return null;
+        }
+        for ( Relationship edge : edges )
+        {
+            vertex.put( VERTEX, edge.getParent() );
+            ascendants.add( edge.getParent() );
+            v = getAscendants( vertex, graph, ascendants );
+        }
+        return v;
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical graph and return all of the descendants for a given node.
+     *
+     * @param parentName maps to vertex to determine parentage.
+     * @param graph      contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return Set of names that are children of given parent.
+     */
+    static Set<String> getDescendants( String parentName, SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Map<String, String> vx = new HashMap<>();
+        // TreeSet will return in sorted order:
+        // create Set with case insensitive comparator:
+        Set<String> children = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+        vx.put( VERTEX, parentName.toUpperCase() );
+        getDescendants( vx, graph, children );
+        return children;
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical graph and determine child node contains a given parent as one of its ascendants.
+     *
+     * @param childName maps to vertex to determine parentage.
+     * @param parentName maps to vertex to determine parentage.
+     * @param graph      contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return Set of names that are children of given parent.
+     */
+    private static boolean isAscendant( String childName, String parentName,
+        SimpleDirectedGraph<String, Relationship> graph )
+    {
+        boolean isAscendant = false;
+        Set<String> ascendants = getAscendants( childName, graph );
+        if ( ascendants.contains( parentName ) )
+        {
+            isAscendant = true;
+        }
+        return isAscendant;
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical graph and determine if parent node contains a given child as one of its descendants.
+     *
+     * @param childName maps to vertex to determine parentage.
+     * @param parentName maps to vertex to determine parentage.
+     * @param graph      contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return Set of names that are children of given parent.
+     */
+    private static boolean isDescedant( String childName, String parentName,
+        SimpleDirectedGraph<String, Relationship> graph )
+    {
+        boolean isDescendant = false;
+        Set<String> descendants = getDescendants( parentName, graph );
+        if ( descendants.contains( childName ) )
+        {
+            isDescendant = true;
+        }
+        return isDescendant;
+    }
+
+
+    /**
+     * Utility function recursively traverses a given digraph to build a set of all descendants names.
+     *
+     * @param vertex      contains the position of the cursor for traversal of graph.
+     * @param graph       contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param descendants contains the result set of names of all descendants of node.
+     * @return value contains the vertex of current position.
+     */
+    private static String getDescendants( Map<String, String> vertex, SimpleDirectedGraph<String, Relationship> graph,
+        Set<String> descendants )
+    {
+        String v = vertex.get( VERTEX );
+        if ( v == null )
+        {
+            // vertex is null
+            return null;
+        }
+        else if ( graph == null )
+        {
+            // graph is null
+            return null;
+        }
+        LOG.debug( "getDescendants [{}]", v);
+        Set<Relationship> edges;
+        try
+        {
+            edges = graph.incomingEdgesOf( v );
+        }
+        catch ( java.lang.IllegalArgumentException iae )
+        {
+            // vertex is leaf.
+            return null;
+        }
+        for ( Relationship edge : edges )
+        {
+            vertex.put( VERTEX, edge.getChild() );
+            descendants.add( edge.getChild() );
+            v = getDescendants( vertex, graph, descendants );
+        }
+        return v;
+    }
+
+
+    /**
+     * Utility function returns a set of all children (direct descendant) names.
+     *
+     * @param vertex contains the position of the cursor for traversal of graph.
+     * @param graph  contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return value contains the vertex of current position.
+     */
+    static Set<String> getChildren( String vertex, SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Set<String> descendants = new HashSet<>();
+        if ( graph == null )
+        {
+            // graph is null
+            return null;
+        }
+
+        LOG.debug( "getChildren [{}]", vertex );
+        Set<Relationship> edges;
+        try
+        {
+            edges = graph.incomingEdgesOf( vertex );
+        }
+        catch ( java.lang.IllegalArgumentException iae )
+        {
+            // vertex is leaf.
+            return null;
+        }
+        for ( Relationship edge : edges )
+        {
+            descendants.add( edge.getChild() );
+        }
+        return descendants;
+    }
+
+
+    /**
+     * Recursively traverse the hierarchical graph and return all of the ascendants of a given node.
+     *
+     * @param childName   maps to vertex to determine parentage.
+     * @param parentName  points to top most ascendant where traversal must stop.
+     * @param isInclusive if set to true will include the parentName in the result set.  False will not return specified parentName.
+     * @param graph       contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return Set of names that are parents of given child.
+     */
+    static Set<String> getAscendants( String childName, String parentName, boolean isInclusive,
+        SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Map<String, String> vx = new HashMap<>();
+        // TreeSet will return in sorted order:
+        // create Set with case insensitive comparator:
+        Set<String> parents = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+
+        vx.put( VERTEX, childName.toUpperCase() );
+        getAscendants( vx, graph, parents, parentName, isInclusive );
+        return parents;
+    }
+
+
+    /**
+     * Private utility to recursively traverse the hierarchical graph and return all of the ascendants of a given child node.
+     *
+     * @param vertex      contains node name and acts as cursor for current location.
+     * @param graph       contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param parents     contains the result set of parent nodes.
+     * @param stopName    contains the name of node where traversal ends.
+     * @param isInclusive if set to true will include the parentName in the result set. False will not return specified parentName.
+     * @return Set of names that are parents of given child.
+     */
+    private static String getAscendants( Map<String, String> vertex, SimpleDirectedGraph<String, Relationship> graph,
+        Set<String> parents, String stopName, boolean isInclusive )
+    {
+        String v = vertex.get( VERTEX );
+        if ( v == null )
+        {
+            // vertex is null
+            return null;
+        }
+        else if ( graph == null )
+        {
+            // graph is null
+            return null;
+        }
+        LOG.debug( "getAscendants [{}]", v);
+        Set<Relationship> edges;
+        try
+        {
+            edges = graph.outgoingEdgesOf( v );
+        }
+        catch ( java.lang.IllegalArgumentException iae )
+        {
+            // vertex is leaf.
+            return null;
+        }
+        for ( Relationship edge : edges )
+        {
+            if ( edge.getParent().equalsIgnoreCase( stopName ) )
+            {
+                if ( isInclusive )
+                {
+                    parents.add( edge.getParent() );
+                }
+                break;
+            }
+            else
+            {
+                vertex.put( VERTEX, edge.getParent() );
+                parents.add( edge.getParent() );
+                v = getAscendants( vertex, graph, parents, stopName, isInclusive );
+            }
+        }
+        return v;
+    }
+
+
+    /**
+     * Private utility to return the parents (direct ascendants) of a given child node.
+     *
+     * @param vertex contains node name and acts as cursor for current location.
+     * @param graph  contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @return Set of names that are parents of given child.
+     */
+    static Set<String> getParents( String vertex, SimpleDirectedGraph<String, Relationship> graph )
+    {
+        Set<String> parents = new HashSet<>();
+        if ( graph == null )
+        {
+            // graph is null
+            return null;
+        }
+        LOG.debug( "getParents [{}]", vertex);
+        Set<Relationship> edges;
+        try
+        {
+            edges = graph.outgoingEdgesOf( vertex );
+        }
+        catch ( java.lang.IllegalArgumentException iae )
+        {
+            // vertex is leaf.
+            return null;
+        }
+        for ( Relationship edge : edges )
+        {
+            parents.add( edge.getParent() );
+        }
+        return parents;
+    }
+
+
+    /**
+     * This method will retrieve the list of all parent-child relationships for a given node.  If the node was not found in
+     * ldap this method will create a new node and store default data.
+     * The following ldap nodes are currently storing hierarchical data:
+     * <ol>
+     * <li>RBAC Role relations are stored in {@code cn=Hierarchies,ou=Roles,ou=RBAC} ldap node and cached as singleton in {@link RoleUtil}</li>
+     * <li>ARBAC Admin Role relations are stored in {@code cn=Hierarchies,ou=AdminRoles,ou=ARBAC} ldap node and cached as singleton in {@link AdminRoleUtil}</li>
+     * <li>User Organizational Unit relations are stored in {@code cn=Hierarchies,ou=OS-U,ou=ARBAC} node and cached as {@link org.apache.directory.fortress.core.impl.UsoUtil}</li>
+     * <li>Permission Organizational Unit relations are stored in {@code cn=Hierarchies,ou=OS-P,ou=ARBAC} node and cached as {@link org.apache.directory.fortress.core.impl.PsoUtil}</li>
+     * </ol>
+     *
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     * @return reference the the Hier result set retrieved from ldap.
+     */
+    static Hier loadHier( String contextId, List<Graphable> descendants )
+    {
+        Hier hier = new Hier();
+        if ( ObjUtil.isNotNullOrEmpty( descendants ) )
+        {
+            hier.setContextId( contextId );
+            for ( Graphable descendant : descendants )
+            {
+                Set<String> parents = descendant.getParents();
+                if ( ObjUtil.isNotNullOrEmpty( parents ) )
+                {
+                    for ( String parent : parents )
+                    {
+                        Relationship relationship = new Relationship();
+                        relationship.setChild( descendant.getName().toUpperCase() );
+                        relationship.setParent( parent.toUpperCase() );
+                        hier.setRelationship( relationship );
+                    }
+                }
+            }
+        }
+        return hier;
+    }
+
+
+    /**
+     * This api allows synchronized access to allow updates to hierarchical relationships.
+     * Method will update the hierarchical data set and reload the JGraphT simple digraph with latest.
+     *
+     * @param graph contains a reference to simple digraph {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     * @param relationship contains parent-child relationship targeted for addition.
+     * @param op   used to pass the ldap op {@link Hier.Op#ADD}, {@link Hier.Op#MOD}, {@link org.apache.directory.fortress.core.model.Hier.Op#REM}
+     * @throws org.apache.directory.fortress.core.SecurityException in the event of a system error.
+     */
+    static void updateHier( SimpleDirectedGraph<String, Relationship> graph, Relationship relationship, Hier.Op op )
+        throws SecurityException
+    {
+        if ( op == Hier.Op.ADD )
+            HierUtil.addEdge( graph, relationship );
+        else if ( op == Hier.Op.REM )
+            HierUtil.removeEdge( graph, relationship );
+        else
+            throw new SecurityException( GlobalErrIds.HIER_CANNOT_PERFORM, CLS_NM
+                + "updateHier Cannot perform hierarchical operation" );
+    }
+
+
+    /**
+     * Method instantiates a new digraph, {@code org.jgrapht.graph.SimpleDirectedGraph}, using data passed in via
+     * {@link Hier} entity.
+     *
+     * @param hier contains the source data for digraph.
+     * @return reference to {@code org.jgrapht.graph.SimpleDirectedGraph}.
+     */
+    static SimpleDirectedGraph<String, Relationship> buildGraph( Hier hier )
+    {
+        SimpleDirectedGraph<String, Relationship> graph;
+        LOG.debug( "buildGraph is initializing" );
+        if ( hier == null )
+        {
+            String error = "buildGraph detected null hier=";
+            LOG.error( error );
+            return null;
+        }
+        graph = toGraph( hier );
+        LOG.debug( "buildGraph success to toGraph" );
+        LOG.debug( "buildGraph is success" );
+        return graph;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/Manageable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/Manageable.java b/src/main/java/org/apache/directory/fortress/core/impl/Manageable.java
new file mode 100755
index 0000000..882dc89
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/Manageable.java
@@ -0,0 +1,176 @@
+/*
+ *   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.impl;
+
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.ValidationException;
+import org.apache.directory.fortress.core.model.FortEntity;
+import org.apache.directory.fortress.core.model.Permission;
+import org.apache.directory.fortress.core.model.Session;
+import org.apache.directory.fortress.core.model.VUtil;
+
+/**
+ * Abstract class allows outside clients to manage security and multi-tenant concerns within the Fortress runtime.
+ * The {@link #setAdmin(org.apache.directory.fortress.core.model.Session)} method allows A/RBAC sessions to be loaded and allows authorization
+ * to be performed on behalf of the user who is contained within the Session object itself.
+ * The ARBAC permissions will be checked each time outside client makes calls into Fortress API.
+ * This interface also allows Fortress clients to operate in a multi-tenant fashion using {@link #setContextId(String)}.
+ * <p/>
+ * Implementers of this abstract class will NOT be thread safe because of instance variables that may be set.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public abstract class Manageable implements org.apache.directory.fortress.core.Manageable
+{
+    // These instance variables are the reason why children of this abstract class will not be thread safe:
+    protected Session adminSess;
+    protected String contextId;
+
+    /**
+     * Use this method to load an administrative user's ARBAC Session object into Manager object will enable authorization to
+     * be performed on behalf of admin user.  Setting Session into this object will enforce ARBAC controls and render this class'
+     * implementer thread unsafe.
+     *
+     * @param session contains a valid Fortress A/RBAC Session object.
+     */
+    public final void setAdmin(Session session)
+    {
+        this.adminSess = session;
+    }
+
+    /**
+     * Use this method to set the tenant id onto function call into Fortress which allows segregation of data by customer.
+     * The contextId is used for multi-tenancy to isolate data sets within a particular sub-tree within DIT.
+     * Setting contextId into this object will render this class' implementer thread unsafe.
+     *
+     * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+     */
+    public final void setContextId(String contextId)
+    {
+        this.contextId = contextId;
+    }
+
+    
+    /**
+     * Set A/RBAC session on entity and perform authorization on behalf of the caller if the {@link #adminSess} is set.
+     *
+     * @param className contains the class name.
+     * @param opName contains operation name.
+     * @param entity contains {@link org.apache.directory.fortress.core.model.FortEntity} instance.
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *          in the event of data validation or system error.
+     */
+    protected final void setEntitySession(String className, String opName, FortEntity entity) throws SecurityException
+    {
+        entity.setContextId(this.contextId);
+        if (this.adminSess != null)
+        {
+            Permission perm = new Permission(className, opName);
+            perm.setContextId(this.contextId);
+            AdminUtil.setEntitySession( this.adminSess, perm, entity, this.contextId );
+        }
+    }
+    
+    
+    /**
+     * Every Fortress Manager API (e.g. addUser, updateUser, addRole, ...) will perform authorization on behalf of the caller IFF the {@link AuditMgrImpl#adminSess} has been set before invocation.
+     *
+     * @param className contains the class name.
+     * @param opName contains operation name.
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *          in the event of data validation or system error.
+     */
+    protected final void checkAccess(String className, String opName) throws SecurityException
+    {
+        if (this.adminSess != null)
+        {
+            Permission perm = new Permission(className, opName);
+            perm.setContextId(this.contextId);
+            AdminUtil.checkAccess(this.adminSess, perm, this.contextId);
+        }
+    }
+
+    /**
+     * Method is called by Manager APIs to load contextual information on {@link FortEntity}.
+     * </p>
+     * The information is used to
+     * <ol>
+     * <li>Load the administrative User's {@link Session} object into entity.  This is used for checking to ensure administrator has privilege to perform administrative operation.</li>
+     * <li>Load the target operation's permission into the audit context.  This is used for Fortress audit log stored in OpenLDAP</li>
+     * </ol>
+     *
+     * @param className contains the class name.
+     * @param opName contains operation name.
+     * @param entity  used to pass contextual information through Fortress layers for administrative security checks and audit.
+     * @throws org.apache.directory.fortress.core.SecurityException
+     *          in the event of data validation or system error.
+     */
+    protected final void setAdminData(String className, String opName, FortEntity entity)
+    {
+        if (this.adminSess != null)
+        {
+            Permission perm = new Permission(className, opName);
+            entity.setAdminSession(this.adminSess);
+            entity.setModCode(AdminUtil.getObjName(perm.getObjName()) + "." + perm.getOpName());
+        }
+        entity.setContextId(this.contextId);
+    }
+
+    
+    /**
+     * Method will throw exception if entity reference is null, otherwise will set the contextId of the tenant onto the supplied entity reference.
+     * @param className contains the class name of caller.
+     * @param opName contains operation name of caller.
+     * @param entity  used here to pass the tenant id into the Fortress DAO layer..
+     * @param errorCode contains the error id to use if null.
+     * @throws ValidationException in the event object is null.
+     */
+    protected final void assertContext( String className, String opName, FortEntity entity, int errorCode ) throws ValidationException
+    {
+        VUtil.assertNotNull( entity, errorCode, getFullMethodName( className, opName ) );
+        entity.setContextId( contextId );
+    }
+
+    
+    /**
+     * Method will throw exception if entity reference is null, otherwise will set the contextId of the tenant onto the supplied entity reference.
+     * 
+     * @param methodName contains the full method name of caller.
+     * @param entity  used here to pass the tenant id into the Fortress DAO layer..
+     * @param errorCode contains the error id to use if null.
+     * @throws ValidationException in the event object is null.
+     */
+    protected final void assertContext( String methodName, FortEntity entity, int errorCode ) throws ValidationException
+    {
+        VUtil.assertNotNull( entity, errorCode, methodName );
+        entity.setContextId( contextId );
+    }
+
+    /**
+     * This method is used to generate log statements and returns the concatenation of class name to the operation name.
+     * @param className of the caller
+     * @param opName of the caller
+     * @return className + '.' + opName
+     */
+    protected final String getFullMethodName(String className, String opName)
+    {
+        return className + "." + opName;
+    }
+}
\ No newline at end of file


Mime
View raw message