LDAP-CIFS on Alfresco Enterprise v3.0.0

cancel
Showing results for 
Search instead for 
Did you mean: 

LDAP-CIFS on Alfresco Enterprise v3.0.0

resplin
Intermediate
0 0 1,756

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



- Instructions, as I can recall them.
- This works with Enterprise 3.0 (I will post eventual changes for 3.1 once we have done the migration)

- Last change: April 24, 2009




Here are instructions on how we have setup Alfresco to authenticate against LDAP. This is using Alfresco v.3.0.0, OpenLDAP on a Linux/Debian Etch platform.


Create your LDAP tree


To keep things simple we only enabled simple authentication. This requires having all users under a single location in the LDAP tree, for example 'uid=user.name,ou=people,dc=company,dc=com'.
You will need to use PLAIN TEXT passwords. NOTE: this is not an issue in our case, as the LDAP and Alfresco servers are located in the same network cluster. Otherwise, you will need to secure the transfer with a tunnel, as password will be sent in clear over the wire.
Make sure you know the username and password of the 'manager' account. It is used by Alfresco to access the LDAP tree, and the credential have to be placed in the config files.


Create/edit /opt/alfresco/tomcat/shared/classes/alfresco/extension/ldap-synchronisation-context.xml


Just renaming the default ldap-synchronisation-context.xml.sample is probably ok, but check to see if anything particular to your config is required. In our case, no changes were required.


Create/edit /opt/alfresco/tomcat/shared/classes/alfresco/extension/ldap-synchronisation.properties


Enter the specifics for your installation, based on the way your LDAP tree is structured. In our case:



#
# This properties file is used to configure LDAP syncronisation
#

# The query to find the people to import
ldap.synchronisation.personQuery=(objectclass=companyPerson)

# The search base of the query to find people to import
ldap.synchronisation.personSearchBase=ou=people,dc=company,dc=com

# The attribute name on people objects found in LDAP to use as the uid in Alfresco
ldap.synchronisation.userIdAttributeName=uid

# The attribute on person objects in LDAP to map to the first name property in Alfresco
ldap.synchronisation.userFirstNameAttributeName=givenName

# The attribute on person objects in LDAP to map to the last name property in Alfresco
ldap.synchronisation.userLastNameAttributeName=sn

# The attribute on person objects in LDAP to map to the email property in Alfresco
ldap.synchronisation.userEmailAttributeName=mail

# The attribute on person objects in LDAP to map to the organizational id  property in Alfresco
ldap.synchronisation.userOrganizationalIdAttributeName=o

# The default home folder provider to use for people created via LDAP import
ldap.synchronisation.defaultHomeFolderProvider=userHomesHomeFolderProvider

# The query to find group objects
ldap.synchronisation.groupQuery=(objectclass=groupOfNames)

# The search base to use to find group objects
ldap.synchronisation.groupSearchBase=ou=groups,dc=company,dc=com

# The attribute on LDAP group objects to map to the gid property in Alfresco
ldap.synchronisation.groupIdAttributeName=cn

# The group type in LDAP
ldap.synchronisation.groupType=groupOfNames

# The person type in LDAP
ldap.synchronisation.personType=companyPerson

# The attribute in LDAP on group objects that defines the DN for its members
ldap.synchronisation.groupMemberAttributeName=member

# The cron expression defining when people imports should take place
ldap.synchronisation.import.person.cron=0 0 * * * ? 2090
#ldap.synchronisation.import.person.cron=0 0/10 * * * ?

# The cron expression defining when group imports should take place
ldap.synchronisation.import.group.cron=0 30 * * * ? 2090
#ldap.synchronisation.import.group.cron=0 0/10 * * * ?

# Should all groups be cleared out at import time?
# - this is safe as groups are not used in Alfresco for other things (unlike person objects which yo
u should never clear out during an import)
# - setting this to true means old group definitions will be tidied up.
ldap.synchronisation.import.group.clearAllChildren=true


Note: In our setup, we are now disabling automatic sync via cron, because we are doing it via a custom action instead.


Create/edit /opt/alfresco/tomcat/shared/classes/alfresco/extension/ldap-authentication-context.xml


Copy the sample file. This is where it is becoming interesting. Instead of using the 'standard' authentication bean, we instruct Alfresco to use another, custom bean to provide MD4 encryption of the password to be compatible with CIFS.



