cancel
Showing results for 
Search instead for 
Did you mean: 

LDAP + CIFS to access alfresco repository

vinuraga
Champ in-the-making
Champ in-the-making
Hi,

Am trying to use LDAP + CIFS to access my alfresco repository,
am using alfresco enterprise 2.1.2 version,
but login is not completing,even am not able to map alfresco server through windows explorer.
my ldap Authenticating code is fine it is not giving any error.

Is there any other way to access the repository with LDAP and CIFS combination.

Thanks
Vinod
7 REPLIES 7

vycitalr
Champ in-the-making
Champ in-the-making
Hi,

I am running this combination, but that is a customization. By default, just by configuration this is impossible, as far as I could understand. That is due to a fact, that you are unable to dig out a MD4-hash password out of LDAP, which you need  for CIFS authentication. What I did is, that I have a modified authentication component that stores the md4 hash to user's properties after it successfully authenticates to LDAP. Than with some configuration I can successfully use CIFS.

Regards Robert

vinuraga
Champ in-the-making
Champ in-the-making
Hi Robert,

Thanks for quick reply,

can u tell me wht authentication componet you modified and what configuration u changed.

Regards
Vinod Raga
Cignex Technologies
India

vycitalr
Champ in-the-making
Champ in-the-making
ok, what you need is schematically following. I suppose you use some standard configuration from the provided ldap-authentication-context.xml. 

1. You need to customize the authentication component (LDAPAuthenticationComponentImpl) , so that always when authentication is successfully performed, write/update the MD-4 hash password into users' properties. For this you will need the authenticationDao be available in the authentication component. But you need the FULL athentication dao, which has tha password encoder set, and can modify the user's properties. This one is defined in authentication-services-context.xml and is implemented by org.alfresco.repo.security.authentication.RepositoryAuthenticationDao. However, this bean definition is overriden in ldap-authentication-context.xml, where the authenticationDao is implemented by org.alfresco.repo.security.authentication.DefaultMutableAuthenticationDao, which you cannot use for the pupose. So I suggest you simply rename the bean atuhenticaionDao in authentication-services-context.xml eg to. authenticationDaoFull, so that it's not redefined. Than you can freely inject this full dao into the authenticationComponent (LDAPAuthenticationComponentImpl). Provided you have this full dao available there you can update the user's password (write the MD-4 hash) with construction like this (on a proper place):

authenticationDao.updateUser(userIdentifier, password);
This writes the hashed password into the users' property.

2. similiar way as you backed-up the full authenticaiton dao for another use, do the similiar with authenticationComponent from authentication-services-context.xml - eg. rename it to authenticationComponentFull and make it use  the authenticationDaoFull. This way you avoid overriding it by the authenticationComponent from ldap-authentication-context.xml. Then change the network-protocol-context.xml so that it uses the authenticationComponentFull. This way the classic authorization for CIFS will be used, without knowing anything about LDAP.

3. then just configure cifs to authenticate with "alfresco" or "enterprise" authenticator with no regards to LDAP. CIFS will have no notion about LDAP.  Litlle disadvantage here is, that if you change the pass in LDAP, you will authenticate to CIFS with the old one, untill you log in normally and successfully to alfresco via  webcliebnt, when the password is updated. From the security point of view is perhaps not very nice.

4. Pay attention to how your user obejcts look like. The above is only applicable if these are normal alfresco users. Where your users come from? Did you create them manually or via some sync? Check what type do they have, is it ContentModel.TYPE_USER or ContentModel.TYPE_PERSON?  This is important, because these two types have different properties, and the PERSON does not have PASSWORD property to set. In toher words, the authenticaionDao expects the users  of TYPE_USER. If you have the users of type TYPE_PERSON than you are liitle out of luck. Then you must also change the authenticationDao so that it works with TYPE_PERSON. I had this problem, becuase I used the LDAP-synch  and it create my users with TYPE_PERSON. But that is another story.

Hope this helps.
Regards Robert

vinuraga
Champ in-the-making
Champ in-the-making
Hi Robert,

Thanks for reply,

Am using ldap-synch,am not going to create the users in alfresco,they will be from ldap only,
am using following list of files for my configuration

ldap-authentication.properties
ldap-authentication-context.xml
ldap-synchronisation-context.xml
ldap-synchronisation.properties
file-servers-custom.xml

i hav tried your soluiton but its not helpful to my problem,is there any other way to solve the issue,
please let me know.

Regards
Vinod

vycitalr
Champ in-the-making
Champ in-the-making
I do not think this problem is solvable just by configuration, you cannot make it without programatic changes.  But I had exactly this case and was able to manage that. What you need is a customized AuthenticationDao, that handles correctly your users. The change is mainly about the getUserOrNull() and loadUserByUsername() methods, where you need to change query how a user is looked up, and also change the use of ContentModel.PROP_USER_USERNAME to ContentModel.PROP_USERNAME.

Here is my modified AuthenticationDao that solved my problem:

package com.XXX.repo.security.authentication;

import java.io.Serializable;
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 org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.tenant.TenantService;
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.QName;
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 extends org.alfresco.repo.security.authentication.RepositoryAuthenticationDao{

    //private static final StoreRef STOREREF_USERS = new StoreRef("user", "alfrescoUserStore");

    private StoreRef userStoreRef;
    private NodeService nodeService;
    private TenantService tenantService;
    private SearchService searchService;

    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.
     */
    @Override
    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 + "\"");
      
        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();
            }
        }
    }
   
    @Override
    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;
    }
   
    @Override
    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.
     */
    @Override
    public boolean getAccountHasExpired(String userName)
    {
        return false;
    }
   
    @Override
    public boolean getAccountlocked(String userName)
    {
        return false;
    }

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

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

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

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

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

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


Than make this authenticationDao be used for cifs - so make in the sense of "authenticationDaoFull" mentioned in my previous post.

I think this is all I can do for you. These are all my changes as to this problem.

Regards Robert

guigsilva
Confirmed Champ
Confirmed Champ
Hi everybody;
I have the same picture here:
- Alfresco Community edition;
- Openldap (zimbra);

I change the default authentication from alfresco to ask user/pass to the zimbra LDAP server. That woks ok for web UI I can login with my LDAP user… But CIF don´t work… I
I read this post and I think that I can live with the non-sync password issue …
So I start to study the modifications on the /opt/alfresco/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/authentication-services-context.xml I find the authenticaionDao bean with this:
       <alias name="authenticationDao" alias="alfDaoImpl"/>  <!– TODO: Remove –>
    <bean id="authenticationDao" class="org.alfresco.repo.security.authentication.RepositoryAuthenticati$
       <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>

    <!– The DAO also acts as a salt provider.                              –>

    <alias alias="saltSource" name="authenticationDao"/>

    <!– Passwords are encoded using MD4                                    –>
    <!– This is not ideal and only done to be compatible with NTLM         –>
    <!– authentication against the default authentication mechanism.       –>

So if I understand, I need to copy/paste with a different name eg.:authenticationDaoFull and on the same file write
authenticationDao.updateUser(userIdentifier, password);

On the bean authenticationComponent, so here is question the line to put in the authenticationComponent is

authenticationDao.updateUser(userIdentifier, password);

or
authenticationDaofull.updateUser(userIdentifier, password);
???

I will try this tomorrow and want to have sure…

Sorry by the poor english,
Guilherme

sundersnegi
Champ in-the-making
Champ in-the-making
Hi Guilherme,

I am also facing the same problem.

Have you tried the steps given above???

If yes, could you please let me know your findings and results.