/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package org.cloudifysource.securityldap; import java.util.Collection; import java.util.logging.Logger; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.ldap.ppolicy.PasswordPolicyControl; import org.springframework.security.ldap.ppolicy.PasswordPolicyResponseControl; import org.springframework.util.Assert; /** * A custom implementation for Spring's UserDetailsContextMapper. * The context mapper used by the LDAP authentication provider to create an LDAP user object with both groups and roles. * * @author Noak * @since 2.3.0 * */ public class CustomLdapUserDetailsMapper { //~ Instance fields ================================================================================================ private Logger logger = java.util.logging.Logger.getLogger(CustomLdapUserDetailsMapper.class.getName()); private String passwordAttributeName = "userPassword"; private String[] roleAttributes = null; private boolean convertToUpperCase = true; /** * * @param ctx User object * @param username the user name * @param authorities A collection of authorities * @param authGroups A collection of authentication groups * @return UserDetails */ public ExtendedLdapUserDetailsImpl mapUserFromContext(final DirContextOperations ctx, final String username, final Collection<? extends GrantedAuthority> authorities, final Collection<String> authGroups) { String dn = ctx.getNameInNamespace(); logger.finest("CustomLdapUserDetailsMapper: mapUserFromContext"); logger.fine("Mapping user details from context with DN: " + dn); ExtendedLdapUserDetailsImpl.ExtendedEssence essence = new ExtendedLdapUserDetailsImpl.ExtendedEssence(); essence.setDn(dn); Object passwordValue = ctx.getObjectAttribute(passwordAttributeName); if (passwordValue != null) { essence.setPassword(mapPassword(passwordValue)); } essence.setUsername(username); // Map the roles for (int i = 0; (roleAttributes != null) && (i < roleAttributes.length); i++) { String[] rolesForAttribute = ctx.getStringAttributes(roleAttributes[i]); if (rolesForAttribute == null) { logger.fine("Couldn't read role attribute '" + roleAttributes[i] + "' for user " + dn); continue; } for (String roleForAttribute : rolesForAttribute) { GrantedAuthority authority = createAuthority(roleForAttribute); if (authority != null) { essence.addAuthority(authority); } } } // Add the supplied authorities for (GrantedAuthority authority : authorities) { essence.addAuthority(authority); } // Add the supplied authorization groups for (String authGroup : authGroups) { essence.addAuthGroup(authGroup); } // Check for PPolicy data PasswordPolicyResponseControl ppolicy = (PasswordPolicyResponseControl) ctx. getObjectAttribute(PasswordPolicyControl.OID); if (ppolicy != null) { essence.setTimeBeforeExpiration(ppolicy.getTimeBeforeExpiration()); essence.setGraceLoginsRemaining(ppolicy.getGraceLoginsRemaining()); } return essence.createUserDetails(); } /** * Not implemented. * @param user . * @param ctx . */ public void mapUserToContext(final UserDetails user, final DirContextAdapter ctx) { throw new UnsupportedOperationException("LdapUserDetailsMapper only supports reading from a context. Please" + "use a subclass if mapUserToContext() is required."); } /** * Extension point to allow customized creation of the user's password from * the attribute stored in the directory. * * @param passwordValue the value of the password attribute * @return a String representation of the password. */ protected String mapPassword(final Object passwordValue) { String passwordString; if (passwordValue instanceof String) { passwordString = (String) passwordValue; } else { // Assume it's binary passwordString = new String((byte[]) passwordValue); } return passwordString; } /** * Creates a GrantedAuthority from a role attribute. Override to customize * authority object creation. * <p> * The default implementation converts string attributes to roles, making use of the <tt>rolePrefix</tt> * and <tt>convertToUpperCase</tt> properties. Non-String attributes are ignored. * </p> * * @param role the attribute returned from * @return the authority to be added to the list of authorities for the user, or null * if this attribute should be ignored. */ protected GrantedAuthority createAuthority(final Object role) { String roleString; if (role instanceof String) { roleString = (String) role; if (convertToUpperCase) { roleString = roleString.toUpperCase(); } return new GrantedAuthorityImpl(roleString); } return null; } /** * Determines whether role field values will be converted to upper case when loaded. * The default is true. * * @param convertToUpperCase true if the roles should be converted to upper case. */ public void setConvertToUpperCase(final boolean convertToUpperCase) { this.convertToUpperCase = convertToUpperCase; } /** * The name of the attribute which contains the user's password. * Defaults to "userPassword". * * @param passwordAttributeName the name of the attribute */ public void setPasswordAttributeName(final String passwordAttributeName) { this.passwordAttributeName = passwordAttributeName; } /** * The names of any attributes in the user's entry which represent application * roles. These will be converted to <tt>GrantedAuthority</tt>s and added to the * list in the returned LdapUserDetails object. The attribute values must be Strings by default. * * @param roleAttributes the names of the role attributes. */ public void setRoleAttributes(final String[] roleAttributes) { Assert.notNull(roleAttributes, "roleAttributes array cannot be null"); this.roleAttributes = roleAttributes; } }