<beans>
   

   
    <bean name='ldapAuthenticationPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'>
        <property name='ignoreUnresolvablePlaceholders'>
            <value>true</value>
        </property> 
        <property name='locations'>
            <value>classpath:alfresco/extension/ldap-authentication.properties</value>
        </property>
    </bean>
   
   
    <bean name='authenticationDao' class='org.alfresco.repo.security.authentication.DefaultMutableAuthenticationDao' >
        <property name='allowDeleteUser'>
            <value>true</value>
        </property>
    </bean>   

    <bean id='authenticationDaoFull' class='com.company.repo.security.authentication.RepositoryAuthenticationDao' >
       <property name='nodeService'>
           <ref bean='nodeService' />
       </property>
       <property name='dictionaryService'>
           <ref bean='dictionaryService' />
       </property>
       <property name='namespaceService'>
           <ref bean='namespaceService' />
       </property>
       <property name='searchService'>
           <ref bean='admSearchService' />
       </property>
       <property name='userNamesAreCaseSensitive'>
          <value>${user.name.caseSensitive}</value>
       </property>
       <property name='passwordEncoder'>
           <ref bean='passwordEncoder' />
       </property>
    </bean>
  

   
   
    <bean id='authenticationComponent'>
         class='com.company.repo.security.authentication.ldap.LDAPAuthenticationComponentImpl'
          parent='authenticationComponentBase'>
        <property name='LDAPInitialDirContextFactory'>
            <ref bean='ldapInitialDirContextFactory'/>
        </property>
        <property name='userNameFormat'>
            <value>${ldap.authentication.userNameFormat}</value>
        </property>
<property name='allowGuestLogin'>
     <value>false</value>
        </property>

        <property name='nodeService'>
            <ref bean='nodeService' />
        </property>
        <property name='personService'>
            <ref bean='personService' />
        </property>
        <property name='transactionService'>
            <ref bean='transactionService' />
        </property>

<property name='authenticationDao'>
            <ref bean='authenticationDaoFull' />
        </property> 
    </bean>
   
   
    <bean id='ldapInitialDirContextFactory' class='org.alfresco.repo.security.authentication.ldap.LDAPInitialDirContextFactoryImpl'>
        <property name='initialDirContextEnvironment'>
            <map>
                <entry key='java.naming.factory.initial'>
                    <value>${ldap.authentication.java.naming.factory.initial}</value>
                </entry>
               


                <entry key='java.naming.provider.url'>
                    <value>${ldap.authentication.java.naming.provider.url}</value>
                </entry>
               



                <entry key='java.naming.security.authentication'>
                    <value>${ldap.authentication.java.naming.security.authentication}</value>
                </entry>
               

                <entry key='java.naming.security.principal'>
                    <value>${ldap.authentication.java.naming.security.principal}</value>
                </entry>
               
                <entry key='java.naming.security.credentials'>
                    <value>${ldap.authentication.java.naming.security.credentials}</value>
                </entry>
            </map>
        </property>
    </bean>
   
</beans>

Create/edit /opt/alfresco/tomcat/shared/classes/alfresco/extension/ldap-authentication.properties


This defines where to find the LDAP server, which username to connect as, etc...



#
# This properties file brings together the common options for LDAP authentication rather than editing the bean definitions
#

# How to map the user id entered by the user to taht passed through to LDAP
# - simple
#    - this must be a DN and would be something like
#      CN=%s,DC=company,DC=com
# - digest
#    - usually pass through what is entered
#      %s    
ldap.authentication.userNameFormat=uid=%s,ou=people,dc=company,dc=com

# The LDAP context factory to use
ldap.authentication.java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory

# The URL to connect to the LDAP server
ldap.authentication.java.naming.provider.url=ldap://ldap.company.com:389

# The authentication mechanism to use
ldap.authentication.java.naming.security.authentication=simple

# The default principal to use (only used for LDAP sync)
ldap.authentication.java.naming.security.principal=cn=manager,dc=company,dc=com

# The password for the default principal (only used for LDAP sync)
ldap.authentication.java.naming.security.credentials=***THE PASSWORD IN CLEAR***

# Escape commas entered by the user at bind time
# Useful when using simple authentication and the CN is part of the DN and contains commas
#ldap.authentication.escapeCommasInBind=false

# Escape commas entered by the user when setting the authenticated user
# Useful when using simple authentication and the CN is part of the DN and contains commas, and the escaped \, is
# pulled in as part of an LDAP sync
# If this option is set to true it will break the default home folder provider as space names can not contain \
#ldap.authentication.escapeCommasInUid=false

