/* ====================================================================
*
* Copyright (C) 2014 GeoSolutions S.A.S.
* http://www.geo-solutions.it
*
* GPLv3 + Classpath exception
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*
* ====================================================================
*
* This software consists of voluntary contributions made by developers
* of GeoSolutions. For more information on GeoSolutions, please see
* <http://www.geo-solutions.it/>.
*
*/
package it.geosolutions.geostore.services.rest.security;
import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper;
import java.util.HashSet;
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.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.util.Assert;
/**
* @author alessio.fabiani
*
*/
public class GeoStoreLdapAuthoritiesPopulator extends
DefaultLdapAuthoritiesPopulator implements GroupsRolesService {
private static final Log logger = LogFactory.getLog(GeoStoreLdapAuthoritiesPopulator.class);
/**
* Template that will be used for searching
*/
private final 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 final SearchControls searchControls = new SearchControls();
/**
* The ID of the attribute which contains the role name for a group
*/
private String groupRoleAttribute = "cn";
/**
* The base DN from which the search for group membership should be performed
*/
private String groupSearchBase;
private String roleSearchBase;
/**
* The pattern to be used for the user search. {0} is the user's DN
*/
private String groupSearchFilter = "(member={0})";
private String roleSearchFilter = "(member={0})";
private String allGroupsSearchFilter = "(objectClass=group)";
private String allRolesSearchFilter = "(objectClass=group)";
/**
* The role prefix that will be prepended to each role name
*/
private String rolePrefix = "ROLE_";
/**
* Should we convert the role name to uppercase
*/
private boolean convertToUpperCase = true;
private GrantedAuthoritiesMapper authoritiesMapper = null;
/**
* @param contextSource
* @param groupSearchBase
*/
public GeoStoreLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase,
String roleSearchBase) {
super(contextSource, groupSearchBase);
Assert.notNull(contextSource, "contextSource must not be null");
ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
ldapTemplate.setSearchControls(searchControls);
this.groupSearchBase = groupSearchBase;
if (groupSearchBase == null) {
logger.info("groupSearchBase is null. No group search will be performed.");
} else if (groupSearchBase.length() == 0) {
logger.info("groupSearchBase is empty. Searches will be performed from the context source base");
}
this.roleSearchBase = roleSearchBase;
if (roleSearchBase == null) {
logger.info("roleSearchBase is null. No group search will be performed.");
} else if (roleSearchBase.length() == 0) {
logger.info("roleSearchBase is empty. Searches will be performed from the context source base");
}
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
@Override
public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
return getGroupsOrRoles(userDn, username, true, true);
}
private Set<GrantedAuthority> getGroupsOrRoles(String userDn, String username, boolean groups, boolean roles) {
if (roleSearchBase == null && groupSearchBase == null) {
return new HashSet<GrantedAuthority>();
}
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
String[] searchParams = username == null ? new String[] {} : new String[] {userDn, username};
if(roles) {
// Searching for ROLES
if (logger.isDebugEnabled()) {
logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
+ roleSearchFilter + " in search base '" + roleSearchBase + "'");
}
String[] rolesRoots = roleSearchBase.split(",");
String filter = username == null ? allRolesSearchFilter : roleSearchFilter;
for(String rolesRoot : rolesRoots) {
addAuthorities(searchParams, authorities, rolesRoot, filter, rolePrefix);
}
}
if(groups) {
// Searching for Groups
if (logger.isDebugEnabled()) {
logger.debug("Searching for groups for user '" + username + "', DN = " + "'" + userDn + "', with filter "
+ groupSearchFilter + " in search base '" + groupSearchBase + "'");
}
String[] groupsRoots = groupSearchBase.split(",");
String filter = username == null ? allGroupsSearchFilter : groupSearchFilter;
for(String groupsRoot : groupsRoots) {
addAuthorities(searchParams, authorities, groupsRoot, filter, null);
}
}
if(authoritiesMapper != null) {
return new HashSet<GrantedAuthority>(authoritiesMapper.mapAuthorities(authorities));
}
return authorities;
}
public Set<GrantedAuthority> getAllGroups() {
return getGroupsOrRoles(null, null, true, false);
}
public Set<GrantedAuthority> getAllRoles() {
return getGroupsOrRoles(null, null, false, true);
}
private void addAuthorities(String[] params, Set<GrantedAuthority> authorities,
String root, String filter, String authorityPrefix) {
Set<String> ldapAuthorities = ldapTemplate.searchForSingleAttributeValues(root, filter,
params, groupRoleAttribute);
if (logger.isDebugEnabled()) {
logger.debug("Authorities from search: " + ldapAuthorities);
}
for (String authority : ldapAuthorities) {
addAuthority(authorities, authorityPrefix, authority);
}
}
private void addAuthority(Set<GrantedAuthority> authorities, String authorityPrefix,
String authority) {
if (convertToUpperCase) {
authority = authority.toUpperCase();
}
String prefix = (authorityPrefix != null && !authority.startsWith(authorityPrefix) ? authorityPrefix : "");
authorities.add(new GrantedAuthorityImpl(prefix + authority));
}
@Override
public void setConvertToUpperCase(boolean convertToUpperCase) {
super.setConvertToUpperCase(convertToUpperCase);
this.convertToUpperCase = convertToUpperCase;
}
@Override
public void setGroupRoleAttribute(String groupRoleAttribute) {
super.setGroupRoleAttribute(groupRoleAttribute);
this.groupRoleAttribute = groupRoleAttribute;
}
@Override
public void setGroupSearchFilter(String groupSearchFilter) {
super.setGroupSearchFilter(groupSearchFilter);
this.groupSearchFilter = groupSearchFilter;
}
public void setRoleSearchFilter(String roleSearchFilter) {
this.roleSearchFilter = roleSearchFilter;
}
@Override
public void setRolePrefix(String rolePrefix) {
super.setRolePrefix(rolePrefix);
this.rolePrefix = rolePrefix;
}
}