Package org.apache.jetspeed.security.spi.impl

Source Code of org.apache.jetspeed.security.spi.impl.DefaultJetspeedSecuritySynchronizer$InternalSynchronizationState

/*
* 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.jetspeed.security.spi.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.jetspeed.security.JetspeedPrincipal;
import org.apache.jetspeed.security.JetspeedPrincipalManager;
import org.apache.jetspeed.security.JetspeedPrincipalManagerProvider;
import org.apache.jetspeed.security.JetspeedPrincipalType;
import org.apache.jetspeed.security.SecurityException;
import org.apache.jetspeed.security.SecurityAttribute;
import org.apache.jetspeed.security.SecurityAttributeType;
import org.apache.jetspeed.security.SecurityAttributes;
import org.apache.jetspeed.security.mapping.SecurityEntityManager;
import org.apache.jetspeed.security.mapping.SecurityEntityRelationType;
import org.apache.jetspeed.security.mapping.model.Attribute;
import org.apache.jetspeed.security.mapping.model.Entity;
import org.apache.jetspeed.security.spi.JetspeedSecuritySynchronizer;

/**
* @author <a href="mailto:ddam@apache.org">Dennis Dam</a>
* @version $Id: DefaultJetspeedSecuritySynchronizer.java 767444 2009-04-22 10:08:53Z ate $
*/
public class DefaultJetspeedSecuritySynchronizer implements JetspeedSecuritySynchronizer
{
    private static final Logger logger = LoggerFactory.getLogger(DefaultJetspeedSecuritySynchronizer.class);
    JetspeedPrincipalManagerProvider principalManagerProvider;
    SecurityEntityManager securityEntityManager;
    Collection<String> supportedExternalEntityTypes = Collections.emptyList();
    Collection<SecurityEntityRelationType> supportedExternalEntityRelationTypes = Collections.emptyList();
    Map<String, Collection<SecurityEntityRelationType>> entityToRelationTypes = Collections.emptyMap();

    /**
     * @param principalManagerProvider
     * @param securityEntityManager
     */
    public DefaultJetspeedSecuritySynchronizer(JetspeedPrincipalManagerProvider principalManagerProvider, SecurityEntityManager securityEntityManager)
    {
        this.principalManagerProvider = principalManagerProvider;
        this.securityEntityManager = securityEntityManager;
        createRelations();
    }

    public synchronized void synchronizeAll()
    {
        setSynchronizing(true);
        try
        {
            // don't skip any entity type when synchronizing all
            Collection<String> skipEntities = new ArrayList<String>();
            InternalSynchronizationState synchronizationState = new InternalSynchronizationState(skipEntities);
            for (String type : securityEntityManager.getSupportedEntityTypes())
            {
                for (Entity entity : securityEntityManager.getAllEntities(type))
                {
                  // recursive is false, because that will synchronize all associated entities which are
                  // direct associations of the principal. Because all principal types are being processed, this ensures
                  // all associations are being processed.
                    recursiveSynchronizeEntity(entity, synchronizationState, false);
                }
            }
        }
        finally
        {
            setSynchronizing(false);
        }
    }

    public synchronized void synchronizePrincipalsByType(String type, boolean recursive)
    {
        setSynchronizing(true);
        try
        {
            Collection<Entity> entites = securityEntityManager.getAllEntities(type);
            Collection<String> skipEntities = new ArrayList<String>();
            if (!type.equals(JetspeedPrincipalType.USER))
            {
                // skip synchronizing users when not synchronizing the USER type itself
                skipEntities.add(JetspeedPrincipalType.USER);
            }
            InternalSynchronizationState synchronizationState = new InternalSynchronizationState(skipEntities);
            for (Entity entity : entites)
            {
                recursiveSynchronizeEntity(entity, synchronizationState, recursive);
            }
        }
        finally
        {
            setSynchronizing(false);
        }
    }

