I'm working on a webapplication using the following:
I'm trying to authenticate users via an LDAP realm.
I tested the login with JXplorer using the following values:
Now to my problem:
I have to authenticate the users not based on cn
but rather on uid
.
This wont work however.
Workflow suggested by LDAP-Admin at my company:
I have a system user which (as far as I understood) should connect to the LDAP-Server, searches the user based on the given uid
and returns the cn
for that user.
In a second step it should then be possible to authenticate the user with his cn
just like its done (successfully) in JXplorer.
My shiro.ini configuration looks like this for my test LDAP (not the one I'm trying to authenticate against now)
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = cn={0},ou=People,dc=maxcrc,dc=com
ldapRealm.contextFactory.url = ldap://localhost:389
The problem I'm facing right now is that I cannot find information about how to configure my shiro.ini file in a way to math the suggested workflow from above.
Question #1
Can I use the standard org.apache.shiro.realm.ldap.JndiLdapRealm
(how do I configure it)? Especially for the integration of the certificate I could not find anything appropriate.
Question #2
Do I need to create a custom JndiLdapRealm for this "2 step authentication" and how could such a Realm look like?
I found a solutiuon and that is to overload the getUserDN()
Method from JndiLdapRealm
It now looks like this:
/**
* Addition to standard shiro code
* User logs in with his {@link principal} (username) but login needs to be performed with his cn instead
* The addition is an ldap query to get the users cn from the ldap server and store it (instead of the given principal) in the return String
* @return UserDN with user uid (cn) instead of principal (username)
*/
@Override
protected String getUserDn( final String principal ) throws IllegalArgumentException, IllegalStateException {
if (!StringUtils.hasText(principal)) {
throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction.");
}
String prefix = getUserDnPrefix();
String suffix = getUserDnSuffix();
if (prefix == null && suffix == null) {
log.debug("userDnTemplate property has not been configured, indicating the submitted " +
"AuthenticationToken's principal is the same as the User DN. Returning the method argument " +
"as is.");
return principal;
}
int prefixLength = prefix != null ? prefix.length() : 0;
int suffixLength = suffix != null ? suffix.length() : 0;
StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength);
if (prefixLength > 0) {
sb.append(prefix);
}
/*############################################################################################
* ADDITION TO STANDARD SHIRO CODE
* User logs in with his {@link principal} (username) but login needs to be performed with his cn instead
* => translate username to cn
*/
AppSettings_Controler settings = Startup.getAppSettingsControler();
LDAP_Manager ldap_manager = new LDAP_Manager();
LdapContext ctx = ldap_manager.getLdapConnection();
String user_uid = "";
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
String[] attrIDs = {"cn"};
constraints.setReturningAttributes(attrIDs);
NamingEnumeration answer = null;
try{
answer = ctx.search(settings.get_ldap_search_dn_1(), settings.get_ldap_uname_field_name_1() + "=" + principal, constraints);
if (answer.hasMore()) {
Attributes attrs = ((SearchResult) answer.next()).getAttributes();
user_uid = attrs.get("cn").toString().substring(attrIDs[0].length() + 2);
} else {
throw new Exception("Invalid User");
}
} catch (Exception ex) {
// Error Handling & Fallback to my second LDAP-Server
try{
answer = ctx.search(settings.get_ldap_search_dn_2(), settings.get_ldap_uname_field_name_2() + "=" + principal, constraints);
if (answer.hasMore()) {
Attributes attrs = ((SearchResult) answer.next()).getAttributes();
user_uid = attrs.get("cn").toString().substring(attrIDs[0].length() + 2);
} else {
throw new Exception("Invalid User");
}
}catch (Exception exe) {
// Error Handling
}
}
try {
ctx.close();
} catch (NamingException ex) {
JndiLdapRealm_custom_error_logger.error("getUserDn(); Fehler beim schließen: ", ex);
}
//############################################################################################
//############################################################################################
// Shiro Standard Code -> add principal to String
//sb.append(principal);
/*############################################################################################
/*############################################################################################
* ALTERED CODE
* Instead -> Add user's cn to String
*/
sb.append(user_uid);
//############################################################################################
if (suffixLength > 0) {
sb.append(suffix);
}
return sb.toString();
}
This addition does an LDAP-Query to get the users uid (cn) based n the username provided. That way the login works with the user just entering his LDAP-Username withing my application and follows the workflow described above.
If you have any suggestions or comments don't hesitate to post them here ;)