/******************************************************************************* * Australian National University Data Commons * Copyright (C) 2013 The Australian National University * * This file is part of Australian National University Data Commons. * * Australian National University Data Commons 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 3 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. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package au.edu.anu.datacommons.ldap; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import javax.naming.AuthenticationException; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.xml.bind.v2.runtime.unmarshaller.Loader; import au.edu.anu.datacommons.properties.GlobalProps; import au.edu.anu.datacommons.services.UserResource; /** * LdapRequest * * Australian National University Data Commons * * This class wraps a request to be submitted to an LDAP server. URL and BaseDN values of LDAP server are retrieved from * the properties file. * * <pre> * Version Date Developer Description * 0.1 09/03/2012 Rahul Khanna (RK) Initial * </pre> */ public class LdapRequest { private static final Logger LOGGER = LoggerFactory.getLogger(LdapRequest.class); private static final String LdapUri = GlobalProps.getProperty(GlobalProps.PROP_LDAP_URI); private static final String LdapBaseDn = GlobalProps.getProperty(GlobalProps.PROP_LDAP_BASEDN); private static final String LdapContextFactoryName = "com.sun.jndi.ldap.LdapCtxFactory"; private static final int DEFAULT_MAX_SEARCH_RESULTS = 10; private static final Hashtable<String, String> authCtxEnv = new Hashtable<String, String>(); private static final Hashtable<String, String> searchCtxEnv = new Hashtable<String, String>(); private static final MessageFormat queryPartTemplate = new MessageFormat("({0}={1})"); private StringBuilder ldapQuery; private SearchControls ldapSearchControl; static { searchCtxEnv.put(Context.INITIAL_CONTEXT_FACTORY, LdapContextFactoryName); searchCtxEnv.put(Context.PROVIDER_URL, LdapUri); authCtxEnv.put(Context.INITIAL_CONTEXT_FACTORY, LdapContextFactoryName); authCtxEnv.put(Context.PROVIDER_URL, LdapUri); authCtxEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); } /** * LdapRequest * * Autralian National University Data Commons * * Constructor * * <pre> * Version Date Developer Description * 0.1 16/03/2012 Rahul Khanna (RK) Initial * </pre> */ public LdapRequest() { ldapQuery = new StringBuilder(); ldapSearchControl = new SearchControls(); } /** * setMaxResults * * Autralian National University Data Commons * * Sets the maximum number of search results that will be returned when the LDAP query is executed. * DEFAULT_MAX_SEARCH_RESULTS is used if not specified. * * <pre> * Version Date Developer Description * 0.1 16/03/2012 Rahul Khanna (RK) Initial * </pre> * * @param maxResults * Number of maximum search results accepted. */ public void setMaxResults(long maxResults) { ldapSearchControl.setCountLimit(maxResults); } /** * setQuery * * Autralian National University Data Commons * * Sets the query passed as the parameter to be run. * * <pre> * Version Date Developer Description * 0.1 16/03/2012 Rahul Khanna (RK) Initial * </pre> * * @param query * LDAP Query to be run */ public void setQuery(String query) { ldapQuery = new StringBuilder(query); } /** * search * * Autralian National University Data Commons * * Executes the LDAP query. Requires the LDAP query to be assigned. * * <pre> * Version Date Developer Description * 0.1 16/03/2012 Rahul Khanna (RK) Initial * </pre> * * @throws NamingException * When the LDAP query is either blank or invalid. */ public List<LdapPerson> search() throws NamingException { DirContext dirContext; NamingEnumeration<SearchResult> searchResults = null; List<LdapPerson> people = new ArrayList<LdapPerson>(); // Perform search. dirContext = new InitialDirContext(searchCtxEnv); searchResults = dirContext.search(LdapBaseDn, ldapQuery.toString(), ldapSearchControl); // Iterate through the results returned, stored each one in results. while (searchResults.hasMore()) { LdapPerson ldapPerson = new LdapPerson(searchResults.next().getAttributes()); people.add(ldapPerson); } try { dirContext.close(); } catch (NamingException e) { LOGGER.warn("Unable to close directory context after LDAP query {}", ldapQuery.toString()); } return people; } /** * searchUniId * * Autralian National University Data Commons * * A method that searches for the specified Uni Id in the LDAP and returns an LdapPerson object with the entry * details. Attributes to be retrieved are specified in properties file. * * @param uniId * A valid ANU university ID. * @return An LdapPerson object containing attributes of the person whose Uni Id was provided as parameter. * @throws Exception */ public LdapPerson searchUniId(String uniId) throws Exception { DirContext dirContext; NamingEnumeration<SearchResult> searchResults = null; ldapSearchControl.setCountLimit(DEFAULT_MAX_SEARCH_RESULTS); ldapSearchControl.setReturningAttributes(GlobalProps.getProperty(GlobalProps.PROP_LDAP_ATTRLIST).split(",")); ldapSearchControl.setTimeLimit(10000); // ldapQuery = "(uid=[uniId])". Where uniId is the parameter passed to this function. ldapQuery = new StringBuilder(); ldapQuery.append("("); ldapQuery.append(GlobalProps.getProperty(GlobalProps.PROP_LDAPATTR_UNIID)); ldapQuery.append("="); ldapQuery.append(uniId); ldapQuery.append(")"); try { dirContext = new InitialDirContext(searchCtxEnv); searchResults = dirContext.search(LdapBaseDn, ldapQuery.toString(), ldapSearchControl); } catch (NamingException e) { // TODO Auto-generated catch block e.printStackTrace(); } // If no results returned or multiple results returned, throw exception. SearchResult firstResult = searchResults.next(); if (firstResult == null || searchResults.hasMore()) { throw new Exception("No results or multiple results returned for Uni Id. Only one expected."); } return new LdapPerson(firstResult.getAttributes()); } /** * authenticate * * Autralian National University Data Commons * * Queries the LDAP server if a set of username and password are valid. * * <pre> * Version Date Developer Description * 0.1 16/03/2012 Rahul Khanna (RK) Initial. * </pre> * * @param username * Username * @param password * Password * @return true if credentials are correct, false otherwise. */ public boolean authenticate(String username, String password) { boolean isAuthenticated = false; DirContext dirContext = null; // Parameter password must never be blank. A user gets authenticated if password is blank. if (password.equals("")) { return isAuthenticated; } // Include username and password in the directory context environment. authCtxEnv.put(Context.SECURITY_PRINCIPAL, GlobalProps.getProperty(GlobalProps.PROP_LDAPATTR_UNIID) + "=" + username + ", " + GlobalProps.getProperty(GlobalProps.PROP_LDAP_BASEDN)); authCtxEnv.put(Context.SECURITY_CREDENTIALS, password); try { dirContext = new InitialDirContext(authCtxEnv); // If no exception thrown then credentials are valid. isAuthenticated = true; } catch (AuthenticationException e) { // Invalid credentials isAuthenticated = false; } catch (NamingException e) { // Can't be sure if credentials are valid when this exception's thrown. Flagged false to be safe. isAuthenticated = false; } finally { if (dirContext != null) { try { dirContext.close(); } catch (NamingException e) { dirContext = null; } } } return isAuthenticated; } String createQueryPart(String attributeName, String attributeValue) { return queryPartTemplate.format(new Object[] {attributeName, attributeValue}); } String createQueryGroup(String operator, String... queryParts) { StringBuilder queryGroup = new StringBuilder("("); queryGroup.append(operator); for (int i = 0; i < queryParts.length; i++) { queryGroup.append(queryParts[i]); } queryGroup.append(")"); return queryGroup.toString(); } }