Create and deploy your customized auth bean.


It will take auth data from the LDAP server and save it in MD4 in the Alfresco database.
Make sure to use whatever system you use to build and deploy these beans to your extension directory.

File: com/company/repo/security/authentication/RepositoryAuthenticationDao.java




package com.company.repo.security.authentication;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.UserDetails;
import net.sf.acegisecurity.providers.dao.User;
import net.sf.acegisecurity.providers.dao.UsernameNotFoundException;
import net.sf.acegisecurity.providers.encoding.PasswordEncoder;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.springframework.dao.DataAccessException;



/**
* This RepositoryAuthenticationDao servers for CIFS authentication. Based on alfresco's RepositoryAuthenticationDao
* with some modification/fixes.
*
*
*
*  @author RVycital
*/
public class RepositoryAuthenticationDao implements MutableAuthenticationDao{

   
    //private static final StoreRef STOREREF_USERS = new StoreRef('user', 'alfrescoUserStore');
private static final StoreRef STOREREF_USERS = new StoreRef('workspace', 'SpacesStore');
    private StoreRef userStoreRef;
    private NodeService nodeService;

    private NamespacePrefixResolver namespacePrefixResolver;

    @SuppressWarnings('unused')
    private DictionaryService dictionaryService;

    private SearchService searchService;
   
    private RetryingTransactionHelper retryingTransactionHelper;

    private PasswordEncoder passwordEncoder;

    private boolean userNamesAreCaseSensitive;

    /**
     * People are stored like type PERSON in our cases (not a USR).
     * Perhaps an alfresco bug. Also corrects the PROP_USER_USERNAME to PROP_USERNAME for this case.
     */
    public NodeRef getUserOrNull(String searchUserName)
    {
        if(searchUserName == null)
        {
            return null;
        }
        if(searchUserName.length() == 0)
        {
            return null;
        }
      
        SearchParameters sp = new SearchParameters();
        sp.setLanguage(SearchService.LANGUAGE_LUCENE);
        sp.setQuery('TYPE:\'{http://www.alfresco.org/model/content/1.0}person\' AND '
                  + ' (@\\{http\\://www.alfresco.org/model/content/1.0\\}userName:\''+searchUserName+'\')');
      
        //old impl
        //sp.setQuery('@usr\\:username:\'' + searchUserName + '\'');
        sp.addStore(STOREREF_USERS);
//        try
//        {
//            sp.addStore(tenantService.getName(searchUserName, userStoreRef));
//        }
//        catch (AlfrescoRuntimeException e)
//        {
//            return null; // no such tenant or tenant not enabled
//        }

        sp.excludeDataInTheCurrentTransaction(false);

        ResultSet rs = null;

        try
        {
            rs = searchService.query(sp);

            NodeRef returnRef = null;

            for (ResultSetRow row : rs)
            {

                NodeRef nodeRef = row.getNodeRef();
                if (nodeService.exists(nodeRef))
                {
                    String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(
                            nodeRef, ContentModel.PROP_USERNAME)); //used to be PROP_USER_USERNAME

                    if (userNamesAreCaseSensitive)
                    {
                        if (realUserName.equals(searchUserName))
                        {
                            if(returnRef == null)
                            {
                               returnRef = nodeRef;
                            }
                            else
                            {
                                throw new AlfrescoRuntimeException('Found more than one user for '+searchUserName+ ' (case sensitive)');
                            }
                        }
                    }
                    else
                    {
                        if (realUserName.equalsIgnoreCase(searchUserName))
                        {
                            if(returnRef == null)
                            {
                               returnRef = nodeRef;
                            }
                            else
                            {
                                throw new AlfrescoRuntimeException('Found more than one user for '+searchUserName+ ' (case insensitive)');
                            }
                        }
                    }
                }
            }
          
            return returnRef;
        }
        finally
        {
            if (rs != null)
            {
                rs.close();
            }
        }
    }
  
    public UserDetails loadUserByUsername(String incomingUserName) throws UsernameNotFoundException, DataAccessException {
        NodeRef userRef = getUserOrNull(incomingUserName);
        if (userRef == null) {
            throw new UsernameNotFoundException('Could not find user by userName: ' + incomingUserName);
        }

        Map<QName, Serializable> properties = nodeService.getProperties(userRef);
        String password = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_PASSWORD));

        // Report back the user name as stored on the user
        String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_USERNAME));

        GrantedAuthority[] gas = new GrantedAuthority[1];
        gas[0] = new GrantedAuthorityImpl('ROLE_AUTHENTICATED');

        UserDetails ud = new User(userName, password, getEnabled(userRef), !getAccountHasExpired(incomingUserName), !getCredentialsHaveExpired(incomingUserName), !getAccountlocked(incomingUserName), gas);
        return ud;
    }
  
    public boolean getEnabled(String userName)
    {
        return getEnabled(getUserOrNull(userName));
    }

    private boolean getEnabled(NodeRef userNode)
    {
        if (userNode == null)
        {
            return false;
        }
        Serializable ser = nodeService.getProperty(userNode, ContentModel.PROP_ENABLED);
        if (ser == null)
        {
            return true;
        }
        else
        {
            return DefaultTypeConverter.INSTANCE.booleanValue(ser);
        }
    }

  
    /**
     * Always return false.
     */

    public boolean getAccountHasExpired(String userName)
    {
        return false;
    }
  

    public boolean getAccountlocked(String userName)
    {
        return false;
    }


    public boolean getCredentialsExpire(String userName)
    {
        return false;
    }
  
  

    public boolean getCredentialsHaveExpired(String userName)
    {
        return false;
    }

    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

