I want to implement a basic user repository using Spring LDAP and it's concept of Object-Directory Mapping (ODM).
My User class is pretty straightforward :
@Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "shadowAccount", "top" }, base = "ou=people")
public class User {
[...]
@Id
private Name dn;
@Attribute(name = "uid")
@DnAttribute(value = "uid")
private String username;
@Attribute(name = "cn")
private String fullName;
@Attribute(name = "givenName")
private String firstName;
@Attribute(name = "sn")
private String lastName;
@Attribute(name = "o")
private String organization;
@Attribute(name = "userPassword")
private String password;
// Getters & Setters
[...]
}
And basic methods of my repository :
public User findByUid(String uid) {
return ldapTemplate.findOne(query().where("uid").is(uid), User.class);
}
public void update(User user) {
ldapTemplate.update(user);
}
Everything works fine except for the password attribute. For example, if I change only the user first name, the password is also changed.
I want to know how to deal with an encoded password (using the SHA - Secure Hashing Algorithm).
I don't see any annotations allowing to specify the encoding method.
Do we have to deal with it manually?
Short version
@Attribute(name = "userPassword", type = Type.BINARY)
private byte[] password;
is the correct definition of your password attribute. This is because LDAP stores the password as binary too.
To provide a convenient way of interaction, you should modify the setter for password
public void setPassword(String password) {
this.password = password.getBytes(Charset.forName("UTF-8"));
}
Long version
The problem is your definition of userPassword
. It is a java.lang.String. And the Spring LDAP ODM Attribute annotation defaults to Type.STRING
Your LDAP gets the string as byte array and checks if it has a proper prefix (in our case {SSHA}
). If there is no prefix present it hashes the given string with its configured hash algorithm and stores it in the attribute as binary. Here lays the root cause. Your attribute definition differs. LDAP has a binary, you have a string.
When you read the entry again, to modify the first name, the password attribute gets read too. But, as it should be a string in the object, Spring converts the binary array to a string. This conversion is wrong, as it creates a string.
e.g.
- you put
test
in the password field of your entity object.
- Spring takes the string and sends it unmodified to the LDAP server.
- the server hashes the string and saves it as
{SSHA}H97JD...
- you read the entry again
spring gets a byte[] containing the ascii numbers representing the stored value
[123, 83, 83, 72, 65, 125, 72, 57, 55, 74, 68, ...]
a conversion to a string results in the following:
123,83,83,72,65,125,72,57,55,74,68,...
spring sets this string in your entity as password value
- you modify the first name
- spring takes the password string again and sends it as is to the server
- the servers prefix check indicates an unhashed password and applies the hash algorithm again on the string, because
123,83,
starts not with {SSHA}
- the server changes the password again.