Penrose supports authentication (LDAP bind operation) against password stored in database or LDAP. In some cases the password is retrievable, so Penrose can compare it directly. In other cases, the password is not retrievable, for instance in LDAP and Active Directory. In that case Penrose will authenticate to the datasource on behalf of the client.
The following are examples on how to configure password mapping when the password is retrievable.
Plain Text Password
If the password is stored as plain text, you can directly map the corresponding field to the userPassword attribute.
<entry ...> <at name="userPassword"> <expression>customers.password</expression> </at> <source name="customers"> <field name="password"> <expression>userPassword</expression> </field> </source> </entry>
Encrypted Password
In LDAP, encrypted password are shown in this format: {encryptionMethod}passwordDigest. Please refer to Bouncy Castle
for supported encryption methods. The password digest is encoded in Base64 format.
If the password is already encrypted in the datasource and encoded in Base64 format, we just need to append the encryption tag in front of it.
<at name="userPassword"> <expression> if (customers.encPassword == void || customers.encPassword == null) return null; return "{SHA}"+customers.encPassword; </expression> </at>
When storing a new password, the client can supply the new password either in plain text or encrypted. If it's not encrypted, we need to encrypt the password first. If it's already encrypted, it will only store the password digest. The PasswordUtil
provides methods to simplify this operation.
<field name="encPassword"> <expression> import org.safehaus.penrose.util.*; if (userPassword == void || userPassword == null) return null; String method = PasswordUtil.getEncryptionMethod(userPassword); String password; if (method == null) { byte[] bytes = PasswordUtil.encrypt("SHA", userPassword); password = PasswordUtil.encode("Base64", bytes); } else if ("SHA".equals(method)) { password = PasswordUtil.getEncryptedPassword(userPassword); } else { throw new Exception("Unsupported encryption: "+method); } return password; </expression> </field>
Using Different Encoding
Sometimes the password digest has to be stored in the datasource using a different encoding. For instance Tomcat's JDBC Realm
, when used with encryption
the digested password need to be stored as plain text. The PasswordUtil supports an encoding called "BigInteger" that can produce the required format. The following script shows how to convert from BigInteger into Base64 during when reading the password.
<at name="userPassword"> <expression> import org.safehaus.penrose.util.*; if (customers.encPassword == void || customers.encPassword == null) return null; byte[] bytes = PasswordUtil.decode("BigInteger", customers.encPassword); return "{SHA}"+PasswordUtil.encode("Base64", bytes); </expression> </at>
When writing it back into the datasource, the password digest needs to be converted from Base64 back into BigInteger.
<field name="encPassword"> <expression> import org.safehaus.penrose.util.*; if (userPassword == void || userPassword == null) return null; String method = PasswordUtil.getEncryptionMethod(userPassword); byte[] bytes; if (method == null) { bytes = PasswordUtil.encrypt("SHA", userPassword); } else if ("SHA".equals(method)) { String password = PasswordUtil.getEncryptedPassword(userPassword); bytes = PasswordUtil.decode("Base64", password); } else { throw new Exception("Unsupported encryption: "+method); } return PasswordUtil.encode("BigInteger", bytes); </expression> </field>