Matt RaibleMatt Raible is a Java Champion and Developer Advocate at Okta. developer.okta.com

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Implementing the AppFuse DAO layer with Spring LDAP

This week, I've been helping a click kickstart an application using AppFuse. The first order of business was re-writing the backend to support LDAP. They'd like to keep the Hibernate implementation in place, but they'd also prefer to have the option to use LDAP. Since a database will still be used to manage entities outside of User and Role, the LDAP implementation I have continues to maintain a userId and username in the database.

I'm pretty impressed that I was able to get 90% of the functionality completed in 2 days. Of course, it wouldn't have been possible w/o the excellent Spring LDAP project, nor the good ol' Acegi Security project. I'm deploying on Geronimo using the Apache Directory Plugin for LDAP. Getting a custom UserDetailsService working wasn't too difficult, but I am still having some issues with CRUDing LDAP:

  • I get an Undefined Attribute Type error when modifying the non-String attributes in a User. This is quite strange since all the attributes are stored as Strings in LDAP.
  • I get the same error when trying to remove a user from a role.
  • There's no LdapUnit (like DbUnit) to delete/insert users and roles before running tests. I tried to use the LdapTestServer from Acegi, but no dice.
  • Since the project has two versions (Hibernate or LDAP) in the source tree, the LDAP and Hibernate tests won't both pass. This is because the schema for LDAP only has 2 columns in the app_user table, while the Hibernate version of the table has all columns. The simplest solution seems to be removing the not-null constraints on most of the columns in this table.

If anyone has experience implementing User->Role CRUD in LDAP with Spring LDAP, I'd love to hear your thoughts on these issues.

Posted in Java at Oct 11 2006, 09:09:48 AM MDT 11 Comments
Comments:

Hey Matt,

It's been over a year since I did some LDAP/Java programming, but I think the error you're getting (Undefined Attribute Type) is related to those attributes not being part of "top", "person", or "inetOrgPerson". (Their being non-Strings is a coincidence.) Those problematic attributes are probably part of the "user" class, so I'd try changing the one line to:

context.setAttributeValues("objectclass", new String[]{"top", "person", "inetOrgPerson", "user"});

and see what happens. If I recall correctly, the user class is MS AD specific. Note that different LDAP-compatible directory servers define users differently. Don't expect this code to be portable from AD to eDirectory to OpenLDAP. You may need a different implementation for each platform (unless Spring is doing some magic here. It wouldn't be the first time. :-).

Posted by kelzer on October 11, 2006 at 02:18 PM MDT #

Thanks for the suggestion - I tried it and unfortunately it didn't work. I still get the same error. I imagine it's something pretty simple, I'm just not seeing it at this point.

Posted by Matt Raible on October 11, 2006 at 03:09 PM MDT #

Hey Matt,

What LDAP server are you using? I think I'm on the right track, but without more info I can't be much help, because every LDAP server treats users differently. Where are you getting the names of those attributes? (accountEnabled, etc.) I'm pretty sure they have to exist within one of those objects listed in the objectclass list. My guess is you're using OpenLDAP. I've worked with AD, Novell eDirectory, and Oracle Internet Directory (OID). They all do things differently when it comes to users.

Check out these LDAP error codes. Error 17 states: LDAP_UNDEFINED_ENGINE: Indicates that the attribute specified in the modify or add operation does not exist in the LDAP server's schema.

Active Directory actually uses a single attribute named "userAccountControl" that's used by the "user" class which stores flags for the locked state, password expiration state, etc. As I've said, other directory services do things differently. Novell eDirectory has a class named "ndsLoginProperties" that has attributes for those kinds of things. IBM Tivoli LDAP Server uses an attribute named "pwdLockout".

It's unfortunate that the LDAP standard doesn't standardize a lot of this type of thing.

Posted by kelzer on October 11, 2006 at 03:42 PM MDT #

I'm using Apache's Directory Server - version 0.9.2. I tried installing ApacheDS 1.0.0 on my Mac, but the installer fails. I'm currently using the Geronimo plugin since that was the easiest to setup and install. I tried installing OpenDS, but that didn't work either.

AFAICT, the attributes are all named the same. The strange thing is the error only occurs when I modify certain attributes - most of the attributes can be modified just fine. You're probably right, I'm just not seeing it.

Posted by Matt Raible on October 11, 2006 at 03:49 PM MDT #

Apache DS probably doesn't have the 'user' objectclass defined, which means that you'll be unable to use the attributes defined for that objectclass. Working against Active Directory it would probably work (if you add the 'user' objectclass as suggested above), but since the 'user' objectclass is proprietary to Active Directory it won't work on any other server. You might need to implement different Dao implementations depending on which server you are running against. Possibly it's the same problem when modifying the Role entity. What objectclass is your Role entity and what's the name of the attribute you're trying to modify?

Posted by Mattias Arthursson on October 13, 2006 at 07:58 AM MDT #

Mattias - here's the current sample-data.ldif file I'm using:

# Define an entry to contain people
dn: ou=users,ou=system
objectClass: organizationalUnit
ou: users