    public synchronized void synchronizeUserPrincipal(String name, boolean recursive)
    {
        setSynchronizing(true);
        try
        {
            // don't process relations going towards users to avoid sync'ing huge
            // amounts of data.
            // TODO: allow processing of required relations towards users.
            Collection<String> skipEntities = Arrays.asList(new String[] { JetspeedPrincipalType.USER });
            recursiveSynchronizeEntity(securityEntityManager.getEntity(JetspeedPrincipalType.USER, name), new InternalSynchronizationState(skipEntities), recursive);
        }
        finally
        {
            setSynchronizing(false);
        }
    }

    protected JetspeedPrincipal recursiveSynchronizeEntity(Entity entity, InternalSynchronizationState syncState, boolean recursive)
    {
        JetspeedPrincipal updatedPrincipal = null;
        if (entity != null && !syncState.isProcessed(entity))
        {
            // mark as processed, to avoid nasty loops
            syncState.setProcessed(entity);
            // update / create corresponding JetspeedPrincipal first
            updatedPrincipal = synchronizePrincipalAttributes(entity);
            if (updatedPrincipal != null)
            {
                // Synchronizing Relations
                synchronizeEntityRelations(updatedPrincipal, entity, syncState, recursive);
            }
        }
        return updatedPrincipal;
    }

    protected JetspeedPrincipal synchronizeEntityRelations(JetspeedPrincipal principal, Entity entity, InternalSynchronizationState syncState, boolean recursive)
    {
        if (entityToRelationTypes.values().size() != 0)
            // loop through all relation types for this entity type
            for (SecurityEntityRelationType relationTypeForThisEntity : entityToRelationTypes.get(entity.getType()))
            {
                // check at what side of the relationship this entity
                // represents (from or to) and check whether
                // entities on the other side should be synchronized.Entity
                // entity
                if (relationTypeForThisEntity.getFromEntityType().equals(entity.getType()))
                {
                    if (syncState.shouldFollowRelationTo(entity,true,relationTypeForThisEntity.getToEntityType()))
                    {
                        Collection<Long> updatedRelationToIds = synchronizeAddedEntityRelations(relationTypeForThisEntity, entity, principal, true, syncState, recursive);
                        synchronizeRemovedAssociations(updatedRelationToIds, relationTypeForThisEntity.getRelationType(), principal, true);
                    }
                }
                // the entity can represent either side or *both* sides of
                // the relationship, so synchronize both ways.
                if (relationTypeForThisEntity.getToEntityType().equals(entity.getType()))
                {
                    if (syncState.shouldFollowRelationTo(entity,false,relationTypeForThisEntity.getFromEntityType()))
                    {
                        Collection<Long> updatedRelationFromIds = synchronizeAddedEntityRelations(relationTypeForThisEntity, entity, principal, false, syncState, recursive);
                        synchronizeRemovedAssociations(updatedRelationFromIds, relationTypeForThisEntity.getRelationType(), principal, false);
                    }
                }
            }
        return principal;
    }

