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 2015/06/09 05:15:43 UTC
[37/75] [abbrv] [partial] directory-fortress-core git commit: FC-109
- rename rbac package to impl
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java b/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java
new file mode 100755
index 0000000..288d499
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/RoleUtil.java
@@ -0,0 +1,425 @@
+/*
+ * 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.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReadWriteLock;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.fortress.core.model.Graphable;
+import org.apache.directory.fortress.core.model.Hier;
+import org.apache.directory.fortress.core.model.ParentUtil;
+import org.apache.directory.fortress.core.model.Relationship;
+import org.apache.directory.fortress.core.model.Role;
+import org.apache.directory.fortress.core.model.UserRole;
+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.GlobalIds;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.ValidationException;
+import org.apache.directory.fortress.core.util.cache.Cache;
+import org.apache.directory.fortress.core.util.cache.CacheMgr;
+
+
+/**
+ * This utility wraps {@link org.apache.directory.fortress.core.impl.HierUtil} methods to provide hierarchical functionality for the {@link org.apache.directory.fortress.core.model.Role} data set.
+ * The {@code cn=Hierarchies, ou=Roles} data is stored within a cache, {@link #roleCache}, contained within this class. The parent-child edges are contained in LDAP,
+ * in {@code ftParents} attribute. The ldap data is retrieved {@link org.apache.directory.fortress.core.impl.RoleP#getAllDescendants(String)} and loaded into {@code org.jgrapht.graph.SimpleDirectedGraph}.
+ * The graph...
+ * <ol>
+ * <li>is stored as singleton in this class 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>contains a general hierarchical data structure i.e. allows multiple inheritance with parents.</li>
+ * <li>is a simple directed graph thus does not allow cycles.</li>
+ * </ol>
+ * After update is performed to ldap, the singleton is refreshed with latest info.
+ * <p/>
+ * Static methods on this class are intended for use by other Fortress classes, i.e. {@link org.apache.directory.fortress.core.impl.UserDAO} and {@link org.apache.directory.fortress.core.impl.PermDAO}
+ * and cannot be directly invoked by outside programs.
+ * <p/>
+ * This class contains singleton that can be updated but is thread safe.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public final class RoleUtil implements ParentUtil
+{
+ private static final Cache roleCache;
+ private static final RoleP roleP = new RoleP();
+ private static final String CLS_NM = RoleUtil.class.getName();
+ private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+ /**
+ * Initialize the Role hierarchies. This will read the {@link org.apache.directory.fortress.core.model.Hier} data set from ldap and load into
+ * the JGraphT simple digraph that referenced statically within this class.
+ */
+ static
+ {
+ CacheMgr cacheMgr = CacheMgr.getInstance();
+ roleCache = cacheMgr.getCache( "fortress.roles" );
+ }
+
+ /**
+ * Private constructor
+ *
+ */
+ private void RoleUtil()
+ {
+ }
+
+ /**
+ * Used to determine if one {@link org.apache.directory.fortress.core.model.Role} is the parent of another. This method
+ * will call recursive routine {@link #getAscendants(String, String)} to walk the {@code org.jgrapht.graph.SimpleDirectedGraph} data structure
+ * returning flag indicating if parent-child relationship is valid.
+ *
+ * @param child maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param parent maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRels' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return boolean result, 'true' indicates parent/child relationship exists.
+ */
+ public static boolean isParent( String child, String parent, String contextId )
+ {
+ boolean result = false;
+ Set<String> parents = getAscendants( child, contextId );
+ if ( parents != null && parents.size() > 0 )
+ {
+ result = parents.contains( parent.toUpperCase() );
+ }
+ return result;
+ }
+
+
+ /**
+ * Recursively traverse the {@link org.apache.directory.fortress.core.model.Role} graph and return all of the descendants of a given node {@link org.apache.directory.fortress.core.model.Role#name}.
+ *
+ * @param roleName {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of Role names are descendants {@link org.apache.directory.fortress.core.model.Role}s of given parent.
+ */
+ public static Set<String> getDescendants( String roleName, String contextId )
+ {
+ return HierUtil.getDescendants( roleName.toUpperCase(), getGraph( contextId ) );
+ }
+
+
+ /**
+ * Traverse the {@link org.apache.directory.fortress.core.model.Role} graph and return all children (direct descendants) of a given parent node {@link org.apache.directory.fortress.core.model.Role#name}.
+ *
+ * @param roleName {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of Role names are children {@link org.apache.directory.fortress.core.model.Role}s of given parent.
+ */
+ public static Set<String> getChildren( String roleName, String contextId )
+ {
+ return HierUtil.getChildren( roleName.toUpperCase(), getGraph( contextId ) );
+ }
+
+
+ /**
+ * Recursively traverse the hierarchical role graph and return all of the ascendants of a given role.
+ *
+ * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of Role names that are ascendants of given child.
+ */
+ public static Set<String> getAscendants( String roleName, String contextId )
+ {
+ return HierUtil.getAscendants( roleName.toUpperCase(), getGraph( contextId ) );
+ }
+
+
+ /**
+ * Traverse the hierarchical role graph and return all of the parents (direct ascendants) of a given role.
+ *
+ * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of Role names that are parents of given child.
+ */
+ static Set<String> getParents( String roleName, String contextId )
+ {
+ return HierUtil.getParents( roleName.toUpperCase(), getGraph( contextId ) );
+ }
+
+
+ /**
+ * Traverse the hierarchical role graph and return all of the parents (direct ascendants) of a given role.
+ *
+ * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of Role names that are parents of given child.
+ */
+ public Set<String> getParentsCB( String roleName, String contextId )
+ {
+ return HierUtil.getParents( roleName.toUpperCase(), getGraph( contextId ) );
+ }
+
+
+ /**
+ * Determine the number of children (direct descendants) a given parent role has.
+ *
+ * @param roleName maps to logical {@link org.apache.directory.fortress.core.model.Role#name} on 'ftRls' object class.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return int value contains the number of children of a given parent nRole.
+ */
+ static int numChildren( String roleName, String contextId )
+ {
+ return HierUtil.numChildren( roleName.toUpperCase(), getGraph( contextId ) );
+ }
+
+
+ /**
+ * Return Set of RBAC {@link org.apache.directory.fortress.core.model.Role#name}s ascendants. Used by {@link org.apache.directory.fortress.core.impl.PermDAO#checkPermission}
+ * for computing authorized {@link org.apache.directory.fortress.core.model.UserRole#name}s.
+ *
+ * @param uRoles contains list of Roles activated within a {@link org.apache.directory.fortress.core.model.User}'s {@link org.apache.directory.fortress.core.model.Session}.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return contains Set of all authorized RBAC Roles for a given User.
+ */
+ public static Set<String> getInheritedRoles( List<UserRole> uRoles, String contextId )
+ {
+ // create Set with case insensitive comparator:
+ Set<String> iRoles = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+ if ( ObjUtil.isNotNullOrEmpty( uRoles ) )
+ {
+ for ( UserRole uRole : uRoles )
+ {
+ String rleName = uRole.getName();
+ iRoles.add( rleName );
+ Set<String> parents = HierUtil.getAscendants( rleName, getGraph( contextId ) );
+ if ( ObjUtil.isNotNullOrEmpty( parents ) )
+ {
+ iRoles.addAll( parents );
+ }
+ }
+ }
+ return iRoles;
+ }
+
+
+ /**
+ *
+ * @param roles
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return set of ascendant roles associated with this entry.
+ */
+ static Set<String> getAscendantRoles( List<String> roles, String contextId )
+ {
+ // create Set with case insensitive comparator:
+ Set<String> iRoles = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+ if ( ObjUtil.isNotNullOrEmpty( roles ) )
+ {
+ for ( String role : roles )
+ {
+ iRoles.add( role );
+ Set<String> parents = HierUtil.getAscendants( role, getGraph( contextId ) );
+ if ( ObjUtil.isNotNullOrEmpty( parents ) )
+ {
+ iRoles.addAll( parents );
+ }
+ }
+ }
+ return iRoles;
+ }
+
+
+ /**
+ *
+ * @param roles
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return set of descendant roles associated with this entry.
+ */
+ static Set<String> getDescendantRoles( Set<String> roles, String contextId )
+ {
+ // create Set with case insensitive comparator:
+ Set<String> iRoles = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+ if ( ObjUtil.isNotNullOrEmpty( roles ) )
+ {
+ for ( String role : roles )
+ {
+ iRoles.add( role );
+ Set<String> children = HierUtil.getDescendants( role, getGraph( contextId ) );
+ if ( ObjUtil.isNotNullOrEmpty( children ) )
+ {
+ iRoles.addAll( children );
+ }
+ }
+ }
+ return iRoles;
+ }
+
+
+ /**
+ * Recursively traverse the hierarchical graph and return all of the ascendants of a given child 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 contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of names that are parents of given child.
+ */
+ static Set<String> getAscendants( String childName, String parentName, boolean isInclusive, String contextId )
+ {
+ return HierUtil.getAscendants( childName, parentName, isInclusive, getGraph( contextId ) );
+ }
+
+
+ /**
+ * This api is used by {@link AdminMgrImpl} to determine parentage for Hierarchical RBAC processing.
+ * It calls {@link HierUtil#validateRelationship(org.jgrapht.graph.SimpleDirectedGraph, String, String, boolean)} to evaluate three adminRole 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 childRole contains {@link org.apache.directory.fortress.core.model.Role#name} of child.
+ * @param parentRole contains {@link org.apache.directory.fortress.core.model.Role#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( Role childRole, Role parentRole, boolean mustExist )
+ throws ValidationException
+ {
+ HierUtil.validateRelationship( getGraph( childRole.getContextId() ), childRole.getName(), parentRole.getName(),
+ mustExist );
+ }
+
+
+ /**
+ * 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 contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @param relationship contains parent-child relationship targeted for addition.
+ * @param op used to pass the ldap op {@link org.apache.directory.fortress.core.model.Hier.Op#ADD}, {@link org.apache.directory.fortress.core.model.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( String contextId, Relationship relationship, Hier.Op op ) throws SecurityException
+ {
+ HierUtil.updateHier( getGraph( contextId ), relationship, op );
+ }
+
+
+ /**
+ * Read this ldap record,{@code cn=Hierarchies, ou=OS-P} into this entity, {@link Hier}, before loading into this collection class,{@code org.jgrapht.graph.SimpleDirectedGraph}
+ * using 3rd party lib, <a href="http://www.jgrapht.org/">JGraphT</a>.
+ *
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return handle to simple digraph containing role hierarchies.
+ */
+ private static SimpleDirectedGraph<String, Relationship> loadGraph( String contextId )
+ {
+ Hier inHier = new Hier( Hier.Type.ROLE );
+ inHier.setContextId( contextId );
+ LOG.info( "loadGraph initializing ROLE context [{}]", inHier.getContextId() );
+ List<Graphable> descendants = null;
+
+ try
+ {
+ descendants = roleP.getAllDescendants( inHier.getContextId() );
+ }
+ catch ( SecurityException se )
+ {
+ LOG.info( "loadGraph caught SecurityException={}", se );
+ }
+
+ Hier hier = HierUtil.loadHier( contextId, descendants );
+ SimpleDirectedGraph<String, Relationship> graph;
+
+ graph = HierUtil.buildGraph( hier );
+ roleCache.put( getKey( contextId ), graph );
+
+ return graph;
+ }
+
+
+ /**
+ *
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return key to this tenant's cache entry.
+ */
+ private static String getKey( String contextId )
+ {
+ String key = HierUtil.Type.ROLE.toString();
+
+ if ( StringUtils.isNotEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) )
+ {
+ key += ":" + contextId;
+ }
+
+ return key;
+ }
+
+
+ /**
+ *
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return handle to simple digraph containing role hierarchies.
+ */
+ private static SimpleDirectedGraph<String, Relationship> getGraph( String contextId )
+ {
+ ReadWriteLock hierLock = HierUtil.getLock( contextId, HierUtil.Type.ROLE );
+ String key = getKey( contextId );
+
+ try
+ {
+ hierLock.readLock().lock();
+ SimpleDirectedGraph<String, Relationship> graph = ( SimpleDirectedGraph<String, Relationship> ) roleCache
+ .get( key );
+
+ if ( graph == null )
+ {
+ try
+ {
+ hierLock.readLock().unlock();
+ hierLock.writeLock().lock();
+
+ // TODO: determine why this (code that was commented out) creates a deadlock:
+ //graph = ( SimpleDirectedGraph<String, Relationship> ) roleCache.get( key );
+
+ //if ( graph == null )
+ //{
+ graph = loadGraph( contextId );
+ //}
+
+ hierLock.readLock().lock();
+ }
+ finally
+ {
+ hierLock.writeLock().unlock();
+ }
+ }
+
+ return graph;
+ }
+ finally
+ {
+ hierLock.readLock().unlock();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java b/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java
new file mode 100755
index 0000000..da7be48
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SDUtil.java
@@ -0,0 +1,548 @@
+/*
+ * 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.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.ReviewMgrFactory;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.ReviewMgr;
+import org.apache.directory.fortress.core.util.Config;
+import org.apache.directory.fortress.core.model.Role;
+import org.apache.directory.fortress.core.model.SDSet;
+import org.apache.directory.fortress.core.model.Session;
+import org.apache.directory.fortress.core.model.User;
+import org.apache.directory.fortress.core.model.UserRole;
+import org.apache.directory.fortress.core.util.ObjUtil;
+import org.apache.directory.fortress.core.util.cache.Cache;
+import org.apache.directory.fortress.core.util.cache.CacheMgr;
+import org.apache.directory.fortress.core.util.cache.DsdCacheEntry;
+import org.apache.directory.fortress.core.util.time.Constraint;
+
+import net.sf.ehcache.search.Attribute;
+import net.sf.ehcache.search.Query;
+import net.sf.ehcache.search.Result;
+import net.sf.ehcache.search.Results;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This utilty provides functionality necessary for SSD and DSD processing and cannot be called by components outside fortress.
+ * This class also contains utility functions for maintaining the SSD and DSD cache.
+ * <p/>
+ * This class is thread safe.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @created September 3, 2010
+ */
+final class SDUtil
+{
+ private static final Cache m_dsdCache;
+ private static final String FORTRESS_DSDS = "fortress.dsd";
+ private static final Cache m_ssdCache;
+ private static final String FORTRESS_SSDS = "fortress.ssd";
+ private static final SdP sp = new SdP();
+ private static final String IS_DSD_CACHE_DISABLED_PARM = "enable.dsd.cache";
+ private static final String DSD_NAME = "name";
+ private static final String EMPTY_ELEMENT = "empty";
+ private static final String CONTEXT_ID = "contextId";
+
+ static
+ {
+ // Get a reference to the CacheManager Singleton object:
+ CacheMgr cacheMgr = CacheMgr.getInstance();
+ // This cache contains a wrapper entry for DSD and is searchable by both DSD and Role name:
+ m_dsdCache = cacheMgr.getCache(FORTRESS_DSDS);
+ // This cache is not searchable and contains Lists of SSD objects by Role:
+ m_ssdCache = cacheMgr.getCache(FORTRESS_SSDS);
+ }
+
+ /**
+ * Private constructor
+ *
+ */
+ private SDUtil()
+ {
+ }
+
+ /**
+ * This method is called by AdminMgr.assignUser and is used to validate Static Separation of Duty
+ * constraints when assigning a role to user.
+ *
+ * @param uRole
+ * @throws org.apache.directory.fortress.core.SecurityException
+ *
+ */
+ static void validateSSD(UserRole uRole)
+ throws SecurityException
+ {
+ validateSSD(new User(uRole.getUserId()), new Role(uRole.getName()));
+ }
+
+ /**
+ * This method is called by AdminMgr.assignUser and is used to validate Static Separation of Duty
+ * constraints when assigning a role to user.
+ *
+ * @param user
+ * @param role
+ * @throws org.apache.directory.fortress.core.SecurityException
+ *
+ */
+ static void validateSSD(User user, Role role)
+ throws SecurityException
+ {
+ int matchCount;
+ // get all authorized roles for user
+ ReviewMgr rMgr = ReviewMgrFactory.createInstance(user.getContextId());
+ Set<String> rls = rMgr.authorizedRoles(user);
+ // Need to proceed?
+ if (!ObjUtil.isNotNullOrEmpty(rls))
+ {
+ return;
+ }
+
+ // get all SSD sets that contain the new role
+ List<SDSet> ssdSets = getSsdCache(role.getName(), user.getContextId());
+ for (SDSet ssd : ssdSets)
+ {
+ matchCount = 0;
+ Set<String> map = ssd.getMembers();
+ // iterate over every authorized role for user:
+ for (String authRole : rls)
+ {
+ // is there a match found between authorized role and SSD set's members?
+ if (map.contains(authRole))
+ {
+ matchCount++;
+ // does the match count exceed the cardinality allowed for this particular SSD set?
+ if (matchCount >= ssd.getCardinality() - 1)
+ {
+ String error = "validateSSD new role [" + role.getName() + "] validates SSD Set Name:" + ssd.getName() + " Cardinality:" + ssd.getCardinality();
+ throw new SecurityException(GlobalErrIds.SSD_VALIDATION_FAILED, error);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is called by AccessMgr.addActiveRole and is used to validate Dynamic Separation of Duty
+ * constraints when activating a role one at a time. For activation of multiple roles simultaneously use
+ * the DSD.validate API which is used during createSession sequence.
+ *
+ * @param session
+ * @param role
+ * @throws org.apache.directory.fortress.core.SecurityException
+ *
+ */
+ static void validateDSD(Session session, Constraint role)
+ throws SecurityException
+ {
+ // get all activated roles from user's session:
+ List<UserRole> rls = session.getRoles();
+ if (!ObjUtil.isNotNullOrEmpty(rls))
+ {
+ // An empty list of roles was passed in the session variable.
+ // No need to continue.
+ return;
+ }
+
+ // get all DSD sets that contain the target role
+ Set<SDSet> dsdSets = getDsdCache(role.getName(), session.getContextId());
+ for (SDSet dsd : dsdSets)
+ {
+ // Keeps the number of matched roles to a particular DSD set.
+ int matchCount = 0;
+
+ // Contains the list of roles assigned to a particular DSD set.
+ Set<String> map = dsd.getMembers();
+
+ // iterate over every role active in session for match wth DSD members:
+ for (UserRole actRole : rls)
+ {
+ // is there a match found between active role in session and DSD set members?
+ if (map.contains(actRole.getName()))
+ {
+ // Yes, we found a match, increment the count.
+ matchCount++;
+
+ // Does the match count exceed the cardinality allowed for this particular DSD set?
+ if (matchCount >= dsd.getCardinality() - 1)
+ {
+ // Yes, the target role violates DSD cardinality rule.
+ String error = "validateDSD failed for role [" + role.getName() + "] DSD Set Name:" + dsd.getName() + " Cardinality:" + dsd.getCardinality();
+ throw new SecurityException(GlobalErrIds.DSD_VALIDATION_FAILED, error);
+ }
+ }
+ else // Check the parents of activated role for DSD match:
+ {
+ // Now pull the activated role's list of parents.
+ Set<String> parentSet = RoleUtil.getAscendants(actRole.getName(), session.getContextId());
+
+ // Iterate over the list of parent roles:
+ for (String parentRole : parentSet)
+ {
+ if (map.contains(parentRole)) // is there match between parent and DSD member?
+ {
+ matchCount++;
+ if (matchCount >= dsd.getCardinality() - 1) // Does the counter exceed max per cardinality on this DSD set?
+ {
+ String error = "validateDSD failed for role [" + role.getName() + "] parent role [" + parentRole + "] DSD Set Name:" + dsd.getName() + " Cardinality:" + dsd.getCardinality();
+ throw new SecurityException(GlobalErrIds.DSD_VALIDATION_FAILED, error);
+ }
+ // Breaking out of the loop here means the DSD algorithm will only match one
+ // role per parent of active role candidate.
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Given DSD entry name, clear its corresponding object values from the cache.
+ *
+ * @param name contains the name of object to be cleared.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com. *
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ static void clearDsdCacheEntry(String name, String contextId)
+ {
+ Attribute<String> context = m_dsdCache.getSearchAttribute(CONTEXT_ID);
+ Attribute<String> dsdName = m_dsdCache.getSearchAttribute(DSD_NAME);
+ Query query = m_dsdCache.createQuery();
+ query.includeKeys();
+ query.includeValues();
+ query.addCriteria(dsdName.eq(name).and(context.eq(contextId)));
+ Results results = query.execute();
+ for (Result result : results.all())
+ {
+ m_dsdCache.clear(result.getKey());
+ }
+ }
+
+ /**
+ * Given a role name, return the set of DSD's that have a matching member.
+ *
+ * @param name contains name of authorized Role used to search the cache.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return un-ordered set of matching DSD's.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ private static Set<SDSet> getDsdCache(String name, String contextId)
+ throws SecurityException
+ {
+ contextId = getContextId(contextId);
+ Set<SDSet> finalSet = new HashSet<>();
+ Attribute<String> context = m_dsdCache.getSearchAttribute(CONTEXT_ID);
+ Attribute<String> member = m_dsdCache.getSearchAttribute(SchemaConstants.MEMBER_AT);
+ Query query = m_dsdCache.createQuery();
+ query.includeKeys();
+ query.includeValues();
+ query.addCriteria(member.eq(name).and(context.eq(contextId)));
+ Results results = query.execute();
+ boolean empty = false;
+ for (Result result : results.all())
+ {
+ DsdCacheEntry entry = (DsdCacheEntry) result.getValue();
+ if (!entry.isEmpty())
+ {
+ finalSet.add(entry.getSdSet());
+ finalSet = putDsdCache(name, contextId);
+ }
+ else
+ {
+ empty = true;
+ }
+ finalSet.add(entry.getSdSet());
+ }
+ // If nothing was found in the cache, determine if it needs to be seeded:
+ if (finalSet.size() == 0 && !empty)
+ {
+ finalSet = putDsdCache(name, contextId);
+ }
+ return finalSet;
+ }
+
+ /**
+ * Given a Set of authorized Roles, return the set of DSD's that have matching members.
+ *
+ * @param authorizedRoleSet contains an un-order Set of authorized Roles.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return un-ordered set of matching DSD's.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ static Set<SDSet> getDsdCache(Set<String> authorizedRoleSet, String contextId)
+ throws SecurityException
+ {
+ contextId = getContextId(contextId);
+ Set<SDSet> dsdRetSets = new HashSet<>();
+ // Need to proceed?
+ if (!ObjUtil.isNotNullOrEmpty(authorizedRoleSet))
+ {
+ return dsdRetSets;
+ }
+ // Was the DSD Cache switched off?
+ boolean isCacheDisabled = Config.getBoolean(IS_DSD_CACHE_DISABLED_PARM, false);
+ // If so, get DSD's from LDAP:
+ if (isCacheDisabled)
+ {
+ SDSet sdSet = new SDSet();
+ sdSet.setType(SDSet.SDType.DYNAMIC);
+ sdSet.setContextId(contextId);
+ dsdRetSets = sp.search(authorizedRoleSet, sdSet);
+ }
+ // Search the DSD cache for matching Role members:
+ else
+ {
+ // Search on roleName attribute which maps to 'member' attr on the cache record:
+ Attribute<String> member = m_dsdCache.getSearchAttribute(SchemaConstants.MEMBER_AT);
+ Attribute<String> context = m_dsdCache.getSearchAttribute(CONTEXT_ID);
+ Query query = m_dsdCache.createQuery();
+ query.includeKeys();
+ query.includeValues();
+ // Add the passed in authorized Role names to this cache query:
+ Set<String> roles = new HashSet<>(authorizedRoleSet);
+ query.addCriteria(member.in(roles).and(context.eq(contextId)));
+ // Return all DSD cache entries that match roleName to the 'member' attribute in cache entry:
+ Results results = query.execute();
+ for (Result result : results.all())
+ {
+ DsdCacheEntry entry = (DsdCacheEntry) result.getValue();
+ // Do not add dummy DSD sets to the final list:
+ if (!entry.isEmpty())
+ {
+ dsdRetSets.add(entry.getSdSet());
+ }
+ // Remove role member from authorizedRoleSet to preclude from upcoming DSD search:
+ authorizedRoleSet.remove(entry.getMember());
+ }
+ // Authorized roles remaining in this set correspond to missed cache hits from above:
+ if (authorizedRoleSet.size() > 0)
+ {
+ dsdRetSets = putDsdCache(authorizedRoleSet, contextId);
+ }
+ }
+ return dsdRetSets;
+ }
+
+ /**
+ * Get the matching DSD's from directory and add to the cache (if found). If matching DSD not found,
+ * add dummy entry to cache to prevent repeated searches.
+ *
+ * @param authorizedRoleSet contains set of Roles used to search directory for matching DSD's.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return List of DSD's who have matching Role members.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ private static Set<SDSet> putDsdCache(Set<String> authorizedRoleSet, String contextId)
+ throws SecurityException
+ {
+ contextId = getContextId(contextId);
+ Set<SDSet> dsdSets = new HashSet<>();
+ // Search the DSD's iteratively to seed the DSD cache by Role name:
+ for (String roleName : authorizedRoleSet)
+ {
+ Role role = new Role(roleName);
+ role.setContextId(contextId);
+ List<SDSet> dsdList = sp.search(role, SDSet.SDType.DYNAMIC);
+ if (ObjUtil.isNotNullOrEmpty(dsdList))
+ {
+ for (SDSet dsd : dsdList)
+ {
+ dsd.setContextId(contextId);
+ Set<String> members = dsd.getMembers();
+ if (members != null)
+ {
+ // Seed the cache with DSD objects mapped to role name:
+ for (String member : members)
+ {
+ String key = buildKey(dsd.getName(), member);
+ DsdCacheEntry entry = new DsdCacheEntry(member, dsd, false);
+ entry.setName(dsd.getName());
+ m_dsdCache.put(getKey(key, contextId), entry);
+ }
+ }
+ }
+ // Maintain the set of DSD's to be returned to the caller:
+ dsdSets.addAll(dsdList);
+ }
+ else
+ {
+ // Seed the cache with dummy entry for a Role that is not referenced by DSD:
+ String key = buildKey(EMPTY_ELEMENT, roleName);
+ SDSet sdSet = new SDSet();
+ sdSet.setType(SDSet.SDType.DYNAMIC);
+ sdSet.setName(key);
+ sdSet.setMember(roleName);
+ sdSet.setContextId(contextId);
+ DsdCacheEntry entry = new DsdCacheEntry(roleName, sdSet, true);
+ entry.setName(key);
+ m_dsdCache.put(getKey(sdSet.getName(), contextId), entry);
+ }
+ }
+ return dsdSets;
+ }
+
+ /**
+ * Get the matching DSD's from directory and add to the cache (if found). If matching DSD not found,
+ * add dummy entry to cache to prevent repeated searches.
+ *
+ * @param roleName of Role is used to search directory for matching DSD's.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return Set of DSD's who have matching Role member.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ private static Set<SDSet> putDsdCache(String roleName, String contextId)
+ throws SecurityException
+ {
+ contextId = getContextId(contextId);
+ Role role = new Role(roleName);
+ role.setContextId(contextId);
+ List<SDSet> dsdList = sp.search(role, SDSet.SDType.DYNAMIC);
+ Set<SDSet> finalSet = new HashSet<>(dsdList);
+ if ( ObjUtil.isNotNullOrEmpty( dsdList ))
+ {
+ for (SDSet dsd : dsdList)
+ {
+ dsd.setContextId(contextId);
+ Set<String> members = dsd.getMembers();
+ if (members != null)
+ {
+ // Seed the cache with DSD objects mapped to role name:
+ for (String member : members)
+ {
+ String key = buildKey(dsd.getName(), member);
+ DsdCacheEntry entry = new DsdCacheEntry(member, dsd, false);
+ entry.setName(dsd.getName());
+ m_dsdCache.put(getKey(key, contextId), entry);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Seed the cache with dummy entry for Role that does not have DSD:
+ String key = buildKey(EMPTY_ELEMENT, roleName);
+ SDSet sdSet = new SDSet();
+ sdSet.setType(SDSet.SDType.DYNAMIC);
+ sdSet.setName(key);
+ sdSet.setMember(roleName);
+ sdSet.setContextId(contextId);
+ DsdCacheEntry entry = new DsdCacheEntry(roleName, sdSet, true);
+ entry.setName(key);
+ m_dsdCache.put(getKey(sdSet.getName(), contextId), entry);
+ }
+ return finalSet;
+ }
+
+ /**
+ * Given entry name, clear its corresponding object value from the cache.
+ *
+ * @param name contains the name of object to be cleared.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ static void clearSsdCacheEntry(String name, String contextId)
+ {
+ contextId = getContextId(contextId);
+ m_ssdCache.clear(getKey(name, contextId));
+ }
+
+ /**
+ * Get the matching SSD's from directory and add to the cache (if found).
+ *
+ * @param name of Role is used to search directory for matching SSD's.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return List of SSD's who have matching Role member.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ private static List<SDSet> putSsdCache(String name, String contextId)
+ throws SecurityException
+ {
+ Role role = new Role(name);
+ role.setContextId(contextId);
+ List<SDSet> ssdSets = sp.search(role, SDSet.SDType.STATIC);
+ m_ssdCache.put(getKey(name, contextId), ssdSets);
+ return ssdSets;
+ }
+
+ /**
+ * Look in cache for matching List of SSD's.
+ *
+ * @param name of Role is used to search directory for matching SSD's.
+ * @param contextId maps to sub-tree in DIT, for example ou=contextId, dc=jts, dc = com.
+ * @return List of SSD's who have matching Role member.
+ * @throws SecurityException in the event of system or rule violation.
+ */
+ private static List<SDSet> getSsdCache(String name, String contextId)
+ throws SecurityException
+ {
+ List<SDSet> ssdSets = (List<SDSet>) m_ssdCache.get(getKey(name, contextId));
+ if (ssdSets == null)
+ {
+ ssdSets = putSsdCache(name, contextId);
+ }
+ return ssdSets;
+ }
+
+ /**
+ *
+ * @param parm1
+ * @param parm2
+ * @return
+ */
+ private static String buildKey(String parm1, String parm2)
+ {
+ return parm1 + ":" + parm2;
+ }
+
+ /**
+ *
+ * @param name
+ * @param contextId
+ * @return
+ */
+ private static String getKey(String name, String contextId)
+ {
+ contextId = getContextId(contextId);
+ return name += ":" + contextId;
+ }
+
+ /**
+ *
+ * @param contextId
+ * @return
+ */
+ private static String getContextId(String contextId)
+ {
+ String szContextId = GlobalIds.HOME;
+ if( StringUtils.isNotEmpty( contextId ) && !contextId.equals(GlobalIds.NULL))
+ {
+ szContextId = contextId;
+ }
+ return szContextId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java b/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java
new file mode 100755
index 0000000..2d2d75a
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SdDAO.java
@@ -0,0 +1,653 @@
+/*
+ * 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.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.SDSet;
+import org.apache.directory.fortress.core.util.ObjUtil;
+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.model.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.model.Role;
+
+
+/**
+ * This class performs persistence on the RBAC Static Separation of Duties and Dynamic Separation of Duties data sets.
+ * <p/>
+ * The Fortress SDSet entity is a composite of the following other Fortress structural and aux object classes:
+ * <h4>1. organizationalRole Structural Object Class is used to store basic attributes like cn and description</h4>
+ * <ul>
+ * <li> ------------------------------------------
+ * <li> <code>objectclass ( 2.5.6.8 NAME 'organizationalRole'</code>
+ * <li> <code>DESC 'RFC2256: an organizational role'</code>
+ * <li> <code>SUP top STRUCTURAL</code>
+ * <li> <code>MUST cn</code>
+ * <li> <code>MAY ( x121Address $ registeredAddress $ destinationIndicator $</code>
+ * <li> <code>preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $</code>
+ * <li> <code>telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $</code>
+ * <li> <code>seeAlso $ roleOccupant $ preferredDeliveryMethod $ street $</code>
+ * <li> <code>postOfficeBox $ postalCode $ postalAddress $</code>
+ * <li> <code>physicalDeliveryOfficeName $ ou $ st $ l $ description ) )</code>
+ * <li> ------------------------------------------
+ * </ul>
+ * <h4>2. The RBAC Separation of Duties</h4>
+ * <ul>
+ * <li> ---Static Separation of Duties Set-------
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.2.4</code>
+ * <li> <code>NAME 'ftSSDSet'</code>
+ * <li> <code>DESC 'Fortress Role Static Separation of Duty Set Object Class'</code>
+ * <li> <code>SUP organizationalrole</code>
+ * <li> <code>STRUCTURAL</code>
+ * <li> <code>MUST ( ftId $ ftSetName $ ftSetCardinality )</code>
+ * <li> <code>MAY ( ftRoles $ description ) )</code>
+ * <li> ------------------------------------------
+ * </ul>
+ * <p/>
+ * OR
+ * <h4>Dynamic Separation of Duties Set</h4>
+ * <ul>
+ * <li>
+ * <li> <code>objectclass ( 1.3.6.1.4.1.38088.2.5</code>
+ * <li> <code>NAME 'ftDSDSet'</code>
+ * <li> <code>DESC 'Fortress Role Dynamic Separation of Duty Set Object Class'</code>
+ * <li> <code>SUP organizationalrole</code>
+ * <li> <code>STRUCTURAL</code>
+ * <li> <code>MUST ( ftId $ ftSetName $ ftSetCardinality )</code>
+ * <li> <code>MAY ( ftRoles $ description ) )</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.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+final class SdDAO extends ApacheDsDataProvider
+{
+ private static final String SD_SET_NM = "ftSetName";
+ private static final String ROLES = "ftRoles";
+ private static final String SD_SET_CARDINALITY = "ftSetCardinality";
+
+ private static final String SSD_OBJECT_CLASS_NM = "ftSSDSet";
+ private static final String SSD_OBJ_CLASS[] =
+ {
+ SchemaConstants.TOP_OC, SSD_OBJECT_CLASS_NM, GlobalIds.FT_MODIFIER_AUX_OBJECT_CLASS_NAME
+ };
+
+ private static final String DSD_OBJECT_CLASS_NM = "ftDSDSet";
+ private static final String DSD_OBJ_CLASS[] =
+ {
+ SchemaConstants.TOP_OC, DSD_OBJECT_CLASS_NM, GlobalIds.FT_MODIFIER_AUX_OBJECT_CLASS_NAME
+ };
+
+ private static final String[] SD_SET_ATRS =
+ {
+ GlobalIds.FT_IID, SD_SET_NM, SchemaConstants.DESCRIPTION_AT, ROLES, SD_SET_CARDINALITY
+ };
+
+
+ /**
+ * @param entity
+ * @return
+ * @throws org.apache.directory.fortress.core.CreateException
+ */
+ SDSet create( SDSet entity ) throws CreateException
+ {
+ LdapConnection ld = null;
+ String dn = getDn( entity.getName(), entity.getContextId() );
+ String[] objectClass = SSD_OBJ_CLASS;
+
+ if ( entity.getType() == SDSet.SDType.DYNAMIC )
+ {
+ objectClass = DSD_OBJ_CLASS;
+ }
+
+ try
+ {
+ Entry entry = new DefaultEntry( dn );
+ entry.add( createAttributes( SchemaConstants.OBJECT_CLASS_AT, objectClass ) );
+ entity.setId();
+ entry.add( GlobalIds.FT_IID, entity.getId() );
+ entry.add( SD_SET_NM, entity.getName() );
+
+ // description field is optional on this object class:
+ if ( StringUtils.isNotEmpty( entity.getDescription() ) )
+ {
+ entry.add( SchemaConstants.DESCRIPTION_AT, entity.getDescription() );
+ }
+
+ // CN attribute is required for this object class:
+ entry.add( SchemaConstants.CN_AT, entity.getName() );
+ loadAttrs( entity.getMembers(), entry, ROLES );
+ entry.add( SD_SET_CARDINALITY, "" + entity.getCardinality() );
+
+ ld = getAdminConnection();
+ add( ld, entry, entity );
+ }
+ catch ( LdapException e )
+ {
+ String error = "create SD set name [" + entity.getName() + "] type [" + entity.getType()
+ + "] caught LdapException=" + e.getMessage();
+ int errCode;
+ if ( entity.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_ADD_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_ADD_FAILED;
+ }
+
+ throw new CreateException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+ return entity;
+ }
+
+
+ /**
+ * @param entity
+ * @return
+ * @throws org.apache.directory.fortress.core.UpdateException
+ */
+ SDSet update( SDSet entity ) throws UpdateException
+ {
+ LdapConnection ld = null;
+ String dn = getDn( entity.getName(), entity.getContextId() );
+
+ try
+ {
+ List<Modification> mods = new ArrayList<Modification>();
+
+ if ( StringUtils.isNotEmpty( entity.getDescription() ) )
+ {
+ mods.add( new DefaultModification(
+ ModificationOperation.REPLACE_ATTRIBUTE, SchemaConstants.DESCRIPTION_AT, entity.getDescription() ) );
+ }
+
+ if ( entity.getCardinality() != null )
+ {
+ mods.add( new DefaultModification(
+ ModificationOperation.REPLACE_ATTRIBUTE, SD_SET_CARDINALITY, entity.getCardinality().toString() ) );
+ }
+
+ loadAttrs( entity.getMembers(), mods, ROLES );
+
+ if ( mods.size() > 0 )
+ {
+ ld = getAdminConnection();
+ modify( ld, dn, mods, entity );
+ }
+ }
+ catch ( LdapException e )
+ {
+ String error = "update name [" + entity.getName() + "] type [" + entity.getType()
+ + "] caught LdapException=" + e.getMessage();
+ int errCode;
+ if ( entity.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_UPDATE_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_UPDATE_FAILED;
+ }
+
+ throw new UpdateException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+
+ return entity;
+ }
+
+
+ /**
+ * @param entity
+ * @throws org.apache.directory.fortress.core.RemoveException
+ */
+ SDSet remove( SDSet entity ) throws RemoveException
+ {
+ LdapConnection ld = null;
+ String dn = getDn( entity.getName(), entity.getContextId() );
+
+ try
+ {
+ ld = getAdminConnection();
+ delete( ld, dn, entity );
+ }
+ catch ( LdapException e )
+ {
+ String error = "remove SD name=" + entity.getName() + " type [" + entity.getType() + "] LdapException="
+ + e.getMessage();
+ int errCode;
+ if ( entity.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_DELETE_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_DELETE_FAILED;
+ }
+
+ throw new RemoveException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+
+ return entity;
+ }
+
+
+ /**
+ * @param sdSet
+ * @return
+ * @throws FinderException
+ */
+ SDSet getSD( SDSet sdSet ) throws FinderException
+ {
+ SDSet entity = null;
+ LdapConnection ld = null;
+ String dn = getDn( sdSet.getName(), sdSet.getContextId() );
+
+ try
+ {
+ ld = getAdminConnection();
+ Entry findEntry = read( ld, dn, SD_SET_ATRS );
+ if ( findEntry == null )
+ {
+ String warning = "getSD no entry found dn [" + dn + "]";
+ throw new FinderException( GlobalErrIds.SSD_NOT_FOUND, warning );
+ }
+ entity = unloadLdapEntry( findEntry, 0 );
+ }
+ catch ( LdapNoSuchObjectException e )
+ {
+ String warning = "getSD Obj COULD NOT FIND ENTRY for dn [" + dn + "]";
+ throw new FinderException( GlobalErrIds.SSD_NOT_FOUND, warning );
+ }
+ catch ( LdapException e )
+ {
+ String error = "getSSD dn [" + dn + "] LEXCD=" + e;
+ int errCode;
+
+ if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_READ_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_READ_FAILED;
+ }
+
+ throw new FinderException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+
+ return entity;
+ }
+
+
+ /**
+ * Given an SSD name and type, find matching object in the directory.
+ * @param sdset requires name and type.
+ * @return List of matching SDSets.
+ * @throws org.apache.directory.fortress.core.FinderException
+ */
+ List<SDSet> search( SDSet sdset ) throws FinderException
+ {
+ List<SDSet> sdList = new ArrayList<>();
+ LdapConnection ld = null;
+ String ssdRoot = getSdRoot( sdset.getContextId() );
+ String objectClass = SSD_OBJECT_CLASS_NM;
+
+ if ( sdset.getType() == SDSet.SDType.DYNAMIC )
+ {
+ objectClass = DSD_OBJECT_CLASS_NM;
+ }
+
+ try
+ {
+ String searchVal = encodeSafeText( sdset.getName(), GlobalIds.ROLE_LEN );
+ String filter = GlobalIds.FILTER_PREFIX + objectClass + ")(" + SD_SET_NM + "=" + searchVal + "*))";
+ ld = getAdminConnection();
+ SearchCursor searchResults = search( ld, ssdRoot,
+ SearchScope.SUBTREE, filter, SD_SET_ATRS, false, GlobalIds.BATCH_SIZE );
+ long sequence = 0;
+
+ while ( searchResults.next() )
+ {
+ sdList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+ }
+ }
+ catch ( LdapException e )
+ {
+ String error = "search sdset name [" + sdset.getName() + "] type [" + sdset.getType()
+ + "] caught LdapException=" + e.getMessage();
+ int errCode;
+
+ if ( sdset.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+ }
+
+ throw new FinderException( errCode, error, e );
+ }
+ catch ( CursorException e )
+ {
+ String error = "search sdset name [" + sdset.getName() + "] type [" + sdset.getType()
+ + "] caught CursorException=" + e.getMessage();
+ int errCode;
+
+ if ( sdset.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+ }
+
+ throw new FinderException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+ return sdList;
+ }
+
+
+ /**
+ * @param role
+ * @return
+ * @throws org.apache.directory.fortress.core.FinderException
+ */
+ List<SDSet> search( Role role, SDSet.SDType type ) throws FinderException
+ {
+ List<SDSet> sdList = new ArrayList<>();
+ LdapConnection ld = null;
+ String ssdRoot = getSdRoot( role.getContextId() );
+ String objectClass = SSD_OBJECT_CLASS_NM;
+ if ( type == SDSet.SDType.DYNAMIC )
+ {
+ objectClass = DSD_OBJECT_CLASS_NM;
+ }
+ try
+ {
+ String roleVal = encodeSafeText( role.getName(), GlobalIds.ROLE_LEN );
+ StringBuilder filterbuf = new StringBuilder();
+ filterbuf.append( GlobalIds.FILTER_PREFIX );
+ filterbuf.append( objectClass );
+ filterbuf.append( ")(" );
+
+ // Include any parents target role may have:
+ Set<String> roles = RoleUtil.getAscendants( role.getName(), role.getContextId() );
+
+ if ( ObjUtil.isNotNullOrEmpty( roles ) )
+ {
+ filterbuf.append( "|(" );
+ filterbuf.append( ROLES );
+ filterbuf.append( "=" );
+ filterbuf.append( roleVal );
+ filterbuf.append( ")" );
+
+ for ( String uRole : roles )
+ {
+ filterbuf.append( "(" );
+ filterbuf.append( ROLES );
+ filterbuf.append( "=" );
+ filterbuf.append( uRole );
+ filterbuf.append( ")" );
+ }
+ filterbuf.append( ")" );
+
+ }
+ else
+ {
+ filterbuf.append( ROLES );
+ filterbuf.append( "=" );
+ filterbuf.append( roleVal );
+ filterbuf.append( ")" );
+
+ }
+
+ filterbuf.append( ")" );
+ ld = getAdminConnection();
+ SearchCursor searchResults = search( ld, ssdRoot,
+ SearchScope.SUBTREE, filterbuf.toString(), SD_SET_ATRS, false, GlobalIds.BATCH_SIZE );
+
+ long sequence = 0;
+ while ( searchResults.next() )
+ {
+ sdList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+ }
+ }
+ catch ( LdapException e )
+ {
+ String error = "search role [" + role.getName() + "] type [" + type + "] caught LdapException="
+ + e.getMessage();
+ int errCode;
+
+ if ( type == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+ }
+
+ throw new FinderException( errCode, error, e );
+ }
+ catch ( CursorException e )
+ {
+ String error = "search role [" + role.getName() + "] type [" + type + "] caught CursorException="
+ + e.getMessage();
+ int errCode;
+
+ if ( type == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+ }
+
+ throw new FinderException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+
+ return sdList;
+ }
+
+
+ /**
+ * @param roles
+ * @param sdSet
+ * @return
+ * @throws org.apache.directory.fortress.core.FinderException
+ */
+ Set<SDSet> search( Set<String> roles, SDSet sdSet ) throws FinderException
+ {
+ Set<SDSet> sdList = new HashSet<>();
+ LdapConnection ld = null;
+ String ssdRoot = getSdRoot( sdSet.getContextId() );
+ String objectClass = SSD_OBJECT_CLASS_NM;
+
+ if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+ {
+ objectClass = DSD_OBJECT_CLASS_NM;
+ }
+
+ try
+ {
+ if ( ObjUtil.isNotNullOrEmpty( roles ) )
+ {
+ StringBuilder filterbuf = new StringBuilder();
+ filterbuf.append( GlobalIds.FILTER_PREFIX );
+ filterbuf.append( objectClass );
+ filterbuf.append( ")(|" );
+
+ for ( String rle : roles )
+ {
+ filterbuf.append( "(" );
+ filterbuf.append( ROLES );
+ filterbuf.append( "=" );
+ filterbuf.append( rle );
+ filterbuf.append( ")" );
+ }
+ filterbuf.append( "))" );
+ ld = getAdminConnection();
+ SearchCursor searchResults = search( ld, ssdRoot,
+ SearchScope.SUBTREE, filterbuf.toString(), SD_SET_ATRS, false, GlobalIds.BATCH_SIZE );
+ long sequence = 0;
+
+ while ( searchResults.next() )
+ {
+ sdList.add( unloadLdapEntry( searchResults.getEntry(), sequence++ ) );
+ }
+ }
+ }
+ catch ( LdapException e )
+ {
+ String error = "search type [" + sdSet.getType() + "] caught LdapException=" + e.getMessage();
+ int errCode;
+
+ if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+ }
+ throw new FinderException( errCode, error, e );
+ }
+ catch ( CursorException e )
+ {
+ String error = "search type [" + sdSet.getType() + "] caught CursorException=" + e.getMessage();
+ int errCode;
+
+ if ( sdSet.getType() == SDSet.SDType.DYNAMIC )
+ {
+ errCode = GlobalErrIds.DSD_SEARCH_FAILED;
+ }
+ else
+ {
+ errCode = GlobalErrIds.SSD_SEARCH_FAILED;
+ }
+ throw new FinderException( errCode, error, e );
+ }
+ finally
+ {
+ closeAdminConnection( ld );
+ }
+
+ return sdList;
+ }
+
+
+ /**
+ * @param le
+ * @return
+ * @throws LdapInvalidAttributeValueException
+ * @throws LdapException
+ */
+ private SDSet unloadLdapEntry( Entry le, long sequence ) throws LdapInvalidAttributeValueException
+ {
+ SDSet entity = new ObjectFactory().createSDset();
+ entity.setSequenceId( sequence );
+ entity.setId( getAttribute( le, GlobalIds.FT_IID ) );
+ entity.setName( getAttribute( le, SD_SET_NM ) );
+ entity.setDescription( getAttribute( le, SchemaConstants.DESCRIPTION_AT ) );
+ entity.setMembers( getAttributeSet( le, ROLES ) );
+ String szCard = getAttribute( le, SD_SET_CARDINALITY );
+ entity.setCardinality( Integer.valueOf( szCard ) );
+
+ return entity;
+ }
+
+
+ private String getDn( String name, String contextId )
+ {
+ return SchemaConstants.CN_AT + "=" + name + "," + getSdRoot( contextId );
+ }
+
+
+ private String getSdRoot( String contextId )
+ {
+ return getRootDn( contextId, GlobalIds.SD_ROOT );
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SdP.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SdP.java b/src/main/java/org/apache/directory/fortress/core/impl/SdP.java
new file mode 100755
index 0000000..a107231
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SdP.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.SecurityException;
+import org.apache.directory.fortress.core.model.Role;
+import org.apache.directory.fortress.core.model.SDSet;
+import org.apache.directory.fortress.core.model.VUtil;
+
+
+/**
+ * Process module for Separation of Duty Relation data sets. The Fortress SD data set can be of two types:
+ * <ol>
+ * <li>Static Separation of Duties (SSD)</li>
+ * <li>Dynamic Separation of Duties (DSD)</li>
+ * </ol>
+ * The SDSet entity itself distinguishes which is being targeted by {@link org.apache.directory.fortress.core.model.SDSet.SDType} which is equal to {@link org.apache.directory.fortress.core.model.SDSet.SDType#STATIC} or {@link org.apache.directory.fortress.core.model.SDSet.SDType#DYNAMIC}.
+ * This class performs data validations and error mapping in addition to calling DAO methods. It is typically called
+ * by internal Fortress Manager classes ({@link org.apache.directory.fortress.core.AdminMgr}, {@link org.apache.directory.fortress.core.ReviewMgr}) and also by internal SD utils.
+ * This class is not intended to be called externally or outside of Fortress Core itself. This class will accept {@link org.apache.directory.fortress.core.model.SDSet},
+ * validate its contents and forward on to it's corresponding DAO {@link org.apache.directory.fortress.core.impl.SdDAO}.
+ * <p>
+ * Class will throw {@link SecurityException} to caller in the event of security policy, data constraint violation or system
+ * error internal to DAO object. This class will forward DAO exceptions ({@link org.apache.directory.fortress.core.FinderException},
+ * {@link org.apache.directory.fortress.core.CreateException},{@link org.apache.directory.fortress.core.UpdateException},{@link org.apache.directory.fortress.core.RemoveException}),
+ * or {@link org.apache.directory.fortress.core.ValidationException} as {@link SecurityException}s with appropriate
+ * error id from {@link org.apache.directory.fortress.core.GlobalErrIds}.
+ * <p>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public final class SdP
+{
+ /**
+ * Get the DAO created:
+ */
+ private static final SdDAO sdDao = new SdDAO();
+
+
+ /**
+ * Package private constructor.
+ */
+ SdP()
+ {
+ }
+
+
+ /**
+ * Adds a new SDSet to directory. The OrgUnit SDType enum will determine which data set insertion will
+ * occur - STATIC or DYNAMIC. The SDSet entity input will be validated to ensure that:
+ * name is present, and reasonability checks on all of the other populated values.
+ *
+ * @param entity SDSet contains data targeted for insertion.
+ * @return SDSet entity copy of input + additional attributes (internalId) that were added by op.
+ * @throws SecurityException in the event of data validation or DAO system error.
+ */
+ SDSet add( SDSet entity ) throws SecurityException
+ {
+ validate( entity );
+ return sdDao.create( entity );
+ }
+
+
+ /**
+ * Updates existing SDSet in directory. The SDSet type enum will determine which data set insertion will
+ * occur - STATIC or DYNAMIC. The SDSet entity input will be validated to ensure that:
+ * name is present, and reasonability checks on all of the other populated values.
+ * Null or empty attributes are ignored.
+ *
+ * @param entity SDSet contains data targeted for updating. Null attributes ignored.
+ * @return SDSet entity copy of input + additional attributes (internalId) that were updated by op.
+ * @throws SecurityException in the event of data validation or DAO system error.
+ */
+ SDSet update( SDSet entity ) throws SecurityException
+ {
+ validate( entity );
+ return sdDao.update( entity );
+ }
+
+
+ /**
+ * This method performs a "hard" delete. It completely the SDSet node from the ldap directory.
+ * The SDSet type enum will determine where deletion will occur - STATIC or DYNAMIC data sets.
+ * SDSet entity must exist in directory prior to making this call else exception will be thrown.
+ *
+ * @param entity Contains the name of the SDSet node targeted for deletion.
+ * @return SDSet is a copy of entity.
+ * @throws SecurityException in the event of data validation or DAO system error.
+ */
+ SDSet delete( SDSet entity ) throws SecurityException
+ {
+ return sdDao.remove( entity );
+ }
+
+
+ /**
+ * Return a fully populated SDSet entity for a given STATIC or DYNAMIC SDSet name. If matching record not found a
+ * SecurityException will be thrown.
+ *
+ * @param entity contains full SDSet name used for STATIC or DYNAMIC data sets in directory.
+ * @return SDSet entity containing all attributes associated with ou in directory.
+ * @throws SecurityException in the event SDSet not found or DAO search error.
+ */
+ SDSet read( SDSet entity ) throws SecurityException
+ {
+ SDSet sde;
+ // The assumption is this method is called from ReviewMgr.ssdRoleSetRoles or ReviewMgr.dsdRoleSetRoles.
+ // If called from ReviewMgr, the object class type will be passed in:
+ SDSet.SDType type = entity.getType();
+ sde = sdDao.getSD( entity );
+ // Load the previously saved type onto the return entity:
+ sde.setType( type );
+ return sde;
+ }
+
+
+ /**
+ * Will search using a single RBAC Role name either STATIC or DYNAMIC SDSet depending on which type is passed.
+ * The role entity contains full RBAC Role name associated with SDSet node in directory.
+ *
+ * @param sdSet contains sdset name or partial name along with sdset type of STATIC or DYNAMIC.
+ * @return List of SDSet entities found.
+ * @throws SecurityException in the event of DAO search error.
+ */
+ List<SDSet> search( SDSet sdSet ) throws SecurityException
+ {
+ return sdDao.search( sdSet );
+ }
+
+
+ /**
+ * Will search using a single RBAC Role name either STATIC or DYNAMIC SDSet depending on which type is passed.
+ * The role entity contains full RBAC Role name associated with SDSet node in directory.
+ *
+ * @param role contains full role name associated with SDSet.
+ * @param type either STATIC or DYNAMIC depending on target search data set.
+ * @return List of SDSet entities found.
+ * @throws SecurityException in the event of DAO search error.
+ */
+ List<SDSet> search( Role role, SDSet.SDType type ) throws SecurityException
+ {
+ return sdDao.search( role, type );
+ }
+
+
+ /**
+ * Will search using list of input RBAC role names either STATIC or DYNAMIC SDSet depending on which type is passed.
+ * The role entity contains full RBAC Role name associated with SDSet node in directory.
+ *
+ * @param rls contains set of type String containing full role names associated with SDSet.
+ * @param sdSet contains type either STATIC or DYNAMIC depending on target search data set.
+ * @return List of SDSet entities found.
+ * @throws SecurityException in the event of DAO search error.
+ */
+ Set<SDSet> search( Set<String> rls, SDSet sdSet ) throws SecurityException
+ {
+ return sdDao.search( rls, sdSet );
+ }
+
+
+ /**
+ * Method will perform simple validations to ensure the integrity of the SDSet entity targeted for insertion
+ * or updating in directory. This method will ensure the name and type enum are specified. Method will
+ * also ensure every Role name set is valid RBAC role entity in directory. It will also perform
+ * reasonability check on description if set.
+ *
+ * @param entity contains the enum type to validate
+ * @throws SecurityException thrown in the event the attribute is null.
+ */
+ private void validate( SDSet entity )
+ throws SecurityException
+ {
+ // TODO: Add more validations here:
+ VUtil.safeText( entity.getName(), GlobalIds.OU_LEN );
+ if ( StringUtils.isNotEmpty( entity.getDescription() ) )
+ {
+ VUtil.description( entity.getDescription() );
+ }
+ Set<String> roles = entity.getMembers();
+ if ( roles != null )
+ {
+ RoleP rp = new RoleP();
+ for ( String key : roles )
+ {
+ // when removing last role member a placeholder must be left in data set:
+ if ( !key.equalsIgnoreCase( GlobalIds.NONE ) )
+ {
+ // Ensure the name exists:
+ Role role = new Role( key );
+ role.setContextId( entity.getContextId() );
+ rp.read( role );
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/ba64d26a/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java b/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java
new file mode 100755
index 0000000..4db75a6
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/impl/SetAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.fortress.core.impl;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Shawn McKinney
+ * Date: 1/21/12
+ * Time: 7:59 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class SetAdapter extends XmlAdapter<ArrayList<String>, Set<String>>
+{
+ public Set<String> unmarshal(ArrayList<String> val) throws Exception
+ {
+ Set<String> members = null;
+ if(val != null)
+ {
+ members = new TreeSet<>();
+ for(String member : val)
+ {
+ members.add(member);
+ }
+ }
+ return members;
+ }
+
+ public ArrayList<String> marshal(Set<String> val) throws Exception
+ {
+ ArrayList<String> members = null;
+ if(val != null)
+ {
+ members = new ArrayList<>();
+ for(String member : val)
+ {
+ members.add(member);
+ }
+ }
+ return members;
+ }
+}
+
+
+/*
+ public char[] unmarshal(String val) throws Exception
+ {
+ return val.toCharArray();
+ }
+
+ public String marshal(char[] val) throws Exception
+ {
+ return val.toString();
+ }
+
+ */