# Define a user entry for Tomcat User
dn: uid=tomcat,ou=users,ou=system
objectClass: inetOrgPerson
objectClass: person
objectClass: top
uid: tomcat
userPassword: {SHA}U2wLM5NFYWwbM8r0VEVNi4oZDWw=
cn: Tomcat
sn: User
displayName: Tomcat User
mail: matt_raible@yahoo.com
telephoneNumber: 
title: Just a User
department: Accounting
passwordHint: A male kitty.
accountEnabled: true
accountExpired: false
accountLocked: false
credentialsExpired: false
version: 1

# Define a user entry for Matt Raible
dn: uid=mraible,ou=users,ou=system
objectClass: inetOrgPerson
objectClass: person
objectClass: top
uid: mraible
userPassword: {SHA}U2wLM5NFYWwbM8r0VEVNi4oZDWw=
cn: Matt
sn: Raible
displayName: Matt Raible
mail: matt@raibledesigns.com
telephoneNumber: 
title: Admin and User
department: Finance
passwordHint: A male kitty.
accountEnabled: true
accountExpired: false
accountLocked: false
credentialsExpired: false
version: 1

# Define an entry to contain LDAP groups
# searches for roles are based on this entry
dn: ou=groups,ou=system
objectClass: organizationalUnit
ou: groups

# Define an entry for the "user" role
dn: cn=user,ou=groups,ou=system
objectClass: groupOfUniqueNames
objectClass: top
cn: user
uniqueMember: uid=mraible,ou=users,ou=system
uniqueMember: uid=tomcat,ou=users,ou=system

# Define an entry for the "admin" role
dn: cn=admin,ou=groups,ou=system
objectClass: groupOfUniqueNames
objectClass: top
cn: admin
uniqueMember: uid=mraible,ou=users,ou=system

And here's my Java code for removing a user from a role:

    public void removeUser(Long userId) {
        User user = getUser(userId);
        ldapTemplate.unbind(buildDn(user));

        // remove user from roles - this currently doesn't work due to issue in last 3 lines of this block
        for (Role role : user.getRoles()) {
            Role r = roleDao.getRoleByName(role.getName());
            List<String> members = new ArrayList<String>(r.getMembers().length);
            for (String member : r.getMembers()) {
                if (member.indexOf(user.getUsername()) == -1) {
                   members.add(member);
                }
            }
            r.setMembers(members.toArray(new String[0]));

            // calling saveRole results in javax.naming.directory.SchemaViolationException
            // todo: fix so users are removed from roles when they're deleted
            //roleDao.saveRole(r);
        }

        // remove from database too
        jdbcTemplate.update("delete from " + userTableName + " where id=?", new Object[]{userId});
    }

RoleDaoLdap.saveRole(Role role) is:

    public void saveRole(Role role) {
        Name dn = buildDn(role.getName());
        DirContextAdapter context = (DirContextAdapter) ldapTemplate.lookup(dn);
        mapToContext(role, context);
        ldapTemplate.modifyAttributes(dn, context.getModificationItems());
    }

    protected void mapToContext(Role role, DirContextAdapter context) {
        context.setAttributeValues("objectclass", new String[]{"top", "groupOfUniqueNames"});
        context.setAttributeValue("cn", role.getName());
        context.setAttributeValue("description", role.getDescription());
        context.setAttributeValues("uniqueMember", role.getMembers());
    }

RoleDaoLdap.saveRole() works fine when I adding a new uniquemember attribute, it just fails when I try to remove one.

Posted by Matt Raible on October 13, 2006 at 08:07 AM MDT #

Regarding the LDAP data loading: post a note about it on the Spring LDAP forum and we can discuss it there. We have a few alternatives. One is based on the LdapFixtureLoader functionality in DDSteps (http://ddsteps.org), another pet project of ours. The other is based on the LdifFileLoader that's built into Apache DS. We use the latter approach in our integration tests for Spring LDAP, towards an in-memory instance of Apache DS.

Posted by Ulrik Sandberg on October 13, 2006 at 10:06 AM MDT #

Thanks Ulrik - I've posted a question about LDAP data loading to the Spring LDAP forum.

Posted by Matt Raible on October 13, 2006 at 10:14 AM MDT #

Well, I'm confused. The ldif file shouldn't work; the inetOrgPerson doesn't have those attributes. Also, the behaviour with the role is really wierd - if it works for adding a role it should work when removing one. I'm beginning to suspect there's something seriously wrong with the version of Apache DS you're using.

Posted by Mattias Arthursson on October 14, 2006 at 12:56 AM MDT #

hi, please can u suggest me how to add uniqueMember into ldap server by using spring ldap api..i need to add multiple uniqueMembers to the ldapserver.

Posted by anil on September 08, 2008 at 11:02 PM MDT #

hi, please can u suggest me how to add uniqueMember into ldap server by using spring ldap api..i need to add multiple uniqueMembers to the ldapserver.

Posted by anil on September 09, 2008 at 02:04 AM MDT #

Post a Comment:
  • HTML Syntax: Allowed