    /**
     * Synchronizes all relations found in the backend store (e.g. LDAP). Returns a collections of all related entity IDs found. This list can be
     * used to check whether associations found in the database should be removed, if the associated principal ID is not in this list.
     * @param relationTypeForThisEntity the type of relation that should be synchronized
     * @param entity the entity for which relations to other entities should be synchronized.
     * @param principal the principal that corresponds with entity
     * @param entityIsFromEntity the passed entity is the "from" side of a relation.
     * @param syncState the internal synchronization state
     * @param recursive whether related entities should be recursively synchronized (true) or not (false).
     * @return
     */
    protected Collection<Long> synchronizeAddedEntityRelations(SecurityEntityRelationType relationTypeForThisEntity, Entity entity, JetspeedPrincipal principal,
                                                              boolean entityIsFromEntity, InternalSynchronizationState syncState, boolean recursive)
    {
        Collection<Entity> relatedEntities = entityIsFromEntity ? securityEntityManager.getRelatedEntitiesFrom(entity, relationTypeForThisEntity)
                                                               : securityEntityManager.getRelatedEntitiesTo(entity, relationTypeForThisEntity);
        Collection<Long> externalRelatedEntityIds = new ArrayList<Long>();
        if (relatedEntities != null){
            for (Entity relatedEntity : relatedEntities)
            {
                Entity fromEntity = entityIsFromEntity ? entity : relatedEntity;
                Entity toEntity = entityIsFromEntity ? relatedEntity : entity;
                if (!syncState.isRelationProcessed(relationTypeForThisEntity, fromEntity, toEntity))
                {
                    // first flag the relation as processed to
                    // prevent synchronizing the same relation from
                    // the other side.
                    syncState.setRelationProcessed(relationTypeForThisEntity, fromEntity, toEntity, entityIsFromEntity);
                    // first create/update principal
                    JetspeedPrincipal relatedPrincipal = null;
                    if (recursive){
                        relatedPrincipal = recursiveSynchronizeEntity(relatedEntity, syncState,recursive);
                    } else {
                        // don't recursively synchronize the related entity. Only add an association (if missing) when the related entity was previously synchronized.
                        JetspeedPrincipalManager principalManager = principalManagerProvider.getManager(principalManagerProvider.getPrincipalType(relatedEntity.getType()));
                        if (principalManager != null)
                        {
                            relatedPrincipal = principalManager.getPrincipal(relatedEntity.getId());
                        }
                    }
                    // .. then update associations to / from it
                    JetspeedPrincipal fromPrincipal = entityIsFromEntity ? principal : relatedPrincipal;
                    JetspeedPrincipal toPrincipal = entityIsFromEntity ? relatedPrincipal : principal;
                    // does association exist in DB ?
                    if (relatedPrincipal != null && !associationExists(fromPrincipal, toPrincipal, relationTypeForThisEntity.getRelationType()))
                    {
                        synchronizeAddedPrincipalAssocation(fromPrincipal, toPrincipal, relationTypeForThisEntity.getRelationType());
                        externalRelatedEntityIds.add(relatedPrincipal.getId());
                    }
                   
                }
            }
        }
        return externalRelatedEntityIds;
    }

    protected void synchronizeRemovedAssociations(Collection<Long> externalRelatedEntityIds, String associationName, JetspeedPrincipal principal,
                                                  boolean isFromPrincipal)
    {
        // check whether associations were removed in external store (e.g.
        // LDAP), but still present in the DB
        if (logger.isDebugEnabled()){
            logger.debug("--- Synchronize removed associations ---");
        }
        JetspeedPrincipalManager principalManager = principalManagerProvider.getManager(principal.getType());
        List<? extends JetspeedPrincipal> relatedToPrincipals = null;
        if (isFromPrincipal)
        {
            relatedToPrincipals = principalManager.getAssociatedFrom(principal.getName(), principal.getType(), associationName);
        }
        else
        {
            relatedToPrincipals = principalManager.getAssociatedTo(principal.getName(), principal.getType(), associationName);
        }
        for (JetspeedPrincipal relatedPrincipal : relatedToPrincipals)
        {
            // check whether principal association still exists
            if (!externalRelatedEntityIds.contains(relatedPrincipal.getId()))
            {
                try
                {
                    if (isFromPrincipal)
                    {
                        principalManager.removeAssociation(principal, relatedPrincipal, associationName);
                        if (logger.isDebugEnabled()){
                            logger.debug("Removed association ["+principal.getName()+" ("+principal.getType().getName()+")] ---["+associationName+"]--> ["+relatedPrincipal.getName()+" ("+relatedPrincipal.getType().getName()+")]");
                        }
                    }
                    else
                    {
                        principalManager.removeAssociation(relatedPrincipal, principal, associationName);
                        if (logger.isDebugEnabled()){
                            logger.debug("Removed association ["+relatedPrincipal.getName()+" ("+relatedPrincipal.getType().getName()+")] ---["+associationName+"]--> ["+principal.getName()+" ("+principal.getType().getName()+")]");
                        }
                    }
                }
                catch (SecurityException e)
                {
                    if (isFromPrincipal)
                    {
                        logger.error("Unexpected SecurityException trying to remove (" + principal.getType().getName() + "," +
                                     relatedPrincipal.getType().getName() + "," + associationName + ") association during synchronization.", e);
                    }
                    else
                    {
                        logger.error("Unexpected SecurityException trying to remove (" + relatedPrincipal.getType().getName() + "," +
                                     principal.getType().getName() + "," + associationName + ") association during synchronization.", e);
                    }
                }
            }
        }
        if (logger.isDebugEnabled()){
            logger.debug("--- /END Synchronize removed associations ---");
        }
    }

