/*
* Copyright (c) 2016 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.obiba.shiro.realm;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.naming.AuthenticationException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* LDAP {@link JndiLdapRealm} implementation that supports authorization.
* <p>
* Here is a sample config for shiro.ini for a basic OpenLDAP config:
* <pre>
* # LDAP realm configuration
* ldapRealm = org.obiba.security.realm.LdapRealm
* ldapRealm.userDnTemplate = uid={0},ou=people,dc=example,dc=com
* ldapRealm.contextFactory.url = ldap://localhost
* ldapRealm.contextFactory.authenticationMechanism = none
* ldapRealm.contextFactory.systemUsername = admin
* ldapRealm.contextFactory.systemPassword = secret
* ldapRealm.searchBase = dc=example,dc=com
* ldapRealm.userGroupAttribute = memberUid
* ldapRealm.groupNameAttribute = cn
* # Specify mapping between LDAP groups and your application roles
* ldapRealm.groupRolesMap = group1:SYSTEM_ADMINISTRATOR, group2:PARTICIPANT_MANAGER, group3:DATA_COLLECTION_OPERATOR
* </pre>
* </p>
*/
@SuppressWarnings("UnusedDeclaration")
public class LdapRealm extends JndiLdapRealm {
private final static Logger logger = LoggerFactory.getLogger(LdapRealm.class);
private String searchBase;
private String userGroupAttribute;
private String groupNameAttribute;
private Map<String, String> groupRolesMap;
/**
* Get groups from LDAP.
*
* @param principals the principals of the Subject whose AuthenticationInfo should be queried from the LDAP server.
* @param ldapContextFactory factory used to retrieve LDAP connections.
* @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server.
* @throws NamingException if any LDAP errors occur during the search.
*/
@Override
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
LdapContextFactory ldapContextFactory) throws NamingException {
Set<String> roleNames = new HashSet<String>();
String username = (String) getAvailablePrincipal(principals);
LdapContext systemLdapCtx = null;
try {
systemLdapCtx = ldapContextFactory.getSystemLdapContext();
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<?> answer = systemLdapCtx.search(searchBase, userGroupAttribute + "=" + username, constraints);
while(answer.hasMore()) {
queryResult(roleNames, (SearchResult) answer.next());
}
} catch(AuthenticationException e) {
// do nothing as the principal was not authenticated on LDAP
} finally {
LdapUtils.closeContext(systemLdapCtx);
}
logger.debug("Role for {}: {}", username, roleNames);
return new SimpleAuthorizationInfo(roleNames);
}
private void queryResult(Collection<String> roleNames, SearchResult sr) throws NamingException {
for(NamingEnumeration<?> attributesEnum = sr.getAttributes().getAll(); attributesEnum.hasMore(); ) {
Attribute attr = (Attribute) attributesEnum.next();
if(attr.getID().equalsIgnoreCase(groupNameAttribute)) {
NamingEnumeration<?> e = attr.getAll();
while(e.hasMore()) {
String role = groupRolesMap.get(e.next());
if(role != null) roleNames.add(role);
}
}
}
}
public void setSearchBase(String searchBase) {
this.searchBase = searchBase;
}
public void setUserGroupAttribute(String userGroupAttribute) {
this.userGroupAttribute = userGroupAttribute;
}
public void setGroupNameAttribute(String groupNameAttribute) {
this.groupNameAttribute = groupNameAttribute;
}
/**
* Set mapping between LDAP groups and application roles
*
* @param groupRolesMap
*/
public void setGroupRolesMap(Map<String, String> groupRolesMap) {
this.groupRolesMap = groupRolesMap;
}
}