Java tutorial
/** * Licensed to Apereo under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * Apereo licenses this file to you 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 the following location: * * 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.apereo.services.persondir.support.ldap; import org.apache.commons.lang3.StringUtils; import org.apereo.services.persondir.IPersonAttributeDao; import org.apereo.services.persondir.IPersonAttributes; import org.apereo.services.persondir.support.AbstractQueryPersonAttributeDao; import org.apereo.services.persondir.support.CaseInsensitiveAttributeNamedPersonImpl; import org.apereo.services.persondir.support.CaseInsensitiveNamedPersonImpl; import org.apereo.services.persondir.support.QueryType; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.InitializingBean; import org.springframework.ldap.core.AttributesMapper; import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.filter.EqualsFilter; import org.springframework.ldap.filter.Filter; import org.springframework.ldap.filter.LikeFilter; import org.springframework.util.Assert; import javax.naming.directory.SearchControls; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * LDAP implementation of {@link IPersonAttributeDao}. * * In the case of multi valued attributes a {@link java.util.List} is set as the value. * * <br> * <br> * Configuration: * <table border="1" summary=""> * <tr> * <th align="left">Property</th> * <th align="left">Description</th> * <th align="left">Required</th> * <th align="left">Default</th> * </tr> * <tr> * <td align="right" valign="top">searchControls</td> * <td> * Set the {@link SearchControls} used for executing the LDAP query. * </td> * <td valign="top">No</td> * <td valign="top">Default instance with SUBTREE scope.</td> * </tr> * <tr> * <td align="right" valign="top">baseDN</td> * <td> * The base DistinguishedName to use when executing the query filter. * </td> * <td valign="top">No</td> * <td valign="top">""</td> * </tr> * <tr> * <td align="right" valign="top">contextSource</td> * <td> * A {@link ContextSource} from the Spring-LDAP framework. Provides a DataSource * style object that this DAO can retrieve LDAP connections from. * </td> * <td valign="top">Yes</td> * <td valign="top">null</td> * </tr> * <tr> * <td align="right" valign="top">setReturningAttributes</td> * <td> * If the ldap attributes set in the ldapAttributesToPortalAttributes Map should be copied * into the {@link SearchControls#setReturningAttributes(String[])}. Setting this helps reduce * wire traffic of ldap queries. * </td> * <td valign="top">No</td> * <td valign="top">true</td> * </tr> * <tr> * <td align="right" valign="top">queryType</td> * <td> * How multiple attributes in a query should be concatenated together. The other option is OR. * </td> * <td valign="top">No</td> * <td valign="top">AND</td> * </tr> * <tr> * <td align="right" valign="top">queryTemplate</td> * <td> * Optional wrapper template for the generated part of the query. Use {0} as a placeholder for where the generated query should be inserted. * </td> * <td valign="top">No</td> * <td valign="top">null</td> * </tr> * </table> * * @author andrew.petro@yale.edu * @author Eric Dalquist * @since uPortal 2.5 */ public class LdapPersonAttributeDao extends AbstractQueryPersonAttributeDao<LogicalFilterWrapper> implements InitializingBean { private static final Pattern QUERY_PLACEHOLDER = Pattern.compile("\\{0\\}"); private final static AttributesMapper MAPPER = new AttributeMapAttributesMapper(); /** * The LdapTemplate to use to execute queries on the DirContext */ private LdapTemplate ldapTemplate = null; private String baseDN = ""; private String queryTemplate = null; private ContextSource contextSource = null; private SearchControls searchControls = new SearchControls(); private final boolean setReturningAttributes = true; private QueryType queryType = QueryType.AND; public LdapPersonAttributeDao() { this.searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); this.searchControls.setReturningObjFlag(false); } /* (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override public void afterPropertiesSet() throws Exception { final Map<String, Set<String>> resultAttributeMapping = this.getResultAttributeMapping(); if (this.setReturningAttributes && resultAttributeMapping != null) { this.searchControls.setReturningAttributes( resultAttributeMapping.keySet().toArray(new String[resultAttributeMapping.size()])); } if (this.contextSource == null) { throw new BeanCreationException("contextSource must be set"); } } /* (non-Javadoc) * @see org.jasig.services.persondir.support.AbstractQueryPersonAttributeDao#appendAttributeToQuery(java.lang.Object, java.lang.String, java.util.List) */ @Override protected LogicalFilterWrapper appendAttributeToQuery(LogicalFilterWrapper queryBuilder, final String dataAttribute, final List<Object> queryValues) { if (queryBuilder == null) { queryBuilder = new LogicalFilterWrapper(this.queryType); } for (final Object queryValue : queryValues) { final String queryValueString = queryValue == null ? null : queryValue.toString(); if (StringUtils.isNotBlank(queryValueString)) { final Filter filter; if (!queryValueString.contains("*")) { filter = new EqualsFilter(dataAttribute, queryValueString); } else { filter = new LikeFilter(dataAttribute, queryValueString); } queryBuilder.append(filter); } } return queryBuilder; } /* (non-Javadoc) * @see org.jasig.services.persondir.support.AbstractQueryPersonAttributeDao#getPeopleForQuery(java.lang.Object, java.lang.String) */ @Override protected List<IPersonAttributes> getPeopleForQuery(final LogicalFilterWrapper queryBuilder, final String queryUserName) { final String generatedLdapQuery = queryBuilder.encode(); //If no query is generated return null since the query cannot be run if (StringUtils.isBlank(generatedLdapQuery)) { return null; } //Insert the generated query into the template if it is configured final String ldapQuery; if (this.queryTemplate == null) { ldapQuery = generatedLdapQuery; } else { final Matcher queryMatcher = QUERY_PLACEHOLDER.matcher(this.queryTemplate); ldapQuery = queryMatcher.replaceAll(generatedLdapQuery); if (logger.isDebugEnabled()) { logger.debug("Final ldapQuery after applying queryTemplate: '" + ldapQuery + "'"); } } //Execute the query @SuppressWarnings("unchecked") final List<Map<String, List<Object>>> queryResults = this.ldapTemplate.search(this.baseDN, ldapQuery, this.searchControls, MAPPER); final List<IPersonAttributes> peopleAttributes = new ArrayList<>(queryResults.size()); for (final Map<String, List<Object>> queryResult : queryResults) { final IPersonAttributes person; // Choose a username from the best available option final String userNameAttribute = this.getConfiguredUserNameAttribute(); if (this.isUserNameAttributeConfigured() && queryResult.containsKey(userNameAttribute)) { // Option #1: An attribute is named explicitly in the config, // and that attribute is present in the results from LDAP; use it person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, queryResult); } else if (queryUserName != null) { // Option #2: Use the userName attribute provided in the query // parameters. (NB: I'm not entirely sure this choice is // preferable to Option #3. Keeping it because it most closely // matches the legacy behavior there the new option -- Option #1 // -- doesn't apply. ~drewwills) person = new CaseInsensitiveNamedPersonImpl(queryUserName, queryResult); } else { // Option #3: Create the IPersonAttributes doing a best-guess // at a userName attribute person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, queryResult); } peopleAttributes.add(person); } return peopleAttributes; } /** * @see javax.naming.directory.SearchControls#getTimeLimit() * @return time limit * @deprecated Set the property on the {@link SearchControls} and set that via {@link #setSearchControls(SearchControls)} */ @Deprecated public int getTimeLimit() { return this.searchControls.getTimeLimit(); } /** * @see javax.naming.directory.SearchControls#setTimeLimit(int) * @param ms time limit in milliseconds * @deprecated */ @Deprecated public void setTimeLimit(final int ms) { this.searchControls.setTimeLimit(ms); } /** * @return The base distinguished name to use for queries. */ public String getBaseDN() { return this.baseDN; } /** * @param baseDN The base distinguished name to use for queries. */ public void setBaseDN(String baseDN) { if (baseDN == null) { baseDN = ""; } this.baseDN = baseDN; } /** * @return The ContextSource to get DirContext objects for queries from. */ public ContextSource getContextSource() { return this.contextSource; } /** * @param contextSource The ContextSource to get DirContext objects for queries from. */ public synchronized void setContextSource(final ContextSource contextSource) { Assert.notNull(contextSource, "contextSource can not be null"); this.contextSource = contextSource; this.ldapTemplate = new LdapTemplate(this.contextSource); } /** * Sets the LdapTemplate, and thus the ContextSource (implicitly). * * @param ldapTemplate the LdapTemplate to query the LDAP server from. CANNOT be NULL. */ public synchronized void setLdapTemplate(final LdapTemplate ldapTemplate) { Assert.notNull(ldapTemplate, "ldapTemplate cannot be null"); this.ldapTemplate = ldapTemplate; this.contextSource = this.ldapTemplate.getContextSource(); } /** * @return Search controls to use for LDAP queries */ public SearchControls getSearchControls() { return this.searchControls; } /** * @param searchControls Search controls to use for LDAP queries */ public void setSearchControls(final SearchControls searchControls) { Assert.notNull(searchControls, "searchControls can not be null"); this.searchControls = searchControls; } /** * @return the queryType */ public QueryType getQueryType() { return queryType; } /** * Type of logical operator to use when joining WHERE clause components * * @param queryType the queryType to set */ public void setQueryType(final QueryType queryType) { this.queryType = queryType; } public String getQueryTemplate() { return this.queryTemplate; } /** * Optional wrapper template for the generated part of the query. Use {0} as a placeholder for where the generated query should be inserted. * * @param queryTemplate query template */ public void setQueryTemplate(final String queryTemplate) { this.queryTemplate = queryTemplate; } }