| /* |
| * 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.concurrent.locks.ReadWriteLock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import org.apache.commons.lang.StringUtils; |
| 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.Graphable; |
| import org.apache.directory.fortress.core.model.OrgUnit; |
| import org.apache.directory.fortress.core.util.VUtil; |
| import org.apache.directory.fortress.core.util.cache.Cache; |
| import org.apache.directory.fortress.core.util.cache.CacheMgr; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * Process module for the OrgUnit entity. The Fortress OrgUnit data set can be associated with two entities: |
| * {@link org.apache.directory.fortress.core.model.User} class or {@link org.apache.directory.fortress.core.model.PermObj} class. The OrgUnit entity itself is stored in two separate locations in the ldap tree one |
| * for each entity listed above. The type of OU entity is set via the enum attribute {@link org.apache.directory.fortress.core.model.OrgUnit.Type} which is equal to 'PERM' or 'USER'. |
| * This class performs data validations. The methods of this class are called by internal Fortress manager impl classes |
| * {@link DelAdminMgrImpl} and {@link DelReviewMgrImpl} but is also called by {@link org.apache.directory.fortress.core.impl.PermP#validate} method and {@link org.apache.directory.fortress.core.impl.UserP#validate} functions |
| * which ensure the entities are related to valid OU entries. This class is not intended to be called external |
| * to Fortress Core itself. This class will accept Fortress entity {@link org.apache.directory.fortress.core.model.OrgUnit}, validate its contents and forward on to it's |
| * corresponding DAO class {@link OrgUnitDAO} for data access. |
| * <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 GlobalErrIds}. |
| * <p> |
| * This class uses synchronized data sets ({@link #ouCache} but is thread safe. |
| |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| final class OrgUnitP |
| { |
| // init the logger: |
| private static final String CLS_NM = OrgUnitP.class.getName(); |
| private static final Logger LOG = LoggerFactory.getLogger( CLS_NM ); |
| |
| // these fields are used to synchronize access to the above static pools: |
| private static final ReadWriteLock userPoolLock = new ReentrantReadWriteLock(); |
| private static final ReadWriteLock permPoolLock = new ReentrantReadWriteLock(); |
| private static Cache ouCache; |
| |
| // DAO class for OU data sets must be initializer before the other statics: |
| private OrgUnitDAO oDao = new OrgUnitDAO(); |
| private static final String USER_OUS = "user.ous"; |
| private static final String PERM_OUS = "perm.ous"; |
| private static final String FORTRESS_OUS = "fortress.ous"; |
| |
| private void init() |
| { |
| CacheMgr cacheMgr = CacheMgr.getInstance(); |
| OrgUnitP.ouCache = cacheMgr.getCache( FORTRESS_OUS ); |
| } |
| |
| |
| /** |
| * Package private constructor. |
| */ |
| OrgUnitP() |
| { |
| init(); |
| } |
| |
| |
| /** |
| * This function uses a case insensitive search. |
| * @param entity |
| * @return true if valid, false otherwise. |
| */ |
| /* No Qualifier */boolean isValid( OrgUnit entity ) |
| { |
| boolean result = false; |
| |
| if ( entity.type == OrgUnit.Type.USER ) |
| { |
| try |
| { |
| userPoolLock.readLock().lock(); |
| Set<String> userPool = getUserSet( entity ); |
| |
| if ( userPool != null ) |
| { |
| result = userPool.contains( entity.getName() ); |
| } |
| } |
| finally |
| { |
| userPoolLock.readLock().unlock(); |
| } |
| } |
| else |
| { |
| try |
| { |
| permPoolLock.readLock().lock(); |
| Set<String> permPool = getPermSet( entity ); |
| |
| if ( permPool != null ) |
| { |
| result = permPool.contains( entity.getName() ); |
| } |
| } |
| finally |
| { |
| permPoolLock.readLock().unlock(); |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| /** |
| * Loads the User set for an orgUnit |
| * @param orgUnit The orgUnit |
| * @return The set of associated User |
| */ |
| private Set<String> loadUserSet( OrgUnit orgUnit ) |
| { |
| Set<String> ouUserSet = null; |
| |
| try |
| { |
| ouUserSet = oDao.getOrgs( orgUnit ); |
| } |
| catch ( SecurityException se ) |
| { |
| String warning = "loadOrgSet static initializer caught SecurityException=" + se; |
| LOG.info( warning, se ); |
| } |
| |
| ouCache.put( getKey( USER_OUS, orgUnit.getContextId() ), ouUserSet ); |
| |
| return ouUserSet; |
| } |
| |
| |
| /** |
| * Loads the Perm set for an orgUnit |
| * @param orgUnit The orgUnit |
| * @return The set of associated Perms |
| */ |
| private Set<String> loadPermSet( OrgUnit orgUnit ) |
| { |
| Set<String> ouPermSet = null; |
| |
| try |
| { |
| ouPermSet = oDao.getOrgs( orgUnit ); |
| } |
| catch ( SecurityException se ) |
| { |
| String warning = "loadOrgSet static initializer caught SecurityException=" + se; |
| LOG.info( warning, se ); |
| } |
| |
| ouCache.put( getKey( PERM_OUS, orgUnit.getContextId() ), ouPermSet ); |
| |
| return ouPermSet; |
| } |
| |
| |
| /** |
| * |
| * @param orgUnit will be a Perm OU. |
| * @return Set containing the OU mapping to a Perm type and tenant. |
| */ |
| private Set<String> getPermSet( OrgUnit orgUnit ) |
| { |
| @SuppressWarnings("unchecked") |
| Set<String> permSet = ( Set<String> ) ouCache.get( getKey( PERM_OUS, orgUnit.getContextId() ) ); |
| |
| if ( permSet == null ) |
| { |
| permSet = loadPermSet( orgUnit ); |
| } |
| |
| return permSet; |
| } |
| |
| |
| /** |
| * |
| * @param orgUnit will be a User OU |
| * @return Set containing the OU mapping to the user type and tenant. |
| */ |
| private Set<String> getUserSet( OrgUnit orgUnit ) |
| { |
| @SuppressWarnings("unchecked") |
| Set<String> userSet = ( Set<String> ) ouCache.get( getKey( USER_OUS, orgUnit.getContextId() ) ); |
| |
| if ( userSet == null ) |
| { |
| userSet = loadUserSet( orgUnit ); |
| } |
| |
| return userSet; |
| } |
| |
| |
| /** |
| * Return a fully populated OrgUnit entity for a given Perm or User orgUnitId. If matching record not found a |
| * SecurityException will be thrown. |
| * |
| * @param entity contains full orgUnit name used for User or Perm OU data sets in directory. |
| * @return OrgUnit entity containing all attributes associated with ou in directory. |
| * @throws SecurityException in the event OrgUnit not found or DAO search error. |
| */ |
| OrgUnit read( OrgUnit entity ) throws SecurityException |
| { |
| validate( entity, false ); |
| |
| return oDao.findByKey( entity ); |
| } |
| |
| |
| /** |
| * Will search either User or Perm OrgUnit data sets depending on which type is passed. |
| * The search string that contains full or partial OrgUnit name associated with OU node in directory. |
| * |
| * @param orgUnit contains full or partial OU name. |
| * @return List of type OrgUnit containing fully populated matching OU entities. If no records found this will be empty. |
| * @throws SecurityException in the event of DAO search error. |
| */ |
| List<OrgUnit> search( OrgUnit orgUnit ) throws SecurityException |
| { |
| // Call the finder. |
| return oDao.findOrgs( orgUnit ); |
| } |
| |
| |
| /** |
| * Adds a new OrgUnit to directory. The OrgUnit type enum will determine which data set insertion will |
| * occur - User or Perm. The OrgUnit entity input will be validated to ensure that: |
| * orgUnit name is present and type is specified, and reasonability checks on all of the other populated values. |
| * |
| * @param entity OrgUnit contains data targeted for insertion. |
| * @return OrgUnit entity copy of input + additional attributes (internalId) that were added by op. |
| * @throws SecurityException in the event of data validation or DAO system error. |
| */ |
| OrgUnit add( OrgUnit entity ) throws SecurityException |
| { |
| validate( entity, false ); |
| OrgUnit oe = oDao.create( entity ); |
| |
| if ( entity.getType() == OrgUnit.Type.USER ) |
| { |
| try |
| { |
| userPoolLock.writeLock().lock(); |
| |
| Set<String> userPool = getUserSet( entity ); |
| |
| if ( userPool != null ) |
| { |
| userPool.add( entity.getName() ); |
| } |
| } |
| finally |
| { |
| userPoolLock.writeLock().unlock(); |
| } |
| } |
| else |
| { |
| try |
| { |
| permPoolLock.writeLock().lock(); |
| |
| Set<String> permPool = getPermSet( entity ); |
| |
| if ( permPool != null ) |
| { |
| permPool.add( entity.getName() ); |
| } |
| } |
| finally |
| { |
| permPoolLock.writeLock().unlock(); |
| } |
| } |
| |
| return oe; |
| } |
| |
| |
| /** |
| * Updates existing OrgUnit in directory. The OrgUnit type enum will determine which data set insertion will |
| * occur - User or Perm. The OrgUnit entity input will be validated to ensure that: |
| * orgUnit name is present, and reasonability checks on all of the other populated values. |
| * Null or empty attributes are ignored. |
| * |
| * @param entity OrgUnit contains data targeted for updating. Null attributes ignored. |
| * @return OrgUnit entity copy of input + additional attributes (internalId) that were updated by op. |
| * @throws SecurityException in the event of data validation or DAO system error. |
| */ |
| OrgUnit update( OrgUnit entity ) throws SecurityException |
| { |
| validate( entity, false ); |
| |
| return oDao.update( entity ); |
| } |
| |
| |
| /** |
| * Remove the parent attribute from the OrgUnit entry in directory. The OrgUnit type enum will determine which data set insertion will |
| * occur - User or Perm. The OrgUnit entity input will be validated to ensure that: |
| * orgUnit name is present. |
| * |
| * @param entity OrgUnit contains data targeted for updating. Null attributes ignored. |
| * @throws SecurityException in the event of data validation or DAO system error. |
| */ |
| void deleteParent( OrgUnit entity ) throws SecurityException |
| { |
| validate( entity, false ); |
| oDao.deleteParent( entity ); |
| } |
| |
| |
| /** |
| * This method performs a "hard" delete. It completely the OrgUnit node from the ldap directory. |
| * The OrgUnit type enum will determine where deletion will occur - User or Perm OU data sets. |
| * OrgUnit entity must exist in directory prior to making this call else exception will be thrown. |
| * |
| * @param entity Contains the name of the OrgUnit node targeted for deletion. |
| * @return OrgUnit entity copy of input. |
| * @throws SecurityException in the event of data validation or DAO system error. |
| */ |
| OrgUnit delete( OrgUnit entity ) throws SecurityException |
| { |
| oDao.remove( entity ); |
| |
| if ( entity.getType() == OrgUnit.Type.USER ) |
| { |
| try |
| { |
| userPoolLock.writeLock().lock(); |
| Set<String> userPool = getUserSet( entity ); |
| |
| if ( userPool != null ) |
| { |
| userPool.remove( entity.getName() ); |
| } |
| } |
| finally |
| { |
| userPoolLock.writeLock().unlock(); |
| } |
| } |
| else |
| { |
| try |
| { |
| permPoolLock.writeLock().lock(); |
| Set<String> permPool = getPermSet( entity ); |
| |
| if ( permPool != null ) |
| { |
| permPool.remove( entity.getName() ); |
| } |
| } |
| finally |
| { |
| permPoolLock.writeLock().unlock(); |
| } |
| } |
| |
| return entity; |
| } |
| |
| |
| /** |
| * Return all OrgUnits that have a parent assignment. This used for hierarchical processing. |
| * |
| * @param orgUnit will either be a User or Perm OU. |
| * @return List of type OrgUnit containing {@link OrgUnit#name} and {@link OrgUnit#parents} populated. |
| * @throws SecurityException in the event of DAO search error. |
| */ |
| List<Graphable> getAllDescendants( OrgUnit orgUnit ) throws SecurityException |
| { |
| return oDao.getAllDescendants( orgUnit ); |
| } |
| |
| |
| /** |
| * Method will perform simple validations to ensure the integrity of the OrgUnit entity targeted for insertion |
| * or updating in directory. This method will ensure the name and type enum are specified. It will also perform |
| * reasonability check on description if set. |
| * |
| * @param entity contains the enum type to validate |
| * @param isUpdate not used at this time. |
| * @throws SecurityException thrown in the event the attribute is null. |
| */ |
| private void validate( OrgUnit entity, boolean isUpdate ) |
| throws SecurityException |
| { |
| VUtil.safeText( entity.getName(), GlobalIds.OU_LEN ); |
| |
| if ( StringUtils.isNotEmpty( entity.getDescription() ) ) |
| { |
| VUtil.description( entity.getDescription() ); |
| } |
| |
| if ( entity.getType() == null ) |
| { |
| String error = "validate null or empty org unit type"; |
| throw new SecurityException( GlobalErrIds.ORG_TYPE_NULL, error ); |
| } |
| } |
| |
| |
| /** |
| * Build a key that is composed of the OU type ({@link #USER_OUS} or {@link #PERM_OUS}) and the contextId which is the id of tenant. |
| * |
| * @param type either {@link #USER_OUS} or {@link #PERM_OUS}. |
| * @param contextId maps to sub-tree in DIT, e.g. ou=contextId, dc=example, dc=com. |
| * @return key mapping to this tenant's cache entry. |
| */ |
| private static String getKey( String type, String contextId ) |
| { |
| String key = type; |
| |
| if ( StringUtils.isNotEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) ) |
| { |
| key += ":" + contextId; |
| } |
| |
| return key; |
| } |
| } |