Merged V3.4-BUG-FIX to HEAD

29333: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/V3.4-BUG-FIX:
      29311: Fixed tomcat shutdown problem (ALF-9574)
   29337: Merged DEV to V3.4-BUG-FIX
      29336: ALF-8554 : matchesEncodedPattern in ISO9075 class fails with some values
             Corrected matchesEncodedPattern method and unit test added to demonstrate the problem has gone.
   29340: Merged DEV to HEAD
      29330: ALF-3681 : Webdav lock issue returned on 3.3g and 4.0a
             LockMethod corrected: expiryDate (as lockOwner) should be cached in fileInfo's properties for correct response generation.
   29361: Fix for CIFS desktop actions are copied by copying a folder: ALF-8640.
   Pseudo file creates are converted to a file open, they are not writeable, writes are dumped.
   29380: ALF-6434: Better detection / prevention of cyclic group relationships in LDAP sync
   29387: Incremented version.revision for 3.4.5
   29388: Merged V3.4 to V3.4-BUG-FIX
      29307: Merged V3.4-BUG-FIX to V3.4 (3.4.4) (RECORD ONLY)
      29314: ALF-9612: Temporary placeholders for missing installer translations
      29385: ALF-9612: New installer translations from Gloria


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29389 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Dave Ward
2011-07-27 11:34:50 +00:00
parent 1111cb6155
commit 73826a7892
3 changed files with 333 additions and 214 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -20,10 +20,10 @@ package org.alfresco.repo.security.sync;
import java.io.Serializable;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -49,8 +49,8 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authority.UnknownAuthorityException;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
@@ -633,13 +633,14 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
+ " Group Analysis", this.transactionService.getRetryingTransactionHelper(), userRegistry
.getGroups(lastModified), this.workerThreads, 20, this.applicationEventPublisher,
ChainingUserRegistrySynchronizer.logger, this.loggingInterval);
class Analyzer implements BatchProcessWorker<NodeDescription>
class Analyzer extends BaseBatchProcessWorker<NodeDescription>
{
private final Map<String, String> groupsToCreate = new TreeMap<String, String>();
private final Map<String, Set<String>> personParentAssocsToCreate = newPersonMap();
private final Map<String, Set<String>> personParentAssocsToDelete = newPersonMap();
private final Map<String, Set<String>> groupParentAssocsToCreate = new TreeMap<String, Set<String>>();
private Map<String, Set<String>> groupParentAssocsToCreate = new TreeMap<String, Set<String>>();
private final Map<String, Set<String>> groupParentAssocsToDelete = new TreeMap<String, Set<String>>();
private final Map<String, Set<String>> finalGroupChildAssocs = new TreeMap<String, Set<String>>();
private List<String> personsProcessed = new LinkedList<String>();
private Set<String> allZonePersons;
private Set<String> deletionCandidates;
@@ -666,22 +667,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
return entry.getSourceId();
}
public void beforeProcess() throws Throwable
{
// Disable rules
ChainingUserRegistrySynchronizer.this.ruleService.disableRules();
// Authentication
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
}
public void afterProcess() throws Throwable
{
// Enable rules
ChainingUserRegistrySynchronizer.this.ruleService.enableRules();
// Clear authentication
AuthenticationUtil.clearCurrentSecurityContext();
}
public void process(NodeDescription group) throws Throwable
{
PropertyMap groupProperties = group.getProperties();
@@ -754,6 +739,45 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
}
}
// Recursively walks and caches the authorities relating to and from this group so that we can later detect potential cycles
private Set<String> getContainedAuthorities(String groupName)
{
// Return the cached children if it is processed
Set<String> children = this.finalGroupChildAssocs.get(groupName);
if (children != null)
{
return children;
}
// First, recurse to the parent most authorities
for (String parent : ChainingUserRegistrySynchronizer.this.authorityService.getContainingAuthorities(
null, groupName, true))
{
getContainedAuthorities(parent);
}
// Return the cached children if it is now processed
children = this.finalGroupChildAssocs.get(groupName);
if (children != null)
{
return children;
}
// Now descend on unprocessed parents.
children = ChainingUserRegistrySynchronizer.this.authorityService.getContainedAuthorities(null,
groupName, true);
this.finalGroupChildAssocs.put(groupName, children);
for (String child : children)
{
if (AuthorityType.getAuthorityType(child) != AuthorityType.USER)
{
getContainedAuthorities(child);
}
}
return children;
}
private synchronized void updateGroup(NodeDescription group, boolean existed)
{
PropertyMap groupProperties = group.getProperties();
@@ -765,7 +789,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
}
// Add an entry for the parent itself, in case it is a root group
recordParentAssociation(this.groupParentAssocsToCreate, groupName, null);
recordParentAssociationDeletion(groupName, null);
// Divide the child associations into person and group associations, dealing with case sensitivity
Set<String> newChildPersons = newPersonSet();
@@ -791,25 +815,22 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
groupDisplayName);
// Work out the association differences
for (String child : ChainingUserRegistrySynchronizer.this.authorityService.getContainedAuthorities(
null, groupName, true))
for (String child : new TreeSet<String>(getContainedAuthorities(groupName)))
{
if (AuthorityType.getAuthorityType(child) == AuthorityType.USER)
{
if (!newChildPersons.remove(child))
{
// Make sure each child features as a key in the creation map
recordParentAssociation(this.personParentAssocsToCreate, child, null);
recordParentAssociation(this.personParentAssocsToDelete, child, groupName);
recordParentAssociationCreation(child, null);
recordParentAssociationDeletion(child, groupName);
}
}
else
{
if (!newChildGroups.remove(child))
{
// Make sure each child features as a key in the creation map
recordParentAssociation(this.groupParentAssocsToCreate, child, null);
recordParentAssociation(this.groupParentAssocsToDelete, child, groupName);
recordParentAssociationDeletion(child, groupName);
}
}
}
@@ -823,16 +844,31 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
// Create new associations
for (String child : newChildPersons)
{
recordParentAssociation(this.personParentAssocsToCreate, child, groupName);
recordParentAssociationCreation(child, groupName);
}
for (String child : newChildGroups)
{
recordParentAssociation(this.groupParentAssocsToCreate, child, groupName);
recordParentAssociationCreation(child, groupName);
}
}
private void recordParentAssociation(Map<String, Set<String>> parentAssocs, String child, String parent)
private void recordParentAssociationDeletion(String child, String parent)
{
Map<String, Set<String>> parentAssocs;
if (AuthorityType.getAuthorityType(child) == AuthorityType.USER)
{
parentAssocs = this.personParentAssocsToDelete;
}
else
{
// Reflect the change in the map of final group associations (for cycle detection later)
parentAssocs = this.groupParentAssocsToDelete;
if (parent != null)
{
Set<String> children = this.finalGroupChildAssocs.get(parent);
children.remove(child);
}
}
Set<String> parents = parentAssocs.get(child);
if (parents == null)
{
@@ -845,6 +881,162 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
}
}
private void recordParentAssociationCreation(String child, String parent)
{
Map<String, Set<String>> parentAssocs = AuthorityType.getAuthorityType(child) == AuthorityType.USER ? this.personParentAssocsToCreate : this.groupParentAssocsToCreate;
Set<String> parents = parentAssocs.get(child);
if (parents == null)
{
parents = new TreeSet<String>();
parentAssocs.put(child, parents);
}
if (parent != null)
{
parents.add(parent);
}
}
private void validateGroupParentAssocsToCreate()
{
Iterator<Map.Entry<String, Set<String>>> i = this.groupParentAssocsToCreate.entrySet().iterator();
while (i.hasNext())
{
Map.Entry<String, Set<String>> entry = i.next();
String group = entry.getKey();
Set<String> parents = entry.getValue();
Deque<String> visited = new LinkedList<String>();
Iterator<String> j = parents.iterator();
while (j.hasNext())
{
String parent = j.next();
visited.add(parent);
if (validateAuthorityChildren(visited, group))
{
// The association validated - commit it
Set<String> children = finalGroupChildAssocs.get(parent);
if (children == null)
{
children = new TreeSet<String>();
finalGroupChildAssocs.put(parent, children);
}
children.add(group);
}
else
{
// The association did not validate - prune it out
if (logger.isWarnEnabled())
{
ChainingUserRegistrySynchronizer.logger.warn("Not adding group '"
+ ChainingUserRegistrySynchronizer.this.authorityService.getShortName(group)
+ "' to group '"
+ ChainingUserRegistrySynchronizer.this.authorityService.getShortName(parent)
+ "' as this creates a cyclic relationship");
}
j.remove();
}
visited.removeLast();
}
if (parents.isEmpty())
{
i.remove();
}
}
// Sort the group associations in parent-first order (root groups first) to minimize reindexing overhead
Map<String, Set<String>> sortedGroupAssociations = new LinkedHashMap<String, Set<String>>(
this.groupParentAssocsToCreate.size() * 2);
Deque<String> visited = new LinkedList<String>();
for (String authority : this.groupParentAssocsToCreate.keySet())
{
visitGroupParentAssocs(visited, authority, this.groupParentAssocsToCreate, sortedGroupAssociations);
}
this.groupParentAssocsToCreate = sortedGroupAssociations;
}
private boolean validateAuthorityChildren(Deque<String> visited, String authority)
{
if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER)
{
return true;
}
if (visited.contains(authority))
{
return false;
}
visited.add(authority);
try
{
Set<String> children = this.finalGroupChildAssocs.get(authority);
if (children != null)
{
for (String child : children)
{
if (!validateAuthorityChildren(visited, child))
{
return false;
}
}
}
return true;
}
finally
{
visited.removeLast();
}
}
/**
* Visits the given authority by recursively visiting its parents in associationsOld and then adding the
* authority to associationsNew. Used to sort associationsOld into 'parent-first' order to minimize
* reindexing overhead.
*
* @param visited
* The ancestors that form the path to the authority to visit. Allows detection of cyclic child
* associations.
* @param authority
* the authority to visit
* @param associationsOld
* the association map to sort
* @param associationsNew
* the association map to add to in parent-first order
*/
private boolean visitGroupParentAssocs(Deque<String> visited, String authority,
Map<String, Set<String>> associationsOld, Map<String, Set<String>> associationsNew)
{
if (visited.contains(authority))
{
// Prevent cyclic paths (Shouldn't happen as we've already validated)
return false;
}
visited.add(authority);
try
{
if (!associationsNew.containsKey(authority))
{
Set<String> oldParents = associationsOld.get(authority);
if (oldParents != null)
{
Set<String> newParents = new TreeSet<String>();
for (String parent: oldParents)
{
if (visitGroupParentAssocs(visited, parent, associationsOld, associationsNew))
{
newParents.add(parent);
}
}
associationsNew.put(authority, newParents);
}
}
return true;
}
finally
{
visited.removeLast();
}
}
private Set<String> newPersonSet()
{
return ChainingUserRegistrySynchronizer.this.personService.getUserNamesAreCaseSensitive() ? new TreeSet<String>()
@@ -956,53 +1148,24 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
// this set
this.allZonePersons = allZonePersons;
if (!this.groupParentAssocsToCreate.isEmpty())
if (!this.groupParentAssocsToDelete.isEmpty())
{
// Sort the group associations in depth-first order (root groups first) and filter out
// non-existent children
Map<String, Set<String>> sortedGroupAssociations = new LinkedHashMap<String, Set<String>>(
this.groupParentAssocsToCreate.size() * 2);
List<String> authorityPath = new ArrayList<String>(5);
for (String authority : this.groupParentAssocsToCreate.keySet())
{
authorityPath.add(authority);
visitGroupAssociations(authorityPath, this.groupParentAssocsToCreate,
sortedGroupAssociations);
authorityPath.clear();
}
// Add the groups and their parent associations in depth-first order
// Create/update the groups and delete parent associations to be deleted
BatchProcessor<Map.Entry<String, Set<String>>> groupCreator = new BatchProcessor<Map.Entry<String, Set<String>>>(
zone + " Group Creation and Association",
zone + " Group Creation and Association Deletion",
ChainingUserRegistrySynchronizer.this.transactionService.getRetryingTransactionHelper(),
sortedGroupAssociations.entrySet(),
this.groupParentAssocsToDelete.entrySet(),
ChainingUserRegistrySynchronizer.this.workerThreads, 20,
ChainingUserRegistrySynchronizer.this.applicationEventPublisher,
ChainingUserRegistrySynchronizer.logger,
ChainingUserRegistrySynchronizer.this.loggingInterval);
groupCreator.process(new BatchProcessWorker<Map.Entry<String, Set<String>>>()
groupCreator.process(new BaseBatchProcessWorker<Map.Entry<String, Set<String>>>()
{
public String getIdentifier(Map.Entry<String, Set<String>> entry)
{
return entry.getKey() + " " + entry.getValue();
}
public void beforeProcess() throws Throwable
{
// Disable rules
ChainingUserRegistrySynchronizer.this.ruleService.disableRules();
// Authentication
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
}
public void afterProcess() throws Throwable
{
// Enable rules
ChainingUserRegistrySynchronizer.this.ruleService.enableRules();
// Clear authentication
AuthenticationUtil.clearCurrentSecurityContext();
}
public void process(Map.Entry<String, Set<String>> entry) throws Throwable
{
String child = entry.getKey();
@@ -1022,7 +1185,12 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
AuthorityType.getAuthorityType(child), groupShortName, groupDisplayName,
zoneSet);
}
maintainAssociations(child);
else
{
// Maintain association deletions now. The creations will have to be done later once
// we have performed all the deletions in order to avoid creating cycles
maintainAssociationDeletions(child);
}
}
}, splitTxns);
}
@@ -1031,53 +1199,92 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
public void finalizeAssociations(UserRegistry userRegistry, boolean splitTxns)
{
// Remove all the associations we have already dealt with
this.personParentAssocsToCreate.keySet().removeAll(this.personsProcessed);
// Filter out associations to authorities that simply can't exist (and log if debugging is enabled)
logRetainParentAssociations(this.personParentAssocsToCreate, this.allZonePersons);
if (!this.personParentAssocsToCreate.isEmpty())
// First validate the group associations to be created for potential cycles. Remove any offending association
validateGroupParentAssocsToCreate();
// Now go ahead and create the group associations
if (!this.groupParentAssocsToCreate.isEmpty())
{
BatchProcessor<Map.Entry<String, Set<String>>> groupCreator = new BatchProcessor<Map.Entry<String, Set<String>>>(
zone + " Authority Association", ChainingUserRegistrySynchronizer.this.transactionService
.getRetryingTransactionHelper(), this.personParentAssocsToCreate.entrySet(),
zone + " Group Association Creation", ChainingUserRegistrySynchronizer.this.transactionService
.getRetryingTransactionHelper(), this.groupParentAssocsToCreate.entrySet(),
ChainingUserRegistrySynchronizer.this.workerThreads, 20,
ChainingUserRegistrySynchronizer.this.applicationEventPublisher,
ChainingUserRegistrySynchronizer.logger,
ChainingUserRegistrySynchronizer.this.loggingInterval);
groupCreator.process(new BatchProcessWorker<Map.Entry<String, Set<String>>>()
groupCreator.process(new BaseBatchProcessWorker<Map.Entry<String, Set<String>>>()
{
public String getIdentifier(Map.Entry<String, Set<String>> entry)
{
return entry.getKey() + " " + entry.getValue();
}
public void beforeProcess() throws Throwable
public void process(Map.Entry<String, Set<String>> entry) throws Throwable
{
// Disable rules
ChainingUserRegistrySynchronizer.this.ruleService.disableRules();
// Authentication
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
maintainAssociationCreations(entry.getKey());
}
}, splitTxns);
}
public void afterProcess() throws Throwable
// Remove all the associations we have already dealt with
this.personParentAssocsToCreate.keySet().removeAll(this.personsProcessed);
// Filter out associations to authorities that simply can't exist (and log if debugging is enabled)
logRetainParentAssociations(this.personParentAssocsToCreate, this.allZonePersons);
// Update associations to persons not updated themselves
if (!this.personParentAssocsToCreate.isEmpty())
{
BatchProcessor<Map.Entry<String, Set<String>>> groupCreator = new BatchProcessor<Map.Entry<String, Set<String>>>(
zone + " Person Association", ChainingUserRegistrySynchronizer.this.transactionService
.getRetryingTransactionHelper(), this.personParentAssocsToCreate.entrySet(),
ChainingUserRegistrySynchronizer.this.workerThreads, 20,
ChainingUserRegistrySynchronizer.this.applicationEventPublisher,
ChainingUserRegistrySynchronizer.logger,
ChainingUserRegistrySynchronizer.this.loggingInterval);
groupCreator.process(new BaseBatchProcessWorker<Map.Entry<String, Set<String>>>()
{
public String getIdentifier(Map.Entry<String, Set<String>> entry)
{
// Enable rules
ChainingUserRegistrySynchronizer.this.ruleService.enableRules();
// Clear authentication
AuthenticationUtil.clearCurrentSecurityContext();
return entry.getKey() + " " + entry.getValue();
}
public void process(Map.Entry<String, Set<String>> entry) throws Throwable
{
maintainAssociations(entry.getKey());
maintainAssociationDeletions(entry.getKey());
maintainAssociationCreations(entry.getKey());
}
}, splitTxns);
}
}
private void maintainAssociations(String authorityName)
private void maintainAssociationDeletions(String authorityName)
{
boolean isPerson = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER;
Set<String> parentsToDelete = isPerson ? this.personParentAssocsToDelete.get(authorityName)
: this.groupParentAssocsToDelete.get(authorityName);
if (parentsToDelete != null && !parentsToDelete.isEmpty())
{
for (String parent : parentsToDelete)
{
if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled())
{
ChainingUserRegistrySynchronizer.logger
.debug("Removing '"
+ ChainingUserRegistrySynchronizer.this.authorityService
.getShortName(authorityName)
+ "' from group '"
+ ChainingUserRegistrySynchronizer.this.authorityService
.getShortName(parent) + "'");
}
ChainingUserRegistrySynchronizer.this.authorityService.removeAuthority(parent, authorityName);
}
}
}
private void maintainAssociationCreations(String authorityName)
{
boolean isPerson = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER;
Set<String> parents = isPerson ? this.personParentAssocsToCreate.get(authorityName)
@@ -1112,26 +1319,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
throw new ConcurrencyFailureException("Forcing batch retry for invalid node", e);
}
}
Set<String> parentsToDelete = isPerson ? this.personParentAssocsToDelete.get(authorityName)
: this.groupParentAssocsToDelete.get(authorityName);
if (parentsToDelete != null && !parentsToDelete.isEmpty())
{
for (String parent : parentsToDelete)
{
if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled())
{
ChainingUserRegistrySynchronizer.logger
.debug("Removing '"
+ ChainingUserRegistrySynchronizer.this.authorityService
.getShortName(authorityName)
+ "' from group '"
+ ChainingUserRegistrySynchronizer.this.authorityService
.getShortName(parent) + "'");
}
ChainingUserRegistrySynchronizer.this.authorityService.removeAuthority(parent, authorityName);
}
}
// Remember that this person's associations have been maintained
if (isPerson)
{
@@ -1169,7 +1356,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
+ " User Creation and Association", this.transactionService.getRetryingTransactionHelper(),
userRegistry.getPersons(lastModified), this.workerThreads, 10, this.applicationEventPublisher,
ChainingUserRegistrySynchronizer.logger, this.loggingInterval);
class PersonWorker implements BatchProcessWorker<NodeDescription>
class PersonWorker extends BaseBatchProcessWorker<NodeDescription>
{
private long latestTime;
@@ -1188,22 +1375,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
return entry.getSourceId();
}
public void beforeProcess() throws Throwable
{
// Disable rules
ChainingUserRegistrySynchronizer.this.ruleService.disableRules();
// Authentication
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
}
public void afterProcess() throws Throwable
{
// Enable rules
ChainingUserRegistrySynchronizer.this.ruleService.enableRules();
// Clear authentication
AuthenticationUtil.clearCurrentSecurityContext();
}
public void process(NodeDescription person) throws Throwable
{
// Make a mutable copy of the person properties, since they get written back to by person service
@@ -1274,8 +1445,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
}
}
// Maintain associations
groupAnalyzer.maintainAssociations(personName);
// Maintain association deletions and creations in one shot (safe to do this with persons as we can't
// create cycles)
groupAnalyzer.maintainAssociationDeletions(personName);
groupAnalyzer.maintainAssociationCreations(personName);
synchronized (this)
{
@@ -1310,13 +1483,14 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
}
// Delete authorities if we have complete information for the zone
if (allowDeletions)
Set<String> deletionCandidates = groupAnalyzer.getDeletionCandidates();
if (allowDeletions && !deletionCandidates.isEmpty())
{
BatchProcessor<String> authorityDeletionProcessor = new BatchProcessor<String>(
zone + " Authority Deletion", this.transactionService.getRetryingTransactionHelper(), groupAnalyzer
.getDeletionCandidates(), this.workerThreads, 10, this.applicationEventPublisher,
zone + " Authority Deletion", this.transactionService.getRetryingTransactionHelper(),
deletionCandidates, this.workerThreads, 10, this.applicationEventPublisher,
ChainingUserRegistrySynchronizer.logger, this.loggingInterval);
class AuthorityDeleter implements BatchProcessWorker<String>
class AuthorityDeleter extends BaseBatchProcessWorker<String>
{
private int personProcessedCount;
private int groupProcessedCount;
@@ -1336,22 +1510,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
return entry;
}
public void beforeProcess() throws Throwable
{
// Disable rules
ChainingUserRegistrySynchronizer.this.ruleService.disableRules();
// Authentication
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
}
public void afterProcess() throws Throwable
{
// Enable rules
ChainingUserRegistrySynchronizer.this.ruleService.enableRules();
// Clear authentication
AuthenticationUtil.clearCurrentSecurityContext();
}
public void process(String authority) throws Throwable
{
if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER)
@@ -1400,62 +1558,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
}
}
/**
* Visits the last authority in the given list by recursively visiting its parents in associationsOld and then
* adding the authority to associationsNew. Used to sort associationsOld into 'depth-first' order.
*
* @param authorityPath
* The authority to visit, preceeded by all its descendants. Allows detection of cyclic child
* associations.
* @param associationsOld
* the association map to sort
* @param associationsNew
* the association map to add to in depth first order
*/
private void visitGroupAssociations(List<String> authorityPath, Map<String, Set<String>> associationsOld,
Map<String, Set<String>> associationsNew)
{
String authorityName = authorityPath.get(authorityPath.size() - 1);
if (!associationsNew.containsKey(authorityName))
{
Set<String> associations = associationsOld.get(authorityName);
if (!associations.isEmpty())
{
int insertIndex = authorityPath.size();
Iterator<String> i = associations.iterator();
while (i.hasNext())
{
String parentAuthority = i.next();
// Prevent cyclic paths
if (authorityPath.contains(parentAuthority))
{
if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
{
ChainingUserRegistrySynchronizer.logger.warn("Detected cyclic dependencies in group '"
+ ChainingUserRegistrySynchronizer.this.authorityService
.getShortName(parentAuthority) + "'");
}
i.remove();
}
else
{
authorityPath.add(parentAuthority);
visitGroupAssociations(authorityPath, associationsOld, associationsNew);
authorityPath.remove(insertIndex);
}
}
}
// Omit associations from users from this map, as they will be processed separately
if (AuthorityType.getAuthorityType(authorityName) != AuthorityType.USER)
{
associationsNew.put(authorityName, associations);
}
}
}
/**
* Gets the persisted most recent update time for a label and zone.
*
@@ -1566,4 +1668,23 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
protected void onShutdown(ApplicationEvent event)
{
}
protected abstract class BaseBatchProcessWorker <T> implements BatchProcessWorker<T>
{
public final void beforeProcess() throws Throwable
{
// Disable rules
ChainingUserRegistrySynchronizer.this.ruleService.disableRules();
// Authentication
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
}
public final void afterProcess() throws Throwable
{
// Enable rules
ChainingUserRegistrySynchronizer.this.ruleService.enableRules();
// Clear authentication
AuthenticationUtil.clearCurrentSecurityContext();
}
}
}