/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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:
*
* 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.jasig.schedassist.impl.ldap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.naming.directory.SearchControls;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.schedassist.IDelegateCalendarAccountDao;
import org.jasig.schedassist.model.ICalendarAccount;
import org.jasig.schedassist.model.IDelegateCalendarAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.ldap.SizeLimitExceededException;
import org.springframework.ldap.TimeLimitExceededException;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.filter.LikeFilter;
import org.springframework.ldap.filter.OrFilter;
/**
* LDAP backed {@link IDelegateCalendarAccountDao} implementation.
* Returns "Resource" (or delegate) calendar accounts, e.g. {@link LDAPDelegateCalendarAccountImpl} instances.
*
* @author Nicholas Blair
*/
public class LDAPDelegateCalendarAccountDaoImpl implements
IDelegateCalendarAccountDao {
private static final String OBJECTCLASS = "objectclass";
private static final String WILDCARD = "*";
private final Log log = LogFactory.getLog(this.getClass());
private LdapOperations ldapTemplate;
private LDAPAttributesKey ldapAttributesKey = new LDAPAttributesKeyImpl();
private String baseDn = "o=isp";
private long searchResultsLimit = 25L;
private int searchTimeLimit = 10000;
private boolean treatOwnerAttributeAsDistinguishedName = true;
private boolean enforceSpecificObjectClass = false;
private String requiredObjectClass = "inetresource";
/**
* @param ldapTemplate the ldapTemplate to set
*/
@Autowired
public void setLdapTemplate(LdapOperations ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
/**
* @param ldapAttributesKey the ldapAttributesKey to set
*/
@Autowired
public void setLdapAttributesKey(LDAPAttributesKey ldapAttributesKey) {
this.ldapAttributesKey = ldapAttributesKey;
}
/**
* @param baseDn the baseDn to set
*/
@Value("${ldap.resourceAccountBaseDn:o=isp}")
public void setBaseDn(String baseDn) {
this.baseDn = baseDn;
}
/**
* @param searchResultsLimit the searchResultsLimit to set
*/
@Value("${ldap.searchResultsLimit:25}")
public void setSearchResultsLimit(long searchResultsLimit) {
this.searchResultsLimit = searchResultsLimit;
}
/**
* @param searchTimeLimit the searchTimeLimit to set (in milliseconds)
*/
@Value("${ldap.searchTimeLimitMillis:5000}")
public void setSearchTimeLimit(int searchTimeLimit) {
this.searchTimeLimit = searchTimeLimit;
}
/**
* @return the treatOwnerAttributeAsDistinguishedName
*/
public boolean isTreatOwnerAttributeAsDistinguishedName() {
return treatOwnerAttributeAsDistinguishedName;
}
/**
* @param treatOwnerAttributeAsDistinguishedName the treatOwnerAttributeAsDistinguishedName to set
*/
@Value("${ldap.treatResourceOwnerAttributeAsDistinguishedName:true}")
public void setTreatOwnerAttributeAsDistinguishedName(
boolean treatOwnerAttributeAsDistinguishedName) {
this.treatOwnerAttributeAsDistinguishedName = treatOwnerAttributeAsDistinguishedName;
}
/**
* @param enforceSpecificObjectClass the enforceSpecificObjectClass to set
*/
@Value("${ldap.resources.enforceSpecificObjectClass:false}")
public void setEnforceSpecificObjectClass(boolean enforceSpecificObjectClass) {
this.enforceSpecificObjectClass = enforceSpecificObjectClass;
}
/**
* @param requiredObjectClass the requiredObjectClass to set
*/
@Value("${ldap.resources.requiredObjectClass:inetresource}")
public void setRequiredObjectClass(String requiredObjectClass) {
this.requiredObjectClass = requiredObjectClass;
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#searchForDelegates(java.lang.String, org.jasig.schedassist.model.ICalendarAccount)
*/
@Override
public List<IDelegateCalendarAccount> searchForDelegates(String searchText,
ICalendarAccount owner) {
String searchTextInternal = searchText.replace(" ", WILDCARD);
if(!searchTextInternal.endsWith(WILDCARD)) {
searchTextInternal += WILDCARD;
}
AndFilter searchFilter = new AndFilter();
// inner orFilter searches on displayName and username
OrFilter orFilter = new OrFilter();
orFilter.or(new LikeFilter(ldapAttributesKey.getDisplayNameAttributeName(), searchTextInternal));
orFilter.or(new LikeFilter(ldapAttributesKey.getUsernameAttributeName(), searchTextInternal));
// if the owner isn't null and owner attribute is NOT a DN, include the owner id in the search
if (owner != null && !isTreatOwnerAttributeAsDistinguishedName()) {
// TODO assumes delegateOwnerAttributeName has values of ICalendarAccount#getUsername
searchFilter.and(new EqualsFilter(ldapAttributesKey.getDelegateOwnerAttributeName(), owner.getUsername()));
}
// and the orFilter with filter to assert our results have calendar unique ids
searchFilter.and(orFilter);
searchFilter.and(new LikeFilter(ldapAttributesKey.getUniqueIdentifierAttributeName(), WILDCARD));
if(enforceSpecificObjectClass) {
searchFilter.and(new EqualsFilter(OBJECTCLASS, requiredObjectClass));
}
List<IDelegateCalendarAccount> results = new ArrayList<IDelegateCalendarAccount>(executeSearchReturnList(searchFilter, owner));
return results;
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#searchForDelegates(java.lang.String)
*/
@Override
public List<IDelegateCalendarAccount> searchForDelegates(String searchText) {
return searchForDelegates(searchText, null);
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#getDelegate(java.lang.String)
*/
@Override
public IDelegateCalendarAccount getDelegate(String accountName) {
return getDelegate(accountName, (ICalendarAccount) null);
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#getDelegate(java.lang.String, org.jasig.schedassist.model.ICalendarAccount)
*/
@Override
public IDelegateCalendarAccount getDelegate(String accountName,
ICalendarAccount owner) {
AndFilter searchFilter = new AndFilter();
searchFilter.and(new EqualsFilter(ldapAttributesKey.getDisplayNameAttributeName(), accountName));
if(owner != null && !isTreatOwnerAttributeAsDistinguishedName()) {
// TODO assumes delegateOwnerAttributeName has values of ICalendarAccount#getUsername
searchFilter.and(new EqualsFilter(ldapAttributesKey.getDelegateOwnerAttributeName(), owner.getUsername()));
}
searchFilter.and(new LikeFilter(ldapAttributesKey.getUniqueIdentifierAttributeName(), WILDCARD));
if(enforceSpecificObjectClass) {
searchFilter.and(new EqualsFilter(OBJECTCLASS, requiredObjectClass));
}
List<IDelegateCalendarAccount> results = executeSearchReturnList(searchFilter, owner);
IDelegateCalendarAccount delegate = (IDelegateCalendarAccount) DataAccessUtils.singleResult(results);
return delegate;
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#getDelegateByUniqueId(java.lang.String)
*/
@Override
public IDelegateCalendarAccount getDelegateByUniqueId(String accountUniqueId) {
return getDelegateByUniqueId(accountUniqueId, null);
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#getDelegateByUniqueId(java.lang.String, org.jasig.schedassist.model.ICalendarAccount)
*/
@Override
public IDelegateCalendarAccount getDelegateByUniqueId(
String accountUniqueId, ICalendarAccount owner) {
AndFilter searchFilter = new AndFilter();
searchFilter.and(new EqualsFilter(ldapAttributesKey.getUniqueIdentifierAttributeName(), accountUniqueId));
if(owner != null && !isTreatOwnerAttributeAsDistinguishedName()) {
// TODO assumes delegateOwnerAttributeName has values of ICalendarAccount#getUsername
searchFilter.and(new EqualsFilter(ldapAttributesKey.getDelegateOwnerAttributeName(), owner.getUsername()));
}
if(enforceSpecificObjectClass) {
searchFilter.and(new EqualsFilter(OBJECTCLASS, requiredObjectClass));
}
List<IDelegateCalendarAccount> results = executeSearchReturnList(searchFilter, owner);
IDelegateCalendarAccount delegate = (IDelegateCalendarAccount) DataAccessUtils.singleResult(results);
return delegate;
}
/* (non-Javadoc)
* @see org.jasig.schedassist.IDelegateCalendarAccountDao#getDelegate(java.lang.String, java.lang.String)
*/
@Override
public IDelegateCalendarAccount getDelegate(String attributeName,
String attributeValue) {
Filter filter = new EqualsFilter(attributeName, attributeValue);
if(enforceSpecificObjectClass) {
AndFilter andFilter = new AndFilter();
andFilter.and(filter);
andFilter.and(new EqualsFilter(OBJECTCLASS, requiredObjectClass));
filter = andFilter;
}
List<IDelegateCalendarAccount> results = executeSearchReturnList(filter, null);
IDelegateCalendarAccount delegate = (IDelegateCalendarAccount) DataAccessUtils.singleResult(results);
return delegate;
}
/**
*
* @param searchFilter
* @param owner
* @return
*/
@SuppressWarnings("unchecked")
protected List<IDelegateCalendarAccount> executeSearchReturnList(final Filter searchFilter, final ICalendarAccount owner) {
SearchControls searchControls = new SearchControls();
searchControls.setCountLimit(searchResultsLimit);
searchControls.setTimeLimit(searchTimeLimit);
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
List<IDelegateCalendarAccount> results = Collections.emptyList();
try {
results = ldapTemplate.search(
baseDn,
searchFilter.toString(),
searchControls,
new DefaultDelegateAccountAttributesMapperImpl(ldapAttributesKey, owner));
if(log.isDebugEnabled()) {
log.debug("search " + searchFilter + " returned " + results.size() + " results");
}
if(isTreatOwnerAttributeAsDistinguishedName() && owner != null && owner instanceof HasDistinguishedName) {
HasDistinguishedName ldapOwnerAccount = (HasDistinguishedName) owner;
enforceDistinguishedNameMatch(results, ldapOwnerAccount);
}
Collections.sort(results, new DelegateDisplayNameComparator());
} catch (SizeLimitExceededException e) {
log.debug("search filter exceeded size limit (" + searchResultsLimit + "): " + searchFilter);
} catch (TimeLimitExceededException e) {
log.debug("search filter exceeded time limit(" + searchTimeLimit + " milliseconds): " + searchFilter);
}
return results;
}
/**
* Iterate over delegates, removing elements that have mismatched DN to owner
* @param delegates
* @param desiredOwnerAccount
*/
protected void enforceDistinguishedNameMatch(List<IDelegateCalendarAccount> delegates, HasDistinguishedName desiredOwnerAccount) {
for(Iterator<IDelegateCalendarAccount> i = delegates.iterator(); i.hasNext(); ) {
IDelegateCalendarAccount delegate = i.next();
String ownerAttributeValue = delegate.getAccountOwnerAttribute();
if(!desiredOwnerAccount.getDistinguishedName().equals(new DistinguishedName(ownerAttributeValue))) {
if(log.isDebugEnabled()) {
log.debug(ownerAttributeValue + " does not match desired owner ICalendarAccount dn: " + desiredOwnerAccount.getDistinguishedName());
}
i.remove();
}
}
}
/**
* Simple {@link Comparator} for {@link IDelegateCalendarAccount} that compares
* on the displayName field.
*
* @author Nicholas Blair
* @version $Id: LDAPDelegateCalendarAccountDaoImpl $
*/
private static class DelegateDisplayNameComparator implements Comparator<IDelegateCalendarAccount>{
@Override
public int compare(IDelegateCalendarAccount o1,
IDelegateCalendarAccount o2) {
return new CompareToBuilder().append(o1.getDisplayName(), o2.getDisplayName()).toComparison();
}
}
}