Integrating with LDAP

2,717 views
Skip to first unread message

hyder

unread,
Mar 13, 2010, 3:14:05 AM3/13/10
to play-framework
Hi,

If I want to use users from an LDAP source, how to do that?

Also, when deploying in production, I would like to use my container's
LDAP integration capabilities. Is this possible?

Thanks a lot!

grandfatha

unread,
Mar 13, 2010, 1:46:01 PM3/13/10
to play-framework
You can use the JNDI APIs to establish a connection to your LDAP
server and do a bind/search for the attributes/nodes/etc that you
would like retrieve from LDAP.

A nicer to code solution would be to use "Spring LDAP", a subproject
of the Spring Framework, which eliminates most of the ugly, repetitive
JNDI code and gives you nice callbacks. Think of it like Spring JDBC
is on top of regular JDBC.

A sample snippet with Spring LDAP is the following:

public class LDAPContactDAO implements ContactDAO{
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}

public List getAllContactNames() {
return ldapTemplate.search("", "(objectclass=person)",
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get();
}
});
}

}

(taken from here: http://www.javaworld.com/javaworld/jw-06-2007/jw-06-springldap.html
)


which is the shorter version to what you would need to write with JNDI
like the following:

public class SimpleLDAPClient {
public static void main(String[] args) {
Hashtable env = new Hashtable();


env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:10389/
ou=system");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
env.put(Context.SECURITY_CREDENTIALS, "secret");
DirContext ctx = null;
NamingEnumeration results = null;
try {
ctx = new InitialDirContext(env);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
results = ctx.search("", "(objectclass=person)",
controls);
while (results.hasMore()) {
SearchResult searchResult = (SearchResult)
results.next();
Attributes attributes = searchResult.getAttributes();
Attribute attr = attributes.get("cn");
String cn = (String) attr.get();
System.out.println(" Person Common Name = " + cn);
}
} catch (NamingException e) {
throw new RuntimeException(e);
} finally {
if (results != null) {
try {
results.close();
} catch (Exception e) {
}
}
if (ctx != null) {
try {
ctx.close();
} catch (Exception e) {
}
}
}
}
}

Here is a sample by the creators of Spring LDAP on how to do
authentication using Spring LDAP:

http://blog.jayway.com/2009/02/02/simple-authentication-using-spring-ldap/

hyder

unread,
Mar 31, 2010, 5:16:18 AM3/31/10
to play-framework
Hi,

So, this is what I tried:

1) Started with the yabe sample, verified that everything is working.
Also, installed Apache Directory and Studio.
2) Added the spring module support and my application-context.xml.
3) Copied spring ldap jars into the spring module lib directory.
4) Wrote a Person model class and a utility class (copied from the
Spring LDAP sample (http://static.springsource.org/spring-ldap/site/
reference/html/dirobjectfactory.html#d0e1013) with a couple of
modifications)
5) Then, I tried to change the authenticate method in the Security
class and the setConnectedUser method in Admin.

Here, I get a bit confused and also ran into a couple of problems:

User user = User.find("byEmail", Security.connected()).first();

How do I change this so that I can use spring ldap instead? The
"byEmail" operates on JPA whereas I need to find on LDAP. I notice
that it's not taking any parameter so how can I make my LDAP search?

2nd, I also get this error:
The file /app/controllers/Security.java could not be compiled. Error
raised is : Secure.Security cannot be resolved to a type.

In the console, I can see this:
play.exceptions.JavaCompilationException: Secure.Security cannot be
resolved to a type
at play.classloading.ApplicationCompiler
$2.acceptResult(ApplicationCompiler.java:240)
at
org.eclipse.jdt.internal.compiler.Compiler.handleInternalException(Compiler.java:
551)
at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:
409)
at
play.classloading.ApplicationCompiler.compile(ApplicationCompiler.java:
271)
at
play.classloading.ApplicationClassloader.getAllClasses(ApplicationClassloader.java:
373)
at
play.classloading.ApplicationClassloader.getAssignableClasses(ApplicationClassloader.java:
400)
at {module:crud}/app/views/tags/crud/types.tag.(line:3)
at play.templates.Template.render(Template.java:238)
at play.templates.Template$ExecutableTemplate.invokeTag(Template.java:
394)
at {module:crud}/conf/routes.(line:4)
at play.templates.Template.render(Template.java:238)
at play.mvc.Router.parse(Router.java:88)
at play.mvc.Router.parse(Router.java:110)
at play.mvc.Router.load(Router.java:36)
at play.mvc.Router.detectChanges(Router.java:131)
at play.Play.detectChanges(Play.java:466)
at play.Invoker$Invocation.init(Invoker.java:98)
at Invocation.HTTP Request(Play!)

So, I then tried reverting the changes in Security and Admin classes
but the last error still happens.

Please advise.

Cheers.

grandfatha

unread,
Apr 1, 2010, 4:30:49 AM4/1/10
to play-framework
Did you activate the security module in your application
configuration? If not, that could be a reason why Play does not put
the Secure.Security class on the classpath which is why the
compilation would fail then.

But in general, I think that the following is incorrect:

" User.find("byEmail", Security.connected()).first(); "

