#1 2014-08-26 09:28:46

Dennie
Member
Registered: 2014-08-22

LDAP Authentication

Hello, based on your example from the forums I've created a custom PAM authenticator; it will first check if there are reportserver credentials available and if not it will try to look up the user using ldap (active directory).
So far, so good; the authenticator returns an AuthenticationResult, but the user is still not allowed to login.
The user entity is not imported, it's simply created manually. Am I missing something? Is there a requirement for setting origin or credentials or something?

package ldap

import javax.naming.AuthenticationException
import javax.naming.Context
import javax.naming.InvalidNameException
import javax.naming.NamingException
import javax.naming.directory.InitialDirContext
import javax.persistence.NoResultException

import net.datenwerke.rs.authenticator.client.login.dto.UserPasswordAuthToken
import net.datenwerke.rs.authenticator.client.login.pam.UserPasswordClientPAM
import net.datenwerke.rs.utils.crypto.PasswordHasher;
import net.datenwerke.security.client.login.AuthToken
import net.datenwerke.security.service.authenticator.AuthenticationResult
import net.datenwerke.security.service.authenticator.ReportServerPAM
import net.datenwerke.security.service.authenticator.hooks.PAMHook
import net.datenwerke.security.service.usermanager.UserManagerService
import net.datenwerke.security.service.usermanager.entities.User

import com.google.inject.Inject



final LdapPAM ldapPam = GLOBALS.injector.getInstance(LdapPAM.class);
GLOBALS.services.callbackRegistry.attachHook("LDAP_PAM", PAMHook.class, new PAMHook(){
	
	public void beforeStaticPamConfig(LinkedHashSet<ReportServerPAM> pams){
		pams.add(ldapPam);
	}
	public void afterStaticPamConfig(LinkedHashSet<ReportServerPAM> pams){
		
	}
	
});


public class LdapPAM implements ReportServerPAM {
	
	private static final String CLIENT_MODULE_NAME = UserPasswordClientPAM.class.getName();
	UserManagerService userManagerService;
	PasswordHasher passwordHasher;
	
	@Inject
	public UserPasswordPAM(UserManagerService userManagerService,PasswordHasher passwordHasher) {
		this.userManagerService = userManagerService;
		this.passwordHasher = passwordHasher;
	}
	
	
	public AuthenticationResult authenticate(AuthToken[] tokens) {
		for(Object token : tokens){
			if(token instanceof UserPasswordAuthToken){
				UserPasswordAuthToken credentials = (UserPasswordAuthToken) token;
				
				/* First try reportserver internal authentication */
				User u = authenticateInternal(credentials.getUsername(), credentials.getPassword());
				if(null != u){
					return new AuthenticationResult(true, u, true);
				}else{
				  /* Secondly try LDAP authentication */
          u = authenticate(credentials.getUsername(), credentials.getPassword());
          if(null != u){
             return new AuthenticationResult(true, u, true);
          }
          else{
            User usr = getUserOrNull(credentials.getUsername());
            return new AuthenticationResult(false, usr, true);
          }
				}
			}
		}

		return new AuthenticationResult(false, null, false);
	}
	
	
	protected User getUserOrNull(String username){
		try{
			return userManagerService.getUserByName(username);
		}catch(NoResultException ex){
			return null;
		}
	}
	public User authenticateInternal(String username, String cleartextPassword){
	  if(cleartextPassword==null || cleartextPassword.empty) return null;
	  
    User user = getUserOrNull(username);
		
		if(null == user)
			return null;
		
		if(null != user.getPassword() && passwordHasher.validatePassword(user.getPassword(), cleartextPassword)){
			return user;
		}else
			return null;
	}
	
	public User authenticate(String username, String cleartextPassword){
		User user = getUserOrNull(username);
		if(null == user)
			return null;
		
		LdapAuthenticator authenticator = new LdapAuthenticator();	
		if(authenticator.authenticate(user, cleartextPassword)){
			return user;
		}else{
			return null;
		}
	}
	
	public String getClientModuleName() {
		return CLIENT_MODULE_NAME;
	}

}


public class LdapAuthenticator {
  /* The active directory domain */
	String domainName = "mydomain.local";
	/* The provider server */
  String server = "server-dc";

	public boolean authenticate(User user, String password){
	  if(password==null || password.empty) return false
		Properties props = new Properties();

		props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		props.setProperty(Context.PROVIDER_URL, "ldap://${server}.${domainName}/");
		props.setProperty(Context.URL_PKG_PREFIXES, "com.sun.jndi.url");
		props.setProperty(Context.SECURITY_AUTHENTICATION, "simple");

		props.setProperty(Context.SECURITY_PRINCIPAL, "${user.username}@${domainName}");
		
		props.setProperty(Context.SECURITY_CREDENTIALS, password);
		
		try {
			InitialDirContext ctx = new InitialDirContext(props);
			return true;
		} catch (AuthenticationException e) {
			return false;
		} catch (InvalidNameException e) {
			throw new RuntimeException(e);
		} catch (NamingException e) {
			if(e.getMessage().contains("LdapErr: DSID-0C0906E8")){
				return false;
			}			
		}

	}
	
}

Offline

#2 2014-08-26 20:29:31

Dennie
Member
Registered: 2014-08-22

Re: LDAP Authentication

Finally I managed to got the development environment running and see what's going on under the hood of reportserver. I understand the way the authentication mechanism works much better now and why my code is failing. My users require a null password otherwise the UserPasswordPAM will be authoritive. I'll reconfigure my scripts.

Last edited by Dennie (2014-08-26 20:30:18)

Offline

#3 2014-08-27 07:38:46

Dennie
Member
Registered: 2014-08-22

Re: LDAP Authentication

Hello, I like to debate the implementation of evaluateTokens of AuthenticatorServiceImpl.

I think it does not make sense that more than one PAM can be authoritive. The first authoritive PAM should be able to make the AuthenticationResult invalid only. Currently there is a need to remove an authoritive conflicting PAM or to work arround the implementation of authoritive.

I'll explain my case:
I want to manually create my users, but want to authenticate them using LDAP. This can only work if the passwords are null, because UserPasswordPAM is authoritive if the password is not null. The implementation of User does not allow me to set the password to null; so I require to update the database manually.

Offline

#4 2014-08-28 10:27:03

Arno Mittelbach
datenwerke
Registered: 2012-02-14

Re: LDAP Authentication

Hi Dennie,

if I see this correctly there is no need for you to have the UserPasswordPAM active in addition to your custom PAM. That is, you should be able to simply remove the UserPasswordPAM from the config as your implementation does all the necessary handling.

As for the implementation of authoritative, we are looking into this.

Arno

Offline

Board footer

Powered by FluxBB