Merged 5.1.N (5.1.2) to 5.2.N (5.2.1)

128140 cpopa: MNT-14026: LDAP sync fails to update attribute's value deletion
      - Update to null, the node properties for which the corresponding LDAP attribute is missing.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@128144 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ramona Neamtu
2016-06-13 14:39:16 +00:00
parent d4b7650088
commit c4c30abae0
2 changed files with 157 additions and 14 deletions

View File

@@ -25,8 +25,11 @@
*/
package org.alfresco.repo.security.sync;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ActivateableBean;
@@ -35,10 +38,12 @@ import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authentication.ldap.LDAPInitialDirContextFactoryImpl;
import org.alfresco.repo.security.person.PersonServiceImpl;
import org.alfresco.repo.security.sync.ldap.AbstractDirectoryServiceUserAccountStatusInterpreter;
import org.alfresco.repo.security.sync.ldap.LDAPUserAccountStatusInterpreter;
import org.alfresco.repo.security.sync.ldap.LDAPUserRegistry;
import org.alfresco.repo.security.sync.ldap.LDAPUserRegistry.PersonCollection;
import org.alfresco.repo.security.sync.ldap_ad.LDAPADUserAccountStatusInterpreter;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
@@ -52,13 +57,23 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.PropertyMap;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import java.io.Serializable;
import java.util.*;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
/**
* Tests the {@link ChainingUserRegistrySynchronizer} using a simulated {@link UserRegistry}.
@@ -82,7 +97,10 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
private UserRegistrySynchronizer synchronizer;
/** The application context manager. */
private MockApplicationContextManager applicationContextManager;
private MockApplicationContextManager applicationContextManager;
/** The namespace service. */
private NamespaceService namespaceService;
/** The person service. */
private PersonService personService;
@@ -128,7 +146,10 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
this.retryingTransactionHelper = (RetryingTransactionHelper) ChainingUserRegistrySynchronizerTest.context
.getBean("retryingTransactionHelper");
setHomeFolderCreationEager(false); // the normal default if using LDAP
setHomeFolderCreationEager(false); // the normal default if using LDAP
this.namespaceService = (NamespaceService) ChainingUserRegistrySynchronizerTest.context
.getBean("namespaceService");
}
/*
@@ -441,8 +462,9 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
@Override
public Collection<NodeDescription> getPersons(Date modifiedSince)
{
return mockUserRegistry.getPersons(modifiedSince);
{
Collection<NodeDescription> persons = mockUserRegistry.getPersons(modifiedSince);
return !persons.isEmpty() ? persons : super.getPersons(modifiedSince);
}
@Override
@@ -815,7 +837,125 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
{
tearDownTestUsersAndGroups();
}
}
}
/**
* <p>Test that upon a first sync, the missing properties at the AD level are set as 'null' on the Alfresco Person object.</p>
* <p>MNT-14026: LDAP sync fails to update attribute's value deletion.</p>
*/
public void testSyncInexistentProperty() throws Exception
{
try
{
// Execute an LDAP sync where the AD server returns the attributes of a person without a certain property, in this case the 'mail'.
executeMockedLDAPSyncWithoutActiveDirectoryEmailProp();
Map<QName, Serializable> userProperties = this.nodeService.getProperties(this.personService.getPerson("U1"));
assertTrue("User must have the email property even though it's null", userProperties.containsKey(ContentModel.PROP_EMAIL));
assertTrue("User's email must be null on first sync.", userProperties.get(ContentModel.PROP_EMAIL) == null);
}
finally
{
tearDownTestUsersAndGroups();
}
}
/**
* <p>Test that an attribute is also removed on the Alfresco side, when it's removed at the AD level.</p>
* <p>MNT-14026: LDAP sync fails to update attribute's value deletion.</p>
*/
public void testSyncDeletedProperty() throws Exception
{
try
{
// Execute an LDAP sync where the AD server returns the attributes of a person, including the 'mail' property.
executeMockedLDAPSyncWithActiveDirectoryEmailProp();
Map<QName, Serializable> userProperties = this.nodeService.getProperties(this.personService.getPerson("U1"));
assertTrue("User's email must be not null.", userProperties.get(ContentModel.PROP_EMAIL).equals("U1@alfresco.com"));
// Execute an LDAP sync where the AD server returns the attributes
// of a person without the 'mail' property, because it was deleted.
executeMockedLDAPSyncWithoutActiveDirectoryEmailProp();
userProperties = this.nodeService.getProperties(this.personService.getPerson("U1"));
assertTrue("User must have the email property even though it's null", userProperties.containsKey(ContentModel.PROP_EMAIL));
assertTrue("User's email must be null on a 2rd sync, since the email property was removed at the AD level.", userProperties.get(ContentModel.PROP_EMAIL) == null);
}
finally
{
tearDownTestUsersAndGroups();
}
}
private void executeMockedLDAPSyncWithActiveDirectoryEmailProp() throws Exception
{
executeMockedLDAPSync(true);
}
private void executeMockedLDAPSyncWithoutActiveDirectoryEmailProp() throws Exception
{
executeMockedLDAPSync(false);
}
private void executeMockedLDAPSync(boolean withEmail) throws NamingException, Exception
{
MockUserRegistry mockUserRegistry = new MockUserRegistry("Z0", new NodeDescription[] {}, new NodeDescription[] {});
MockLDAPUserRegistry mockLDAPUserRegistry = new MockLDAPUserRegistry(mockUserRegistry);
LDAPInitialDirContextFactoryImpl mockedLdapInitialDirContextFactory = getMockedLDAPSearchResult(withEmail);
mockLDAPUserRegistry.setLDAPInitialDirContextFactory(mockedLdapInitialDirContextFactory);
mockLDAPUserRegistry.setEnableProgressEstimation(false);
mockLDAPUserRegistry.setUserIdAttributeName("sAMAccountName");
Map<String, String> personAttributeMapping = getMockedLdapAttributeMapping();
mockLDAPUserRegistry.setPersonAttributeMapping(personAttributeMapping);
mockLDAPUserRegistry.setNamespaceService(this.namespaceService);
mockLDAPUserRegistry.afterPropertiesSet();
this.applicationContextManager.setUserRegistries(mockLDAPUserRegistry);
ChainingUserRegistrySynchronizer chainingSynchronizer = (ChainingUserRegistrySynchronizer) this.synchronizer;
chainingSynchronizer.synchronize(false, false);
}
private LDAPInitialDirContextFactoryImpl getMockedLDAPSearchResult(boolean withEmail) throws NamingException
{
@SuppressWarnings("unchecked")
NamingEnumeration<SearchResult> mockedNamingEnumeration = mock(NamingEnumeration.class);
when(mockedNamingEnumeration.hasMore()).thenReturn(true).thenReturn(false);
BasicAttributes attributes = new BasicAttributes();
attributes.put(new BasicAttribute("sAMAccountName", "U1"));
attributes.put(new BasicAttribute("givenName", "U1"));
if (withEmail)
{
attributes.put(new BasicAttribute("mail", "U1@alfresco.com"));
}
SearchResult mockedSearchResult = new SearchResult("CN:U1", null, attributes);
mockedSearchResult.setNameInNamespace("CN:U1");
when(mockedNamingEnumeration.next()).thenReturn(mockedSearchResult);
InitialDirContext mockedInitialDirContext = mock(InitialDirContext.class);
when(mockedInitialDirContext.search(any(String.class), any(String.class), any(SearchControls.class))).thenReturn(mockedNamingEnumeration);
LDAPInitialDirContextFactoryImpl mockedLdapInitialDirContextFactory = mock(LDAPInitialDirContextFactoryImpl.class);
when(mockedLdapInitialDirContextFactory.getDefaultIntialDirContext(0)).thenReturn(mockedInitialDirContext);
return mockedLdapInitialDirContextFactory;
}
private Map<String, String> getMockedLdapAttributeMapping()
{
Map<String, String> personAttributeMapping = new HashMap<>();
personAttributeMapping.put("cm:userName", "sAMAccountName");
personAttributeMapping.put("cm:firstName", "givenName");
personAttributeMapping.put("cm:email", "mail");
return personAttributeMapping;
}
/**
* Tests synchronization of group associations in a zone with a larger volume of authorities.