You need to replace that with your actual Ldap-Searcher-Class, because
this line will hit the database and not your LDAP server. You can
create an instance of that class in your Security controller yourself
or use dependency injection for that, so you would end up with
something similar to the following:


String username = ...
String password = ...
MyLdapSearcher ldap = new MyLdapSearcher();

User u = ldap.search(username, password);

hyder

unread,
Apr 7, 2010, 5:10:48 AM4/7/10
to play-framework
Hi,

Thanks for your advice. It was really helpful.

I'm now able to authenticate against LDAP (tested against Apache DS).
I used the Spring Module and Spring LDAP.

There are still a few limitations left which I'll try to clear and
then I hope I can contribute a write up.

Cheers.

Prakash Agarwalla

unread,
Mar 12, 2013, 1:01:00 PM3/12/13
to play-fr...@googlegroups.com
Hi.. I need a working sample of LDAP integration with play framework Version 2. Any one can help ??

Thanks in advance,
Prakash

Sascha Kleiber

unread,
Mar 13, 2013, 3:56:58 AM3/13/13
to play-fr...@googlegroups.com
Yes. You should get the Spring LDAP-Plugin first (http://www.springsource.org/ldap). Then you have to create an AttributeMapper depending on the values you want to get from LDAP. I wanted to get user-data from ldap, so I created an "PersonAttributeMapper":

package ldap;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import ldap.domain.Person;
import org.springframework.ldap.core.AttributesMapper;

/**
 * AttributeMapper for LDAP-mapping (Java Class Person <-> LDAP objectType = person)
 */
public class PersonAttributeMapper implements AttributesMapper {

    @Override
    public Object mapFromAttributes(Attributes attrs) throws NamingException {
        Person person = new Person();
        person.setFirstName((String)attrs.get("givenName").get());
        person.setLastName((String)attrs.get("sn").get());
        person.setUsername((String)attrs.get("uid").get());
        person.setUnit((String)attrs.get("description").get());
        person.setEmail((String)attrs.get("mail").get());
        person.setLdapid(Integer.parseInt((String)attrs.get("uidNumber").get()));

        return person;
    }
}

Also you have to create a Mapper-Class which represents an Ldap-Object a a Java-Entity,almost like an ORM-Entity, in my case "Person.java":

package ldap.domain;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * Mapping class for LDAP-person object
 *
 */
public class Person {

   private String unit;
   private String firstName;
   private String lastName;
   private String email;
   private String username;
   private int ldapid;


    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getLdapid() {
        return ldapid;
    }

    public void setLdapid(int ldapid) {
        this.ldapid = ldapid;
    }


    @Override
   public boolean equals(Object obj) {
      return EqualsBuilder.reflectionEquals(this, obj);
   }

    @Override
   public int hashCode() {
      return HashCodeBuilder.reflectionHashCode(this);
   }

    @Override
   public String toString() {
      return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
   }
}

At last I created a DAO:

package ldap.dao;

import java.util.List;
import ldap.domain.Person;

public interface PersonDao {
    public Person findPersonByUsername(String username);

    public Person findPersonByLdapid(int ldapid);

    public List<Person> getAllPersons();
}

And its implementation:

package ldap.dao;

import java.util.List;
import javax.inject.Inject;
import ldap.PersonAttributeMapper;
import ldap.domain.Person;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;

public class PersonDaoImpl implements PersonDao {

    @Inject
    private LdapTemplate ldapTemplate;

    public LdapTemplate getLdapTemplate() {
        return this.ldapTemplate;
    }

    public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }

    @Override
    public Person findPersonByUsername(String username) {
        AndFilter andFilter = new AndFilter();

        andFilter.and(new EqualsFilter("objectclass", "person"));
        andFilter.and(new EqualsFilter("uid", username));

        return (Person) getLdapTemplate().search("", andFilter.encode(), new PersonAttributeMapper()).get(0);
    }

    @Override
    public Person findPersonByLdapid(int ldapid) {
        AndFilter andFilter = new AndFilter();

        andFilter.and(new EqualsFilter("objectclass", "person"));
        andFilter.and(new EqualsFilter("uidNumber", ldapid));

        return (Person) getLdapTemplate().search("", andFilter.encode(), new PersonAttributeMapper()).get(0);
    }

    @Override
    public List getAllPersons() {
        return ldapTemplate.search("", "(&(objectClass=person)(!(uid=vpn_*))(!(uid=_ldap*))(description=*))", new PersonAttributeMapper());
    }
}

For spring you have to create a config file named application-context.xml in folder "conf":

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springsource.org/dtd/spring-beans-2.0.dtd">

<beans>
    <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
        <property name="url" value="ldap://<INSET LDAP IP>" />
        <property name="base" value="cn=users,dc=halx,dc=example,dc=int" />
    </bean>

    <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
        <constructor-arg ref="contextSource" />
    </bean>

    <bean id="ldapPerson" class="ldap.dao.PersonDaoImpl">
        <property name="ldapTemplate" ref="ldapTemplate" />
    </bean>
</beans>

That's it, works for me. I needed some time to get it working.
--
You received this message because you are subscribed to a topic in the Google Groups "play-framework" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/play-framework/-xZ9Ei037DI/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 
Reply all
Reply to author
Forward
0 new messages