package com.hwlcn.security.realm.ldap; import com.hwlcn.security.authc.AuthenticationException; import com.hwlcn.security.authc.AuthenticationInfo; import com.hwlcn.security.authc.AuthenticationToken; import com.hwlcn.security.authc.SimpleAuthenticationInfo; import com.hwlcn.security.authc.credential.AllowAllCredentialsMatcher; import com.hwlcn.security.authz.AuthorizationException; import com.hwlcn.security.authz.AuthorizationInfo; import com.hwlcn.security.ldap.UnsupportedAuthenticationMechanismException; import com.hwlcn.security.realm.AuthorizingRealm; import com.hwlcn.security.subject.PrincipalCollection; import com.hwlcn.security.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.naming.AuthenticationNotSupportedException; import javax.naming.NamingException; import javax.naming.ldap.LdapContext; public class JndiLdapRealm extends AuthorizingRealm { private static final Logger log = LoggerFactory.getLogger(JndiLdapRealm.class); private static final String USERDN_SUBSTITUTION_TOKEN = "{0}"; private String userDnPrefix; private String userDnSuffix; private LdapContextFactory contextFactory; public JndiLdapRealm() { setCredentialsMatcher(new AllowAllCredentialsMatcher()); setAuthenticationTokenClass(AuthenticationToken.class); this.contextFactory = new JndiLdapContextFactory(); } protected String getUserDnPrefix() { return userDnPrefix; } protected String getUserDnSuffix() { return userDnSuffix; } public void setUserDnTemplate(String template) throws IllegalArgumentException { if (!StringUtils.hasText(template)) { String msg = "User DN template cannot be null or empty."; throw new IllegalArgumentException(msg); } int index = template.indexOf(USERDN_SUBSTITUTION_TOKEN); if (index < 0) { String msg = "User DN template must contain the '" + USERDN_SUBSTITUTION_TOKEN + "' replacement token to understand where to " + "insert the runtime authentication principal."; throw new IllegalArgumentException(msg); } String prefix = template.substring(0, index); String suffix = template.substring(prefix.length() + USERDN_SUBSTITUTION_TOKEN.length()); if (log.isDebugEnabled()) { log.debug("Determined user DN prefix [{}] and suffix [{}]", prefix, suffix); } this.userDnPrefix = prefix; this.userDnSuffix = suffix; } public String getUserDnTemplate() { return getUserDn(USERDN_SUBSTITUTION_TOKEN); } protected String getUserDn(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); } sb.append(principal); if (suffixLength > 0) { sb.append(suffix); } return sb.toString(); } public void setContextFactory(LdapContextFactory contextFactory) { this.contextFactory = contextFactory; } public LdapContextFactory getContextFactory() { return this.contextFactory; } protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info; try { info = queryForAuthenticationInfo(token, getContextFactory()); } catch (AuthenticationNotSupportedException e) { String msg = "Unsupported configured authentication mechanism"; throw new UnsupportedAuthenticationMechanismException(msg, e); } catch (javax.naming.AuthenticationException e) { throw new AuthenticationException("LDAP authentication failed.", e); } catch (NamingException e) { String msg = "LDAP naming error while attempting to authenticate user."; throw new AuthenticationException(msg, e); } return info; } protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { AuthorizationInfo info; try { info = queryForAuthorizationInfo(principals, getContextFactory()); } catch (NamingException e) { String msg = "LDAP naming error while attempting to retrieve authorization for user [" + principals + "]."; throw new AuthorizationException(msg, e); } return info; } protected Object getLdapPrincipal(AuthenticationToken token) { Object principal = token.getPrincipal(); if (principal instanceof String) { String sPrincipal = (String) principal; return getUserDn(sPrincipal); } return principal; } protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException { Object principal = token.getPrincipal(); Object credentials = token.getCredentials(); log.debug("Authenticating user '{}' through LDAP", principal); principal = getLdapPrincipal(token); LdapContext ctx = null; try { ctx = ldapContextFactory.getLdapContext(principal, credentials); return createAuthenticationInfo(token, principal, credentials, ctx); } finally { LdapUtils.closeContext(ctx); } } @SuppressWarnings({"UnusedDeclaration"}) protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal, Object ldapCredentials, LdapContext ldapContext) throws NamingException { return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName()); } protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, LdapContextFactory ldapContextFactory) throws NamingException { return null; } }