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?
@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(StandardCharsets.UTF_8);
}
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 a 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, ...]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With