/*******************************************************************************
* 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.naming.directory.SearchControls;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.util.Assert;
/**
* Populates the user's authorization groups based on group membership.
* @author noak
* @since 2.3.0
*
*/
public class CustomLdapAuthGroupsPopulator implements LdapAuthGroupsPopulator {
private static final Log logger = LogFactory.getLog(CustomLdapAuthGroupsPopulator.class);
private SpringSecurityLdapTemplate ldapTemplate;
/**
* Controls used to determine whether group searches should be performed over the full sub-tree from the
* base DN. Modified by searchSubTree property
*/
private SearchControls searchControls = new SearchControls();
/**
* The ID of the attribute which contains the authorization group name.
*/
private String groupNameAttribute = "cn";
/**
* The base DN from which the search for group membership should be performed.
*/
private String groupSearchBase;
/**
* The pattern to be used for the user search. {0} is the user's DN
*/
private String groupSearchFilter = "(member={0})";
/**
* Attributes of the User's LDAP Object that contain role name information.
*/
// private String[] userRoleAttributes = null;
/**
* Constructor.
*
* @param contextSource supplies the contexts used to search for group membership.
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the
* context factory.
*/
public CustomLdapAuthGroupsPopulator(final ContextSource contextSource, final String groupSearchBase) {
Assert.notNull(contextSource, "contextSource must not be null");
ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
ldapTemplate.setSearchControls(searchControls);
setGroupSearchBase(groupSearchBase);
}
/**
* Obtains the authorization groups for the user.
*
* @param user the user who's authorities are required
* @param username the user name
* @return the authorization groups of the given user.
*/
public final Collection<String> getAuthGroups(final DirContextOperations user, final String username) {
String userDn = user.getNameInNamespace();
if (logger.isDebugEnabled()) {
logger.debug("Getting authorization groups for user " + userDn);
}
Set<String> authGroups = getAuthGroups(userDn, username);
List<String> result = new ArrayList<String>(authGroups.size());
result.addAll(authGroups);
return result;
}
/**
* Obtains the authorization groups for the user.
*
* @param userDn The user DN
* @param username The user name
* @return Authorization groups
*/
public Set<String> getAuthGroups(final String userDn, final String username) {
if (getGroupSearchBase() == null) {
return Collections.emptySet();
}
if (logger.isDebugEnabled()) {
logger.debug("Searching for authorization groups for user '" + username + "', DN = " + "'" + userDn
+ "', with filter " + groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
}
Set<String> authGroups = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter,
new String[]{userDn, username}, groupNameAttribute);
if (logger.isDebugEnabled()) {
logger.debug("Authorization groups from search: " + authGroups);
}
return authGroups;
}
protected ContextSource getContextSource() {
return ldapTemplate.getContextSource();
}
/**
* Set the group search base (name to search under).
*
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the context
* factory.
*/
private void setGroupSearchBase(final String groupSearchBase) {
Assert.notNull(groupSearchBase, "The groupSearchBase (name to search under), must not be null.");
this.groupSearchBase = groupSearchBase;
if (groupSearchBase.length() == 0) {
logger.info("groupSearchBase is empty. Searches will be performed from the context source base");
}
}
protected String getGroupSearchBase() {
return groupSearchBase;
}
/**
* Sets the attribute which contains the authorization group name.
* @param groupNameAttribute The attribute which contains the authorization group name.
*/
public void setGroupNameAttribute(final String groupNameAttribute) {
Assert.notNull(groupNameAttribute, "groupNameAttribute must not be null");
this.groupNameAttribute = groupNameAttribute;
}
/**
* Sets the pattern to be used for the user search.
* @param groupSearchFilter The pattern to be used for the user search.
*/
public void setGroupSearchFilter(final String groupSearchFilter) {
Assert.notNull(groupSearchFilter, "groupSearchFilter must not be null");
this.groupSearchFilter = groupSearchFilter;
}
/**
* If set to true, a subtree scope search will be performed. If false a single-level search is used.
*
* @param searchSubtree set to true to enable searching of the entire tree below the <tt>groupSearchBase</tt>.
*/
public void setSearchSubtree(final boolean searchSubtree) {
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
searchControls.setSearchScope(searchScope);
}
/**
* Sets the corresponding property on the underlying template, avoiding specific issues with Active Directory.
*
* @see LdapTemplate#setIgnoreNameNotFoundException(boolean)
*/
/*public void setIgnorePartialResultException(final boolean ignore) {
ldapTemplate.setIgnorePartialResultException(ignore);
}*/
}