    protected boolean associationExists(JetspeedPrincipal fromPrincipal, JetspeedPrincipal toPrincipal, String associationName)
    {
        JetspeedPrincipalManager principalManager = principalManagerProvider.getManager(fromPrincipal.getType());
        List<String> toPrincipals = principalManager.getAssociatedNamesFrom(fromPrincipal.getName(), fromPrincipal.getType(), associationName);
        return toPrincipals.contains(toPrincipal.getName());
    }

    protected void synchronizeAddedPrincipalAssocation(JetspeedPrincipal fromPrincipal, JetspeedPrincipal toPrincipal, String associationName)
    {
        JetspeedPrincipalManager principalManager = principalManagerProvider.getManager(fromPrincipal.getType());
        try
        {
            principalManager.addAssociation(fromPrincipal, toPrincipal, associationName);
        }
        catch (SecurityException e)
        {
            logger.error("Unexpected SecurityException during synchronization.", e);
        }
    }

    protected JetspeedPrincipal synchronizePrincipalAttributes(Entity entity)
    {
        JetspeedPrincipal updatedPrincipal = null;
        JetspeedPrincipalManager principalManager = principalManagerProvider.getManager(principalManagerProvider.getPrincipalType(entity.getType()));
        if (logger.isDebugEnabled()){
            logger.debug("--- Synchronize principal attributes ---");
        }
        if (principalManager != null)
        {
            updatedPrincipal = principalManager.getPrincipal(entity.getId());
            Map<String, Attribute> mappedEntityAttrs = entity.getMappedAttributes();
            Collection<Attribute> attrsToBeUpdated = new ArrayList<Attribute>();
            if (updatedPrincipal == null)
            {
                // principal does not exist yet, create it using the Jetspeed
                // principal manager
                updatedPrincipal = principalManager.newPrincipal(entity.getId(), true);
                try
                {
                    principalManager.addPrincipal(updatedPrincipal, null);
                    if (logger.isDebugEnabled()){
                        logger.debug("Adding principal "+updatedPrincipal.getName()+" of type "+updatedPrincipal.getType().getName()+" ...");
                    }
                }
                catch (SecurityException sexp)
                {
                    if (logger.isErrorEnabled())
                    {
                        logger.error("Unexpected exception in adding new pricipal of type " + updatedPrincipal.getType().getName() + ".", sexp);
                    }
                }
                attrsToBeUpdated.addAll(mappedEntityAttrs.values());
            }
            else if (updatedPrincipal.isMapped())
            {
                if (logger.isDebugEnabled()){
                    logger.debug("Updating principal "+updatedPrincipal.getName()+" of type "+updatedPrincipal.getType().getName()+" ...");
                }
                SecurityAttributes principalAttrs = updatedPrincipal.getSecurityAttributes();
                for (Map.Entry<String, Attribute> entityAttrEntry : mappedEntityAttrs.entrySet())
                {
                    SecurityAttribute principalAttr = principalAttrs.getAttribute(entityAttrEntry.getKey());
                    Attribute entityAttr = entityAttrEntry.getValue();
                    if (principalAttr != null)
                    {
                        if (entityAttr.getDefinition().isMultiValue())
                        {
                            // TODO : multi-valued Principal attrs are not yet
                            // supported
                        }
                        else
                        {
                            if (!StringUtils.equals(principalAttr.getStringValue(), entityAttr.getValue()))
                            {
                                attrsToBeUpdated.add(entityAttr);
                            }
                        }
                    }
                    else
                    {
                        attrsToBeUpdated.add(entityAttr);
                    }
                }
            }
            SecurityAttributes principalAttrs = updatedPrincipal.getSecurityAttributes();
            Map<String, SecurityAttributeType> securityAttrTypes = principalAttrs.getSecurityAttributeTypes().getAttributeTypeMap();
            // Step 1. update principal's attributes
            for (Attribute addedEntityAttr : attrsToBeUpdated)
            {
                if (!addedEntityAttr.getDefinition().isMultiValue())
                {
                    SecurityAttribute principalAttr = null;
                    try
                    {
                        SecurityAttributeType securityAttrType = securityAttrTypes.get(addedEntityAttr.getMappedName());
                        if (securityAttrType != null)
                        {
                            principalAttr = principalAttrs.getAttribute(addedEntityAttr.getMappedName(), true);
                        }
                        if (principalAttr != null)
                            principalAttr.setStringValue(addedEntityAttr.getValue());
                        if (logger.isDebugEnabled()){
                            logger.debug("Marked attribute "+principalAttr.getName()+" as updated for principal "+updatedPrincipal.getName()+". New value: "+principalAttr.getStringValue());
                        }
                    }
                    catch (SecurityException e)
                    {
                        if (logger.isErrorEnabled())
                        {
                            logger.error("Unexpected exception for attribute " + addedEntityAttr.getMappedName() + ".", e);
                        }
                    }
                }
            }
            if (updatedPrincipal.isMapped())
            {
                boolean updated = (attrsToBeUpdated.size() > 0);
                // Step 2, check whether attributes should be removed.
                for (Map.Entry<String, SecurityAttribute> principalAttrEntry : principalAttrs.getAttributeMap().entrySet())
                {
                    // TODO: check whether this attribute is mapped
                    if (!mappedEntityAttrs.containsKey(principalAttrEntry.getKey()))
                    {
                        try
                        {
                            principalAttrs.removeAttribute(principalAttrEntry.getKey());
                            updated = true;
                            if (logger.isDebugEnabled()){
                                logger.debug("Marked attribute "+principalAttrEntry.getKey()+" as removed for principal "+updatedPrincipal.getName());
                            }
                        }
                        catch (SecurityException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
                // step 3, update synchronized principal
                if (updated)
                {
                    try
                    {
                        principalManager.updatePrincipal(updatedPrincipal);
                        if (logger.isDebugEnabled()){
                            logger.debug("Committing attribute changes for principal "+updatedPrincipal.getName());
                        }
                    }
                    catch (SecurityException e)
                    {
                        logger.error("Unexpected SecurityException: could not synchronize principal " + updatedPrincipal.getName() + " of type " +
                                     updatedPrincipal.getType().getName(), e);
                    }
                }
            }
        }
        else
        {
            // TODO throw proper exception
        }
        if (logger.isDebugEnabled()){
            logger.debug("--- /END Synchronize principal attributes ---");
        }
        return updatedPrincipal;
    }

    private void setSynchronizing(boolean sync)
    {
        SynchronizationStateAccess.setSynchronizing(sync ? Boolean.TRUE : Boolean.FALSE);
    }

    public void setPrincipalManagerProvider(JetspeedPrincipalManagerProvider principalManagerProvider)
    {
        this.principalManagerProvider = principalManagerProvider;
    }

    private void createRelations()
    {
        supportedExternalEntityTypes = securityEntityManager.getSupportedEntityTypes();
        supportedExternalEntityRelationTypes = securityEntityManager.getSupportedEntityRelationTypes();
        entityToRelationTypes = new HashMap<String, Collection<SecurityEntityRelationType>>();
        for (String entityType : supportedExternalEntityTypes)
        {
            entityToRelationTypes.put(entityType, securityEntityManager.getSupportedEntityRelationTypes(entityType));
        }
    }

    protected class InternalSynchronizationState
    {
        // entity type to processed entity IDs map
        Map<String, Set<String>> processedEntities = new HashMap<String, Set<String>>();
        // map relation type to a "from entity" -> "to entity" mapping
        Map<SecurityEntityRelationType, Map<String, Collection<String>>> processedEntityRelationsFromTo = new HashMap<SecurityEntityRelationType, Map<String, Collection<String>>>();
        // Entity types which are not processed
        // This is implemented as not following relations towards entities of
        // these types. E.g. if skipEntities contains the "user" type, and
        // isProcessedFromTo(..) is invoked,
        // where the toEntity is of type "user", then the result of
        // isProcessedFromTo will be "true", to effectively skip the processing
        // of entities of type "user".
        // The same goes for isProcessedToFrom(..) : if the type of fromEntity
        // is "user", the relation is flagged as processed.
        Collection<String> skipEntities;

        InternalSynchronizationState(Collection<String> skipEntities)
        {
            this.skipEntities = skipEntities;
        }

        protected boolean isProcessed(Entity entity)
        {
            Set<String> processedEntitiesByType = processedEntities.get(entity.getType());
            return processedEntitiesByType != null && processedEntitiesByType.contains(entity.getId());
        }

        protected boolean shouldFollowRelationTo(Entity entity, boolean isFromEntity, String relationType)
        {
            return !skipEntities.contains(relationType);
        }

        protected void setProcessed(Entity entity)
        {
            Set<String> processedEntitiesByType = processedEntities.get(entity.getType());
            if (processedEntitiesByType == null)
            {
                processedEntitiesByType = new HashSet<String>();
            }
            processedEntitiesByType.add(entity.getId());
        }

        protected boolean isRelationProcessed(SecurityEntityRelationType relationType, Entity fromEntity, Entity toEntity)
        {
            Map<String, Collection<String>> e2eMap = processedEntityRelationsFromTo.get(relationType);
            if (e2eMap != null)
            {
                Collection<String> endIds = e2eMap.get(fromEntity.getId());
                return endIds != null && endIds.contains(toEntity.getId());
            }
            return false;
        }

        protected void setRelationProcessed(SecurityEntityRelationType relationType, Entity startEntity, Entity endEntity, boolean startEntityIsFrom)
        {
            if (startEntityIsFrom)
            {
                setRelationProcessed(relationType, startEntity, endEntity);
            }
            else
            {
                setRelationProcessed(relationType, endEntity, startEntity);
            }
        }

        protected void setRelationProcessed(SecurityEntityRelationType relationType, Entity fromEntity, Entity toEntity)
        {
            Map<String, Collection<String>> e2eMap = processedEntityRelationsFromTo.get(relationType);
            if (e2eMap == null)
            {
                e2eMap = new HashMap<String, Collection<String>>();
                processedEntityRelationsFromTo.put(relationType, e2eMap);
            }
            Collection<String> endIds = e2eMap.get(fromEntity.getId());
            if (endIds == null)
            {
                endIds = new ArrayList<String>();
                e2eMap.put(fromEntity.getId(), endIds);
            }
            endIds.add(toEntity.getId());
        }
    }
}
TOP

Related Classes of org.apache.jetspeed.security.spi.impl.DefaultJetspeedSecuritySynchronizer$InternalSynchronizationState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.