mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-15 15:02:20 +00:00
Merged 5.2.N (5.2.1) to HEAD (5.2)
125792 rmunteanu: Merged 5.1.N (5.1.2) to 5.2.N (5.2.1) 125621 rmunteanu: Merged 5.0.N (5.0.4) to 5.1.N (5.1.2) 125577 abalmus: MNT-15038 : Unexpected behavior when disabling Active Directory user (New feature to sync userAccountControl) - Implemented new requirements and tests git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127813 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -93,6 +93,9 @@
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
<property name="personService">
|
||||
<ref bean="PersonService" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!--
|
||||
@@ -365,6 +368,13 @@
|
||||
<value>${ldap.synchronization.timestampFormat}</value>
|
||||
</property>
|
||||
|
||||
<!--
|
||||
The user account status property interpreter. Unfortunately, this varies between directory servers.
|
||||
-->
|
||||
<property name="userAccountStatusInterpreter">
|
||||
<ref bean="${ldap.synchronization.userAccountStatusInterpreter}" />
|
||||
</property>
|
||||
|
||||
<!--
|
||||
An attribute that is a unique identifier for each group found.
|
||||
This is also the name of the group with the current group implementation.
|
||||
@@ -451,6 +461,12 @@
|
||||
<entry key="cm:homeFolderProvider">
|
||||
<null/>
|
||||
</entry>
|
||||
<entry key="cm:userAccountStatusProperty">
|
||||
<!-- Oracle / Red Hat / 389 DS: "nsAccountLock"-->
|
||||
<!-- OpenLDAP: "pwdAccountLockedTime" -->
|
||||
<!-- Active Directory: "userAccountControl" -->
|
||||
<value>${ldap.synchronization.userAccountStatusProperty}</value>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<!-- Set a default home folder provider -->
|
||||
|
@@ -7,4 +7,8 @@
|
||||
defaults
|
||||
-->
|
||||
<import resource="../common-ldap-context.xml" />
|
||||
|
||||
<!-- LDAP-AD User Account Status Interpreter -->
|
||||
<bean id="ldapadUserAccountStatusInterpreter" class="org.alfresco.repo.security.sync.ldap_ad.LDAPADUserAccountStatusInterpreter">
|
||||
</bean>
|
||||
</beans>
|
@@ -165,4 +165,10 @@ ldap.pooling.com.sun.jndi.ldap.connect.pool.protocol=plain
|
||||
ldap.pooling.com.sun.jndi.ldap.connect.pool.timeout=
|
||||
|
||||
# The string representation of an integer that represents the number of milliseconds to specify how long to wait for a pooled connection. If you omit this property, the application will wait indefinitely.
|
||||
ldap.pooling.com.sun.jndi.ldap.connect.timeout=
|
||||
ldap.pooling.com.sun.jndi.ldap.connect.timeout=
|
||||
|
||||
# LDAP-AD property name for user enabled/disabled status
|
||||
ldap.synchronization.userAccountStatusProperty=userAccountControl
|
||||
|
||||
# The Account Status Interpreter bean name
|
||||
ldap.synchronization.userAccountStatusInterpreter=ldapadUserAccountStatusInterpreter
|
||||
|
@@ -7,4 +7,14 @@
|
||||
defaults
|
||||
-->
|
||||
<import resource="../common-ldap-context.xml" />
|
||||
|
||||
<!-- LDAP User Account Status Interpreter -->
|
||||
<bean id="ldapUserAccountStatusInterpreter" class="org.alfresco.repo.security.sync.ldap.LDAPUserAccountStatusInterpreter">
|
||||
<property name="disabledAccountPropertyValue">
|
||||
<value>${ldap.synchronization.disabledAccountPropertyValue}</value>
|
||||
</property>
|
||||
<property name="acceptNullArgument">
|
||||
<value>${ldap.synchronization.disabledAccountPropertyValueCanBeNull}</value>
|
||||
</property>
|
||||
</bean>
|
||||
</beans>
|
@@ -172,4 +172,20 @@ ldap.pooling.com.sun.jndi.ldap.connect.pool.timeout=
|
||||
|
||||
# The string representation of an integer that represents the number of milliseconds to specify how long to wait for a pooled connection.
|
||||
# Empty value means the application will wait indefinitely.
|
||||
ldap.pooling.com.sun.jndi.ldap.connect.timeout=
|
||||
ldap.pooling.com.sun.jndi.ldap.connect.timeout=
|
||||
|
||||
# Enabled/disabled status - there is no standard way to check for this;
|
||||
# "nsAccountLock" is used by most NDS derived directory systems (Oracle / Red Hat / 389 DS);
|
||||
# For OpenLDAP you may want to specify "pwdAccountLockedTime" instead
|
||||
ldap.synchronization.userAccountStatusProperty=nsAccountLock
|
||||
|
||||
# Expected value for disabled account;
|
||||
# For NDS directory servers: nsAccountLock=true
|
||||
# For OpenLDAP: pwdAccountLockedTime=000001010000Z
|
||||
ldap.synchronization.disabledAccountPropertyValue=true
|
||||
|
||||
# Some directory servers may not send a status value at all if account is enabled
|
||||
ldap.synchronization.disabledAccountPropertyValueCanBeNull=true
|
||||
|
||||
# The Account Status Interpreter bean name
|
||||
ldap.synchronization.userAccountStatusInterpreter=ldapUserAccountStatusInterpreter
|
||||
|
@@ -84,6 +84,12 @@
|
||||
<value>${synchronization.syncDelete}</value>
|
||||
</property>
|
||||
<property name="nameChecker" ref="nameChecker" />
|
||||
<property name="externalUserControl">
|
||||
<value>${synchronization.externalUserControl}</value>
|
||||
</property>
|
||||
<property name="externalUserControlSubsystemName">
|
||||
<value>${synchronization.externalUserControlSubsystemName}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
|
@@ -33,4 +33,10 @@ synchronization.workerThreads=1
|
||||
synchronization.allowDeletions=true
|
||||
|
||||
# For large LDAP directories the delete query is expensive and time consuming, needing to read the entire LDAP directory.
|
||||
synchronization.syncDelete=true
|
||||
synchronization.syncDelete=true
|
||||
|
||||
# external setting (LDAP systems) - whether users can be enabled; if false then users have to be explicitly disabled in Alfresco
|
||||
synchronization.externalUserControl=false
|
||||
|
||||
# Subsystem that will handle the external user control
|
||||
synchronization.externalUserControlSubsystemName=
|
||||
|
@@ -31,6 +31,7 @@ import java.util.Set;
|
||||
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationComponent.UserNameValidationMode;
|
||||
import org.alfresco.repo.tenant.TenantContextHolder;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.util.Pair;
|
||||
|
||||
public class AuthenticationServiceImpl extends AbstractAuthenticationService implements ActivateableBean
|
||||
@@ -42,7 +43,14 @@ public class AuthenticationServiceImpl extends AbstractAuthenticationService imp
|
||||
private boolean allowsUserCreation = true;
|
||||
private boolean allowsUserDeletion = true;
|
||||
private boolean allowsUserPasswordChange = true;
|
||||
|
||||
|
||||
private PersonService personService;
|
||||
|
||||
public void setPersonService(PersonService personService)
|
||||
{
|
||||
this.personService = personService;
|
||||
}
|
||||
|
||||
public AuthenticationServiceImpl()
|
||||
{
|
||||
super();
|
||||
@@ -336,6 +344,11 @@ public class AuthenticationServiceImpl extends AbstractAuthenticationService imp
|
||||
*/
|
||||
public boolean getAuthenticationEnabled(String userName) throws AuthenticationException
|
||||
{
|
||||
if (personService.personExists(userName))
|
||||
{
|
||||
return personService.isEnabled(userName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -848,6 +848,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
properties.put(ContentModel.PROP_USERNAME, realUserName);
|
||||
}
|
||||
}
|
||||
|
||||
checkIfPersonShouldBeDisabledAndSetAspect(personNode, properties);
|
||||
|
||||
Map<QName, Serializable> update = nodeService.getProperties(personNode);
|
||||
update.putAll(properties);
|
||||
|
||||
@@ -987,6 +990,8 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
beforeCreateNodeValidationBehaviour.enable();
|
||||
}
|
||||
|
||||
checkIfPersonShouldBeDisabledAndSetAspect(personRef, properties);
|
||||
|
||||
if (zones != null)
|
||||
{
|
||||
for (String zone : zones)
|
||||
@@ -1004,6 +1009,26 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
return personRef;
|
||||
}
|
||||
|
||||
private void checkIfPersonShouldBeDisabledAndSetAspect(NodeRef person, Map<QName, Serializable> properties)
|
||||
{
|
||||
if (properties.get(ContentModel.PROP_ENABLED) != null)
|
||||
{
|
||||
boolean isEnabled = Boolean.parseBoolean(properties.get(ContentModel.PROP_ENABLED).toString());
|
||||
|
||||
if (isEnabled)
|
||||
{
|
||||
if (nodeService.hasAspect(person, ContentModel.ASPECT_PERSON_DISABLED))
|
||||
{
|
||||
nodeService.removeAspect(person, ContentModel.ASPECT_PERSON_DISABLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeService.addAspect(person, ContentModel.ASPECT_PERSON_DISABLED, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@@ -39,6 +39,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.security.authentication.AuthenticatorDeletedEvent;
|
||||
import org.alfresco.repo.security.authority.UnknownAuthorityException;
|
||||
import org.alfresco.repo.security.sync.ldap.LDAPUserRegistry;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
@@ -65,6 +66,7 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
import org.springframework.extensions.surf.util.I18NUtil;
|
||||
|
||||
import javax.management.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -195,6 +197,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
|
||||
private NameChecker nameChecker;
|
||||
|
||||
private SysAdminParams sysAdminParams;
|
||||
|
||||
private String externalUserControl = "";
|
||||
|
||||
private String externalUserControlSubsystemName = "";
|
||||
|
||||
public void init()
|
||||
{
|
||||
@@ -208,6 +214,16 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
|
||||
PropertyCheck.mandatory(this, "sysAdminParams", sysAdminParams);
|
||||
}
|
||||
|
||||
public void setExternalUserControl(String externalUserControl)
|
||||
{
|
||||
this.externalUserControl = externalUserControl;
|
||||
}
|
||||
|
||||
public void setExternalUserControlSubsystemName(String externalUserControlSubsystemName)
|
||||
{
|
||||
this.externalUserControlSubsystemName = externalUserControlSubsystemName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets name checker
|
||||
*/
|
||||
@@ -1765,6 +1781,9 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
|
||||
this.applicationEventPublisher,
|
||||
ChainingUserRegistrySynchronizer.logger,
|
||||
this.loggingInterval);
|
||||
|
||||
final UserRegistry userRegistryFinalRef = userRegistry;
|
||||
|
||||
class PersonWorker extends BaseBatchProcessWorker<NodeDescription>
|
||||
{
|
||||
private long latestTime;
|
||||
@@ -1790,6 +1809,36 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
|
||||
HashMap<QName, Serializable> personProperties = new HashMap<QName, Serializable>(person.getProperties());
|
||||
String personName = personProperties.get(ContentModel.PROP_USERNAME).toString().trim();
|
||||
personProperties.put(ContentModel.PROP_USERNAME, personName);
|
||||
|
||||
if (Boolean.parseBoolean(ChainingUserRegistrySynchronizer.this.externalUserControl)
|
||||
&& ChainingUserRegistrySynchronizer.this.externalUserControlSubsystemName.equals(zone)
|
||||
&& userRegistryFinalRef instanceof LDAPUserRegistry)
|
||||
{
|
||||
try
|
||||
{
|
||||
LDAPUserRegistry ldapUserRegistry = (LDAPUserRegistry) userRegistryFinalRef;
|
||||
|
||||
if (ldapUserRegistry.getUserAccountStatusInterpreter() != null)
|
||||
{
|
||||
QName propertyNameToCheck = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userAccountStatusProperty");
|
||||
|
||||
if (personProperties.get(propertyNameToCheck) != null || ldapUserRegistry.getUserAccountStatusInterpreter().acceptsNullArgument())
|
||||
{
|
||||
boolean isUserAccountDisabled = ldapUserRegistry.getUserAccountStatusInterpreter().isUserAccountDisabled(
|
||||
personProperties.get(propertyNameToCheck));
|
||||
|
||||
personProperties.put(ContentModel.PROP_ENABLED, !isUserAccountDisabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException iae)
|
||||
{
|
||||
// Can be thrown by certain implementations of AbstractDirectoryServiceUserAccountStatusInterpreter;
|
||||
// We'll just log it.
|
||||
ChainingUserRegistrySynchronizer.logger.debug(iae.getMessage(), iae);
|
||||
}
|
||||
}
|
||||
|
||||
// for invalid names will throw ConstraintException that will be catched by BatchProcessor$TxnCallback
|
||||
nameChecker.evaluate(personName);
|
||||
Set<String> zones = ChainingUserRegistrySynchronizer.this.authorityService
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2016 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.security.sync.ldap;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class AbstractDirectoryServiceUserAccountStatusInterpreter
|
||||
{
|
||||
public static final String USER_ACCOUNT_STATUS_NOT_NULL_MESSAGE = "User account status property value must not be null.";
|
||||
|
||||
protected void checkForNullArgument(Serializable arg)
|
||||
{
|
||||
if (arg == null)
|
||||
{
|
||||
throw new IllegalArgumentException(USER_ACCOUNT_STATUS_NOT_NULL_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if directory server user account status is disabled.
|
||||
*
|
||||
* @param userAccountStatusValue
|
||||
* value to interpret user account status from;
|
||||
*
|
||||
* @return true if interpreted as disabled, false otherwise
|
||||
*/
|
||||
public abstract boolean isUserAccountDisabled(Serializable userAccountStatusValue) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Specify if the particular implementation of
|
||||
* {@link AbstractDirectoryServiceUserAccountStatusInterpreter#isUserAccountDisabled(Serializable)}
|
||||
* will accept null.
|
||||
*
|
||||
* @return true if accepts null.
|
||||
*/
|
||||
public abstract boolean acceptsNullArgument();
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2016 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.security.sync.ldap;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class LDAPUserAccountStatusInterpreter extends AbstractDirectoryServiceUserAccountStatusInterpreter
|
||||
{
|
||||
private String disabledAccountPropertyValue = "";
|
||||
private boolean acceptNullArgument;
|
||||
|
||||
public void setDisabledAccountPropertyValue(String disabledAccountPropertyValue)
|
||||
{
|
||||
this.disabledAccountPropertyValue = disabledAccountPropertyValue;
|
||||
}
|
||||
|
||||
public void setAcceptNullArgument(boolean acceptNullArgument)
|
||||
{
|
||||
this.acceptNullArgument = acceptNullArgument;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserAccountDisabled(Serializable userAccountStatus)
|
||||
{
|
||||
if (!acceptsNullArgument())
|
||||
{
|
||||
checkForNullArgument(userAccountStatus);
|
||||
}
|
||||
|
||||
return userAccountStatus != null && userAccountStatus.toString().equalsIgnoreCase(disabledAccountPropertyValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsNullArgument()
|
||||
{
|
||||
return acceptNullArgument;
|
||||
}
|
||||
}
|
@@ -191,6 +191,9 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial
|
||||
/** The LDAP generalized time format. */
|
||||
private DateFormat timestampFormat;
|
||||
|
||||
/** The LDAP User Account Status Property Interpreter */
|
||||
private AbstractDirectoryServiceUserAccountStatusInterpreter userAccountStatusInterpreter;
|
||||
|
||||
/**
|
||||
* Instantiates a new lDAP user registry.
|
||||
*/
|
||||
@@ -505,6 +508,16 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial
|
||||
this.attributeBatchSize = attributeBatchSize;
|
||||
}
|
||||
|
||||
public void setUserAccountStatusInterpreter(AbstractDirectoryServiceUserAccountStatusInterpreter userAccountStatusInterpreter)
|
||||
{
|
||||
this.userAccountStatusInterpreter = userAccountStatusInterpreter;
|
||||
}
|
||||
|
||||
public AbstractDirectoryServiceUserAccountStatusInterpreter getUserAccountStatusInterpreter()
|
||||
{
|
||||
return userAccountStatusInterpreter;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.repo.management.subsystems.ActivateableBean#isActive()
|
||||
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2016 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.security.sync.ldap_ad;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.alfresco.repo.security.sync.ldap.AbstractDirectoryServiceUserAccountStatusInterpreter;
|
||||
|
||||
public class LDAPADUserAccountStatusInterpreter extends AbstractDirectoryServiceUserAccountStatusInterpreter
|
||||
{
|
||||
@Override
|
||||
public boolean isUserAccountDisabled(Serializable userAccountStatusValue)
|
||||
{
|
||||
checkForNullArgument(userAccountStatusValue);
|
||||
|
||||
/*
|
||||
* References:
|
||||
* https://blogs.technet.microsoft.com/heyscriptingguy/2005/05/12/how-can-i-get-a-list-of-all-the-disabled-user-accounts-in-active-directory
|
||||
* http://stackoverflow.com/questions/19250969/include-enabled-disabled-account-status-of-ldap-user-in-results/19252033#19252033
|
||||
*/
|
||||
return ((Integer.parseInt(userAccountStatusValue.toString())) & 2) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsNullArgument()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -36,6 +36,10 @@ 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.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_ad.LDAPADUserAccountStatusInterpreter;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -44,6 +48,7 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.security.AuthorityService;
|
||||
import org.alfresco.service.cmr.security.AuthorityType;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.alfresco.util.PropertyMap;
|
||||
@@ -407,6 +412,135 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
tearDownTestUsersAndGroups();
|
||||
}
|
||||
|
||||
private class MockLDAPUserRegistry extends LDAPUserRegistry implements IMockUserRegistry
|
||||
{
|
||||
MockUserRegistry mockUserRegistry;
|
||||
|
||||
public MockLDAPUserRegistry(MockUserRegistry mockUserRegistry)
|
||||
{
|
||||
this.mockUserRegistry = mockUserRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActive(boolean active)
|
||||
{
|
||||
mockUserRegistry.setActive(active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive()
|
||||
{
|
||||
return mockUserRegistry.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<QName> getPersonMappedProperties()
|
||||
{
|
||||
return mockUserRegistry.getPersonMappedProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<NodeDescription> getPersons(Date modifiedSince)
|
||||
{
|
||||
return mockUserRegistry.getPersons(modifiedSince);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPersonNames()
|
||||
{
|
||||
return mockUserRegistry.getPersonNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getGroupNames()
|
||||
{
|
||||
return mockUserRegistry.getGroupNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<NodeDescription> getGroups(Date modifiedSince)
|
||||
{
|
||||
return mockUserRegistry.getGroups(modifiedSince);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getZoneId()
|
||||
{
|
||||
return mockUserRegistry.getZoneId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Collection<NodeDescription> persons, Collection<NodeDescription> groups)
|
||||
{
|
||||
mockUserRegistry.updateState(persons, groups);
|
||||
}
|
||||
}
|
||||
|
||||
private void testLDAPDisableUserAccount(AbstractDirectoryServiceUserAccountStatusInterpreter userAccountStatusInterpreter,
|
||||
String enabledAccountPropertyValue, String disabledAccountPropertyValue) throws Exception
|
||||
{
|
||||
MockUserRegistry mockUserRegistry = new MockUserRegistry("ldap1", new NodeDescription[] {
|
||||
newPersonWithUserAccountStatusProperty("EnabledUser", enabledAccountPropertyValue),
|
||||
newPersonWithUserAccountStatusProperty("DisabledUser", disabledAccountPropertyValue) }, new NodeDescription[] {});
|
||||
|
||||
MockLDAPUserRegistry mockLDAPUserRegistry = new MockLDAPUserRegistry(mockUserRegistry);
|
||||
mockLDAPUserRegistry.setUserAccountStatusInterpreter(userAccountStatusInterpreter);
|
||||
|
||||
this.applicationContextManager.setUserRegistries(mockLDAPUserRegistry);
|
||||
|
||||
ChainingUserRegistrySynchronizer chainingSynchronizer = (ChainingUserRegistrySynchronizer) this.synchronizer;
|
||||
chainingSynchronizer.setExternalUserControl("true");
|
||||
chainingSynchronizer.setExternalUserControlSubsystemName("ldap1");
|
||||
|
||||
this.synchronizer.synchronize(false, false);
|
||||
|
||||
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
NodeRef enabledUserRef = ChainingUserRegistrySynchronizerTest.this.personService.getPerson("EnabledUser", false);
|
||||
assertFalse(ChainingUserRegistrySynchronizerTest.this.nodeService.hasAspect(enabledUserRef, ContentModel.ASPECT_PERSON_DISABLED));
|
||||
|
||||
NodeRef disabledUserRef = ChainingUserRegistrySynchronizerTest.this.personService.getPerson("DisabledUser", false);
|
||||
assertTrue(ChainingUserRegistrySynchronizerTest.this.nodeService.hasAspect(disabledUserRef, ContentModel.ASPECT_PERSON_DISABLED));
|
||||
|
||||
return null;
|
||||
}
|
||||
}, false, true);
|
||||
}
|
||||
|
||||
public void testLDAPDisableUserAccountWithActiveDirectoryProperty() throws Exception
|
||||
{
|
||||
LDAPADUserAccountStatusInterpreter ldapadUserAccountStatusInterpreter = new LDAPADUserAccountStatusInterpreter();
|
||||
|
||||
// Active Directory enabled account: userAccountControl=512;
|
||||
// disabled account: userAccountControl=514.
|
||||
testLDAPDisableUserAccount(ldapadUserAccountStatusInterpreter, "512", "514");
|
||||
}
|
||||
|
||||
public void testLDAPDisableUserAccountWithNetscapDSProperty() throws Exception
|
||||
{
|
||||
LDAPUserAccountStatusInterpreter ldapUserAccountStatusInterpreter = new LDAPUserAccountStatusInterpreter();
|
||||
ldapUserAccountStatusInterpreter.setAcceptNullArgument(true);
|
||||
|
||||
// Netscape Directory Server derivatives (Oracle, Red Hat, 389 DS)
|
||||
// disabled account property: nsAccountLock=true.
|
||||
ldapUserAccountStatusInterpreter.setDisabledAccountPropertyValue("true");
|
||||
|
||||
testLDAPDisableUserAccount(ldapUserAccountStatusInterpreter, null, "true");
|
||||
}
|
||||
|
||||
public void testLDAPDisableUserAccountWithOpenLDAPProperty() throws Exception
|
||||
{
|
||||
LDAPUserAccountStatusInterpreter ldapUserAccountStatusInterpreter = new LDAPUserAccountStatusInterpreter();
|
||||
ldapUserAccountStatusInterpreter.setAcceptNullArgument(true);
|
||||
|
||||
// OpenLDAP disabled account: pwdAccountLockedTime=000001010000Z (part of PPolicy module)
|
||||
ldapUserAccountStatusInterpreter.setDisabledAccountPropertyValue("000001010000Z");
|
||||
|
||||
testLDAPDisableUserAccount(ldapUserAccountStatusInterpreter, null, "000001010000Z");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a forced update of the test users and groups with deletions disabled. No users or groups should be deleted,
|
||||
* whether or not they move registry. Groups that would have been deleted should have no members and should only be
|
||||
@@ -785,6 +919,16 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
return person;
|
||||
}
|
||||
|
||||
private NodeDescription newPersonWithUserAccountStatusProperty(String userName, String userAccountPropertyValue)
|
||||
{
|
||||
NodeDescription person = newPerson(userName, userName + "@somedomain.com");
|
||||
|
||||
person.getProperties().put(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userAccountStatusProperty"), userAccountPropertyValue);
|
||||
person.setLastModified(new Date());
|
||||
|
||||
return person;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform all the necessary assertions to ensure that an authority and its members exist in the correct zone.
|
||||
*
|
||||
@@ -936,10 +1080,31 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
public static interface IMockUserRegistry extends UserRegistry, ActivateableBean
|
||||
{
|
||||
/**
|
||||
* Gets the zone id.
|
||||
*
|
||||
* @return the zoneId
|
||||
*/
|
||||
String getZoneId();
|
||||
|
||||
/**
|
||||
* Modifies the state to match the arguments. Compares new with old and
|
||||
* records new modification dates only for changes.
|
||||
*
|
||||
* @param persons
|
||||
* the persons
|
||||
* @param groups
|
||||
* the groups
|
||||
*/
|
||||
void updateState(Collection<NodeDescription> persons, Collection<NodeDescription> groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mock {@link UserRegistry} that returns a fixed set of users and groups.
|
||||
*/
|
||||
public static class MockUserRegistry implements UserRegistry, ActivateableBean
|
||||
public static class MockUserRegistry implements IMockUserRegistry
|
||||
{
|
||||
private boolean isActive = true;
|
||||
|
||||
@@ -973,15 +1138,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Modifies the state to match the arguments. Compares new with old and records new modification dates only for
|
||||
* changes.
|
||||
*
|
||||
* @param persons
|
||||
* the persons
|
||||
* @param groups
|
||||
* the groups
|
||||
*/
|
||||
@Override
|
||||
public void updateState(Collection<NodeDescription> persons, Collection<NodeDescription> groups)
|
||||
{
|
||||
List<NodeDescription> newPersons = new ArrayList<NodeDescription>(this.persons);
|
||||
@@ -1066,11 +1223,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
this(zoneId, Arrays.asList(persons), Arrays.asList(groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zone id.
|
||||
*
|
||||
* @return the zoneId
|
||||
*/
|
||||
@Override
|
||||
public String getZoneId()
|
||||
{
|
||||
return this.zoneId;
|
||||
@@ -1195,10 +1348,10 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
* @param registries
|
||||
* the new user registries
|
||||
*/
|
||||
public void setUserRegistries(MockUserRegistry... registries)
|
||||
public void setUserRegistries(IMockUserRegistry... registries)
|
||||
{
|
||||
this.contexts = new LinkedHashMap<String, ApplicationContext>(registries.length * 2);
|
||||
for (MockUserRegistry registry : registries)
|
||||
for (IMockUserRegistry registry : registries)
|
||||
{
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.getDefaultListableBeanFactory().registerSingleton("userRegistry", registry);
|
||||
@@ -1230,7 +1383,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
public void updateZone(String zoneId, NodeDescription[] persons, NodeDescription[] groups)
|
||||
{
|
||||
ApplicationContext context = this.contexts.get(zoneId);
|
||||
MockUserRegistry registry = (MockUserRegistry) context.getBean("userRegistry");
|
||||
IMockUserRegistry registry = (IMockUserRegistry) context.getBean("userRegistry");
|
||||
registry.updateState(Arrays.asList(persons), Arrays.asList(groups));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user