//    public void setTenantService(TenantService tenantService) {
//        this.tenantService = tenantService;
//        super.setTenantService(tenantService);
//    }

    public void setSearchService(SearchService searchService) {
        this.searchService = searchService;
    }

    public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) {
        this.userNamesAreCaseSensitive = userNamesAreCaseSensitive;
    }

    public void setUserStoreRef(String userStoreRef) {
        this.userStoreRef = new StoreRef(userStoreRef);
    }
   
   

    public boolean getUserNamesAreCaseSensitive()
    {
        return userNamesAreCaseSensitive;
    }



    public void setDictionaryService(DictionaryService dictionaryService)
    {
        this.dictionaryService = dictionaryService;
    }

    public void setNamespaceService(NamespacePrefixResolver namespacePrefixResolver)
    {
        this.namespacePrefixResolver = namespacePrefixResolver;
    }


   
    public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
    {
        this.retryingTransactionHelper = retryingTransactionHelper;
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder)
    {
        this.passwordEncoder = passwordEncoder;
    }





   

    public void createUser(String caseSensitiveUserName, char[] rawPassword) throws AuthenticationException
    {
        NodeRef userRef = getUserOrNull(caseSensitiveUserName);
        if (userRef != null)
        {
            throw new AuthenticationException('User already exists: ' + caseSensitiveUserName);
        }
        NodeRef typesNode = getUserFolderLocation();
        Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_USER_USERNAME, caseSensitiveUserName);
        String salt = null; // GUID.generate();
        properties.put(ContentModel.PROP_SALT, salt);
        properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt));
        properties.put(ContentModel.PROP_ACCOUNT_EXPIRES, Boolean.valueOf(false));
        properties.put(ContentModel.PROP_CREDENTIALS_EXPIRE, Boolean.valueOf(false));
        properties.put(ContentModel.PROP_ENABLED, Boolean.valueOf(true));
        properties.put(ContentModel.PROP_ACCOUNT_LOCKED, Boolean.valueOf(false));
        nodeService.createNode(typesNode, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_USER, ContentModel.TYPE_USER,
                properties);

    }

    private NodeRef getUserFolderLocation()
    {
        QName qnameAssocSystem = QName.createQName('sys', 'system', namespacePrefixResolver);
        QName qnameAssocUsers = QName.createQName('sys', 'people', namespacePrefixResolver); // see
        // AR-527
        NodeRef rootNode = nodeService.getRootNode(STOREREF_USERS);
        List<ChildAssociationRef> results = nodeService.getChildAssocs(rootNode, RegexQNamePattern.MATCH_ALL,
                qnameAssocSystem);
        NodeRef sysNodeRef = null;
        if (results.isEmpty())
        {
            throw new AlfrescoRuntimeException('Required authority system folder path not found: ' + qnameAssocSystem);
        }
        else
        {
            sysNodeRef = results.get(0).getChildRef();
        }
        results = nodeService.getChildAssocs(sysNodeRef, RegexQNamePattern.MATCH_ALL, qnameAssocUsers);
        NodeRef userNodeRef = null;
        if (results.isEmpty())
        {
            throw new AlfrescoRuntimeException('Required user folder path not found: ' + qnameAssocUsers);
        }
        else
        {
            userNodeRef = results.get(0).getChildRef();
        }
        return userNodeRef;
    }

    public void updateUser(String userName, char[] rawPassword) throws AuthenticationException
    {
        NodeRef userRef = getUserOrNull(userName);
        if (userRef == null)
        {
            throw new AuthenticationException('User name does not exist: ' + userName);
        }
        Map<QName, Serializable> properties = nodeService.getProperties(userRef);
        String salt = null; // GUID.generate();
        properties.remove(ContentModel.PROP_SALT);
        properties.put(ContentModel.PROP_SALT, salt);
        properties.remove(ContentModel.PROP_PASSWORD);
        properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt));
        nodeService.setProperties(userRef, properties);
    }

    public void deleteUser(String userName) throws AuthenticationException
    {
        NodeRef userRef = getUserOrNull(userName);
        if (userRef == null)
        {
            throw new AuthenticationException('User name does not exist: ' + userName);
        }
        nodeService.deleteNode(userRef);
    }

    public Object getSalt(UserDetails userDetails)
    {
        // NodeRef userRef = getUserOrNull(userDetails.getUsername());
        // if (userRef == null)
        // {
        // throw new UsernameNotFoundException('Could not find user by userName:
        // ' + userDetails.getUsername());
        // }
        //
        // Map<QName, Serializable> properties =
        // nodeService.getProperties(userRef);
        //
        // String salt = DefaultTypeConverter.INSTANCE.convert(String.class,
        // properties.get(QName.createQName('usr', 'salt',
        // namespacePrefixResolver)));
        //
        // return salt;
        return null;
    }

    public boolean userExists(String userName)
    {
        return (getUserOrNull(userName) != null);
    }

    public boolean getAccountExpires(String userName)
    {
        NodeRef userNode = getUserOrNull(userName);
        if (userNode == null)
        {
            return false;
        }
        Serializable ser = nodeService.getProperty(userNode, ContentModel.PROP_ACCOUNT_EXPIRES);
        if (ser == null)
        {
            return false;
        }
        else
        {
            return DefaultTypeConverter.INSTANCE.booleanValue(ser);
        }
    }

    public Date getAccountExpiryDate(String userName)
    {
        NodeRef userNode = getUserOrNull(userName);
        if (userNode == null)
        {
            return null;
        }
        if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode,
                ContentModel.PROP_ACCOUNT_EXPIRES)))
        {
            return DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode,
                    ContentModel.PROP_ACCOUNT_EXPIRY_DATE));
        }
        else
        {
            return null;
        }
    }

   

    public Date getCredentialsExpiryDate(String userName)
    {
        NodeRef userNode = getUserOrNull(userName);
        if (userNode == null)
        {
            return null;
        }
        if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode,
                ContentModel.PROP_CREDENTIALS_EXPIRE)))
        {
            return DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode,
                    ContentModel.PROP_CREDENTIALS_EXPIRY_DATE));
        }
        else
        {
            return null;
        }
    }


    private boolean getCredentialsHaveExpired(NodeRef userNode)
    {
        if (userNode == null)
        {
            return false;
        }
        if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode,
                ContentModel.PROP_CREDENTIALS_EXPIRE)))
        {
            Date date = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode,
                    ContentModel.PROP_CREDENTIALS_EXPIRY_DATE));
            if (date == null)
            {
                return false;
            }
            else
            {
                return date.compareTo(new Date())

File: com/company/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java



package com.company.repo.security.authentication.ldap;

import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;

import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import com.company.repo.security.authentication.RepositoryAuthenticationDao;
import org.alfresco.repo.security.authentication.ldap.LDAPInitialDirContextFactory;
import org.alfresco.repo.security.authentication.NTLMMode;
/*
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
*/

/**
* Modified to reencode the password in MD4 to be able to use CIFS with LDAP
*
* @author tverin
*/
public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationComponent
{

  // Logging
    /* 
  protected static final Log logger = LogFactory.getLog('com.company.repo.security.authentication.ldap');
    */
   
    private String userNameFormat;
   
    private LDAPInitialDirContextFactory ldapInitialContextFactory;
   
    private RepositoryAuthenticationDao authenticationDao;
   

    public void setAuthenticationDao(RepositoryAuthenticationDao authenticationDao) {
this.authenticationDao = authenticationDao;
    }


    public LDAPAuthenticationComponentImpl() {
        super();
    }

   
    public void setLDAPInitialDirContextFactory(LDAPInitialDirContextFactory ldapInitialDirContextFactory)
    {
        this.ldapInitialContextFactory = ldapInitialDirContextFactory;
    }
   
   
    public void setUserNameFormat(String userNameFormat)
    {
        this.userNameFormat = userNameFormat;
    }

    /**
     * This provides supports MD4, so let's say so.
     */
    public NTLMMode getNTLMMode()
    {
return NTLMMode.MD4_PROVIDER;
    }

    public String getMD4HashedPassword(String userName)
    {
return authenticationDao.getMD4HashedPassword(userName);
    }


    /**
     * Implement the authentication method
     */
    protected void authenticateImpl(String userName, char[] password) throws AuthenticationException
    {
        InitialDirContext ctx = null;
        try
        {
     /*
     String pwStr = String.valueOf (password);
     logger.debug ('> User: '+userName+ ' - pw: ' + pwStr);
     */

     String userIdentifier = String.format(userNameFormat, new Object[]{userName});
            ctx = ldapInitialContextFactory.getInitialDirContext(userIdentifier, new String(password));
           
            // Authentication has been successful.
            // Set the current user, they are now authenticated.
            setCurrentUser(userName);      
            //save the password in the alfresco user in MD4
            authenticationDao.updateUser(userName, password);
        }
        finally
        {
            if(ctx != null)
            {
                try
                {
                    ctx.close();
                }
                catch (NamingException e)
                {
                    clearCurrentSecurityContext();
                    throw new AuthenticationException('Failed to close connection', e);
                }
            }
        }
    }


    @Override
    protected boolean implementationAllowsGuestLogin()
    {
        InitialDirContext ctx = null;
        try
        {
            ctx = ldapInitialContextFactory.getDefaultIntialDirContext();
            return true;      
           
        }
        catch(Exception e)
        {
            return false;
        }
        finally
        {
            if(ctx != null)
            {
                try
                {
                    ctx.close();
                }
                catch (NamingException e)
                {
                    throw new AuthenticationException('Failed to close connection', e);
                }
            }
        }
    }

}


Create/edit /opt/alfresco/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/extension/file-servers-custom.xml


Follow Alfresco documentation to properly configure the parameters for your CIFS server.


Appendix - Instruction for Alfresco Labs 3 STABLE


Apologies if this seems painfully obvious to some people.
First compile the two sources and put them
$alf_home/tomcat/webapps/alfresco/WEB-INF/classes/com/company/repo/security/authentication and $alf_home/tomcat/webapps/alfresco/WEB-INF/classes/com/company/repo/security/authentication respectfully.


To compile


Its assumed that you installed in /opt/Alfresco, if not make changes accordingly.

export CLASSPATH=/opt/Alfresco/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-repository.jar:/opt/Alfresco/tomcat/webapps/alfresco/WEB-INF/lib/acegi-security-0.8.2_patched.jar:/opt/Alfresco/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-core.jar:/opt/Alfresco/tomcat/webapps/alfresco/WEB-INF/lib/spring-2.0.6.jar

javac RepositoryAuthenticationDao.java

export CLASSPATH=$CLASSPATH:/opt/Alfresco/tomcat/webapps/alfresco/WEB-INF/classes/

cd ldap

javac LDAPAuthenticationComponentImpl.java


Further configuration


Edit authentication-services-context.xml, usually found in $alf_home/tomcat/webapps/alfresco/WEB-INF/classes/alfresco
Add following after the bean 'authenticationDao', line 103 on my installation.



<bean id='authenticationDaoFull' class='com.company.repo.security.authentication.RepositoryAuthenticationDao'>
    <property name='nodeService'>
        <ref bean='nodeService' />
    </property>
    <property name='dictionaryService'>
        <ref bean='dictionaryService' />
    </property>
    <property name='namespaceService'>
        <ref bean='namespaceService' />
    </property>
    <property name='searchService'>
        <ref bean='admSearchService' />
    </property>
    <property name='retryingTransactionHelper'>
        <ref bean='retryingTransactionHelper'/>
    </property>
    <property name='userNamesAreCaseSensitive'>
        <value>${user.name.caseSensitive}</value>
    </property>
    <property name='passwordEncoder'>
        <ref bean='passwordEncoder' />
    </property>
</bean>

3.0
Authentication