LDAP-248: DirContextAdapter now handles javax.naming.Name instances properly with regards to equality

LDAP-274: ODM now uses standard Spring converter (if present).
LDAP-275: Support for different Collection types in ODM
This commit is contained in:
Mattias Hellborg Arthursson
2013-10-24 10:25:22 +02:00
parent 27593ac973
commit c2abc9660e
25 changed files with 1422 additions and 133 deletions

View File

@@ -13,6 +13,7 @@ dependencies {
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework.data:spring-data-commons:$springDataVersion"
"org.slf4j:slf4j-api:$slf4jVersion"
provided "commons-pool:commons-pool:$commonsPoolVersion",
"com.sun:ldapbp:1.0",
@@ -24,6 +25,7 @@ dependencies {
testCompile "junit:junit:$junitVersion",
"commons-lang:commons-lang:$commonsLangVersion",
"gsbase:gsbase:$gsbaseVersion",
"org.mockito:mockito-core:$mockitoVersion"
"org.mockito:mockito-core:$mockitoVersion",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}

View File

@@ -47,9 +47,14 @@ public interface LdapDataEntry {
/**
* Set the with the name <code>name</code> to the <code>value</code>.
* If the value is a {@link Name} instance, equality for Distinguished
* Names will be used for calculating attribute modifications.
*
* @param name name of the attribute.
* @param value value to set the attribute to.
* @throws IllegalArgumentException if the value is a {@link Name} instance
* and one or several of the currently present attribute values is <strong>not</strong>
* {@link Name} instances or Strings representing valid Distinguished Names.
*/
public void setAttributeValue(String name, Object value);
@@ -62,8 +67,14 @@ public interface LdapDataEntry {
* objects or if one or more object has changed. Reordering the objects will
* not cause an update.
*
* If the values are {@link Name} instances, equality for Distinguished
* Names will be used for calculating attribute modifications.
*
* @param name The id of the attribute.
* @param values Attribute values.
* @throws IllegalArgumentException if value is a {@link Name} instance
* and one or several of the currently present attribute values is <strong>not</strong>
* {@link Name} instances or Strings representing valid Distinguished Names.
*/
void setAttributeValues(String name, Object[] values);
@@ -78,10 +89,15 @@ public interface LdapDataEntry {
* Reordering the objects will only cause an update if orderMatters is set
* to true.
*
* If the values are {@link Name} instances, equality for Distinguished
* Names will be used for calculating attribute modifications.
* @param name The id of the attribute.
* @param values Attribute values.
* @param orderMatters If <code>true</code>, it will be changed even if data
* was just reordered.
* @throws IllegalArgumentException if value is a {@link Name} instance
* and one or several of the currently present attribute values is <strong>not</strong>
* {@link Name} instances or Strings representing valid Distinguished Names.
*/
void setAttributeValues(String name, Object[] values, boolean orderMatters);
@@ -91,9 +107,15 @@ public interface LdapDataEntry {
* will be no duplicates of an added value - it the value exists it will not
* be added again.
*
* If the value is a {@link Name} instance, equality for Distinguished
* Names will be used for calculating attribute modifications.
*
* @param name the name of the Attribute to which the specified value should
* be added.
* @param value the Attribute value to add.
* @throws IllegalArgumentException if value is a {@link Name} instance
* and one or several of the currently present attribute values is <strong>not</strong>
* {@link Name} instances or Strings representing valid Distinguished Names.
*/
void addAttributeValue(String name, Object value);
@@ -104,6 +126,9 @@ public interface LdapDataEntry {
* this method makes sure that the there will be no duplicates of an added
* value - it the value exists it will not be added again.
*
* If the value is a {@link Name} instance, equality for Distinguished
* Names will be used for calculating attribute modifications.
*
* @param name the name of the Attribute to which the specified value should
* be added.
* @param value the Attribute value to add.
@@ -111,6 +136,9 @@ public interface LdapDataEntry {
* regardless of whether there is an identical value already, allowing for
* duplicate attribute values; <code>false</code> will not add the value if
* it already exists.
* @throws IllegalArgumentException if value is a {@link Name} instance
* and one or several of the currently present attribute values is <strong>not</strong>
* {@link Name} instances or Strings representing valid Distinguished Names.
*/
void addAttributeValue(String name, Object value,
boolean addIfDuplicateExists);
@@ -119,9 +147,15 @@ public interface LdapDataEntry {
* Remove a value from the Attribute with the specified name. If the
* Attribute doesn't exist, do nothing.
*
* If the value is a {@link Name} instance, equality for Distinguished
* Names will be used for calculating attribute modifications.
*
* @param name the name of the Attribute from which the specified value
* should be removed.
* @param value the value to remove.
* @throws IllegalArgumentException if value is a {@link Name} instance
* and one or several of the currently present attribute values is <strong>not</strong>
* {@link Name} instances or Strings representing valid Distinguished Names.
*/
void removeAttributeValue(String name, Object value);

View File

@@ -33,8 +33,6 @@ import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
@@ -60,10 +58,27 @@ import java.util.TreeSet;
* this class keeps track of the changes made to its attributes, making them
* available as an array of <code>ModificationItem</code> objects, suitable as
* input to {@link LdapTemplate#modifyAttributes(DirContextOperations)}.
*
* Note that this is not a complete implementation of DirContext. Several
* methods are not relevant for the intended usage of this class, so they
* throw UnsupportOperationException.
*
* <p>
* This class is aware of the specifics of {@link Name} instances with regards
* to equality when working with attribute values. This comes in very handy
* when working with e.g. security groups and modifications of them. If
* {@link Name} instances are supplied to one of the Attribute manipulation
* methods (e.g. {@link #addAttributeValue(String, Object)},
* {@link #removeAttributeValue(String, Object)}, {@link #setAttributeValue(String, Object)},
* or {@link #setAttributeValues(String, Object[])}), the produced modifications
* will be calculated using {@link Name} equality. This means that if an the <code>member</code>
* has a value of <code>"cn=John Doe,ou=People"</code>, and we call
* <code>addAttributeValue("member", LdapUtils.newLdapName("CN=John Doe,OU=People")</code>,
* this will <strong>not</strong> be considered a modification since the two DN
* strings represent the same distinguished name (case and spacing between attributes is
* disregarded).
* </p>
* <p>
* Note that this is not a complete implementation of DirContext. Several
* methods are not relevant for the intended usage of this class, so they
* throw UnsupportOperationException.
* </p>
*
* @see #setAttributeValue(String, Object)
* @see #setAttributeValues(String, Object[])
@@ -90,7 +105,7 @@ public class DirContextAdapter implements DirContextOperations {
private static Logger log = LoggerFactory.getLogger(DirContextAdapter.class);
private final Attributes originalAttrs;
private final NameAwareAttributes originalAttrs;
private LdapName dn;
@@ -98,7 +113,7 @@ public class DirContextAdapter implements DirContextOperations {
private boolean updateMode = false;
private Attributes updatedAttrs;
private NameAwareAttributes updatedAttrs;
private String referralUrl;
@@ -160,10 +175,10 @@ public class DirContextAdapter implements DirContextOperations {
public DirContextAdapter(Attributes attrs, Name dn, Name base,
String referralUrl) {
if (attrs != null) {
this.originalAttrs = attrs;
this.originalAttrs = new NameAwareAttributes(attrs);
}
else {
this.originalAttrs = new BasicAttributes(true);
this.originalAttrs = new NameAwareAttributes();
}
if (dn != null) {
@@ -193,9 +208,9 @@ public class DirContextAdapter implements DirContextOperations {
* @param master The adapter to be copied.
*/
protected DirContextAdapter(DirContextAdapter master) {
this.originalAttrs = (Attributes) master.originalAttrs.clone();
this.originalAttrs = (NameAwareAttributes) master.originalAttrs.clone();
this.dn = master.dn;
this.updatedAttrs = (Attributes) master.updatedAttrs.clone();
this.updatedAttrs = (NameAwareAttributes) master.updatedAttrs.clone();
this.updateMode = master.updateMode;
}
@@ -209,21 +224,14 @@ public class DirContextAdapter implements DirContextOperations {
public void setUpdateMode(boolean mode) {
this.updateMode = mode;
if (updateMode) {
updatedAttrs = new BasicAttributes(true);
updatedAttrs = new NameAwareAttributes();
}
}
/*
* @see org.springframework.ldap.support.DirContextOperations#isUpdateMode()
*/
public boolean isUpdateMode() {
return updateMode;
}
/*
* @seeorg.springframework.ldap.support.DirContextOperations#
* getNamesOfModifiedAttributes()
*/
public String[] getNamesOfModifiedAttributes() {
List<String> tmpList = new ArrayList<String>();
@@ -264,10 +272,6 @@ public class DirContextAdapter implements DirContextOperations {
}
}
/*
* @seeorg.springframework.ldap.support.AttributeModificationsAware#
* getModificationItems()
*/
public ModificationItem[] getModificationItems() {
if (!updateMode) {
return new ModificationItem[0];
@@ -280,7 +284,7 @@ public class DirContextAdapter implements DirContextOperations {
// find attributes that have been changed, removed or added
while (attributesEnumeration.hasMore()) {
Attribute oneAttr = attributesEnumeration.next();
NameAwareAttribute oneAttr = (NameAwareAttribute) attributesEnumeration.next();
collectModifications(oneAttr, tmpList);
}
@@ -313,9 +317,17 @@ public class DirContextAdapter implements DirContextOperations {
* @param modificationList the list in which to add the modifications.
* @throws NamingException if thrown by called Attribute methods.
*/
private void collectModifications(Attribute changedAttr,
private void collectModifications(NameAwareAttribute changedAttr,
List<ModificationItem> modificationList) throws NamingException {
Attribute currentAttribute = originalAttrs.get(changedAttr.getID());
NameAwareAttribute currentAttribute = originalAttrs.get(changedAttr.getID());
if(changedAttr.hasValuesAsNames()) {
try {
currentAttribute.initValuesAsNames();
} catch(IllegalArgumentException e) {
log.warn("Incompatible attributes; changed attribute has Name values but " +
"original cannot be converted to this");
}
}
if (changedAttr.equals(currentAttribute)) {
// No changes
@@ -368,15 +380,16 @@ public class DirContextAdapter implements DirContextOperations {
throws NamingException {
Attribute originalClone = (Attribute) originalAttr.clone();
Attribute addedValuesAttribute = new BasicAttribute(originalAttr
Attribute addedValuesAttribute = new NameAwareAttribute(originalAttr
.getID());
for (int i = 0; i < changedAttr.size(); i++) {
Object attributeValue = changedAttr.get(i);
if (!originalClone.remove(attributeValue)) {
addedValuesAttribute.add(attributeValue);
}
}
NamingEnumeration<?> allValues = changedAttr.getAll();
while(allValues.hasMoreElements()) {
Object attributeValue = allValues.nextElement();
if (!originalClone.remove(attributeValue)) {
addedValuesAttribute.add(attributeValue);
}
}
// We have now traversed and removed all values from the original that
// were also present in the new values. The remaining values in the
@@ -552,19 +565,10 @@ public class DirContextAdapter implements DirContextOperations {
return originalAttrs.get(attrId) != null;
}
/*
* @see
* org.springframework.ldap.support.DirContextOperations#getStringAttribute
* (java.lang.String)
*/
public String getStringAttribute(String name) {
return (String) getObjectAttribute(name);
}
/*
* @see org.springframework.ldap.support.DirContextOperations#getObjectAttribute
* (java.lang.String)
*/
public Object getObjectAttribute(String name) {
Attribute oneAttr = originalAttrs.get(name);
if (oneAttr == null || oneAttr.size() == 0) { // LDAP-215
@@ -579,19 +583,11 @@ public class DirContextAdapter implements DirContextOperations {
}
// LDAP-215
/* (non-Javadoc)
* @see org.springframework.ldap.core.DirContextOperations#attributeExists(java.lang.String)
*/
public boolean attributeExists(String name) {
Attribute oneAttr = originalAttrs.get(name);
return oneAttr != null;
}
/*
* @see
* org.springframework.ldap.support.DirContextOperations#setAttributeValue
* (java.lang.String, java.lang.Object)
*/
public void setAttributeValue(String name, Object value) {
// new entry
if (!updateMode && value != null) {
@@ -600,7 +596,7 @@ public class DirContextAdapter implements DirContextOperations {
// updating entry
if (updateMode) {
BasicAttribute attribute = new BasicAttribute(name);
Attribute attribute = new NameAwareAttribute(name);
if (value != null) {
attribute.add(value);
}
@@ -608,13 +604,6 @@ public class DirContextAdapter implements DirContextOperations {
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.ldap.core.DirContextOperations#addAttributeValue(
* java.lang.String, java.lang.Object)
*/
public void addAttributeValue(String name, Object value) {
addAttributeValue(name, value, DONT_ADD_IF_DUPLICATE_EXISTS);
}
@@ -654,13 +643,6 @@ public class DirContextAdapter implements DirContextOperations {
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.ldap.core.DirContextOperations#removeAttributeValue
* (java.lang.String, java.lang.Object)
*/
public void removeAttributeValue(String name, Object value) {
if (!updateMode && value != null) {
Attribute attr = originalAttrs.get(name);
@@ -686,23 +668,13 @@ public class DirContextAdapter implements DirContextOperations {
}
}
/*
* @see
* org.springframework.ldap.support.DirContextOperations#setAttributeValues
* (java.lang.String, java.lang.Object[])
*/
public void setAttributeValues(String name, Object[] values) {
setAttributeValues(name, values, ORDER_DOESNT_MATTER);
}
/*
* @see
* org.springframework.ldap.support.DirContextOperations#setAttributeValues
* (java.lang.String, java.lang.Object[], boolean)
*/
public void setAttributeValues(String name, Object[] values,
boolean orderMatters) {
Attribute a = new BasicAttribute(name, orderMatters);
Attribute a = new NameAwareAttribute(name, orderMatters);
for (int i = 0; values != null && i < values.length; i++) {
a.add(values[i]);
@@ -720,9 +692,6 @@ public class DirContextAdapter implements DirContextOperations {
}
}
/*
* @see org.springframework.ldap.support.DirContextOperations#update()
*/
public void update() {
NamingEnumeration<? extends Attribute> attributesEnumeration = null;
@@ -751,14 +720,9 @@ public class DirContextAdapter implements DirContextOperations {
}
// Reset the attributes to be updated
updatedAttrs = new BasicAttributes(true);
updatedAttrs = new NameAwareAttributes();
}
/*
* @see
* org.springframework.ldap.core.DirContextOperations#getStringAttributes
* (java.lang.String)
*/
public String[] getStringAttributes(String name) {
try {
List<String> objects = collectAttributeValuesAsList(name, String.class);
@@ -770,13 +734,6 @@ public class DirContextAdapter implements DirContextOperations {
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.ldap.core.DirContextOperations#getObjectAttributes
* (java.lang.String)
*/
public Object[] getObjectAttributes(String name) {
try {
List<Object> list = collectAttributeValuesAsList(name, Object.class);
@@ -864,7 +821,7 @@ public class DirContextAdapter implements DirContextOperations {
throw new NameNotFoundException();
}
Attributes a = new BasicAttributes(true);
Attributes a = new NameAwareAttributes();
Attribute target;
for (String attrId : attrIds) {
target = originalAttrs.get(attrId);

View File

@@ -0,0 +1,40 @@
package org.springframework.ldap.core;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import java.util.Iterator;
/**
* @author Mattias Hellborg Arthursson
*/
final class IterableNamingEnumeration<T> implements NamingEnumeration<T> {
private final Iterator<T> iterator;
IterableNamingEnumeration(Iterable<T> iterable) {
this.iterator = iterable.iterator();
}
@Override
public T next() {
return iterator.next();
}
@Override
public boolean hasMore() {
return iterator.hasNext();
}
@Override
public void close() throws NamingException {
}
@Override
public boolean hasMoreElements() {
return hasMore();
}
@Override
public T nextElement() {
return next();
}
}

View File

@@ -0,0 +1,348 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.core;
import org.springframework.ldap.InvalidNameException;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.DirContext;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Used internally to make DirContextAdapter properly handle Names as values.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
public class NameAwareAttribute implements Attribute {
private final String id;
private final boolean orderMatters;
private final Set<Object> values = new LinkedHashSet<Object>();
private Map<Name, String> valuesAsNames = new HashMap<Name, String>();
/**
* Construct a new instance with the specified id and one value.
* @param id the attribute id
* @param value the value to start off with
*/
public NameAwareAttribute(String id, Object value) {
this(id);
values.add(value);
}
/**
* Construct a new instance from the supplied Attribute.
*
* @param attribute the Attribute to copy.
*/
public NameAwareAttribute(Attribute attribute) {
this(attribute.getID(), attribute.isOrdered());
try {
NamingEnumeration<?> values = attribute.getAll();
while(values.hasMore()) {
this.add(values.next());
}
} catch (NamingException e) {
throw LdapUtils.convertLdapException(e);
}
if (attribute instanceof NameAwareAttribute) {
NameAwareAttribute nameAwareAttribute = (NameAwareAttribute) attribute;
populateValuesAsNames(nameAwareAttribute, this);
}
}
/**
* Construct a new instance with the specified id and no values.
* @param id the attribute id
*/
public NameAwareAttribute(String id) {
this(id, false);
}
/**
* Construct a new instance with the specified id, no values and order significance as specified.
* @param id the attribute id
* @param orderMatters whether order has significance in this attribute.
*/
public NameAwareAttribute(String id, boolean orderMatters) {
this.id = id;
this.orderMatters = orderMatters;
}
@Override
public NamingEnumeration<?> getAll() {
return new IterableNamingEnumeration<Object>(values);
}
@Override
public Object get() {
if(values.isEmpty()) {
return null;
}
return values.iterator().next();
}
@Override
public int size() {
return values.size();
}
@Override
public String getID() {
return id;
}
@Override
public boolean contains(Object attrVal) {
return values.contains(attrVal);
}
@Override
public boolean add(Object attrVal) {
if (attrVal instanceof Name) {
initValuesAsNames();
Name name = LdapUtils.newLdapName((Name) attrVal);
String currentValue = valuesAsNames.get(name);
String nameAsString = name.toString();
if(currentValue == null) {
valuesAsNames.put(name, name.toString());
values.add(nameAsString);
return true;
} else {
if(!currentValue.equals(nameAsString)) {
values.remove(currentValue);
values.add(nameAsString);
}
return false;
}
}
return values.add(attrVal);
}
public void initValuesAsNames() {
if(hasValuesAsNames()) {
return;
}
Map<Name, String> valuesAsNames = new HashMap<Name, String>();
for (Object value : values) {
if (value instanceof String) {
String s = (String) value;
try {
valuesAsNames.put(LdapUtils.newLdapName(s), s);
} catch (InvalidNameException e) {
throw new IllegalArgumentException("This instance has values that are not valid distinguished names; " +
"cannot handle Name values");
}
} else {
throw new IllegalArgumentException("This instance has non-string attribute values; " +
"cannot handle Name values");
}
}
this.valuesAsNames = valuesAsNames;
}
public boolean hasValuesAsNames() {
return !valuesAsNames.isEmpty();
}
@Override
public boolean remove(Object attrval) {
if (attrval instanceof Name) {
initValuesAsNames();
Name name = LdapUtils.newLdapName((Name) attrval);
String removedValue = valuesAsNames.remove(name);
if(removedValue != null) {
values.remove(removedValue);
return true;
}
return false;
}
return values.remove(attrval);
}
@Override
public void clear() {
values.clear();
}
@Override
public DirContext getAttributeSyntaxDefinition() throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public DirContext getAttributeDefinition() throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public boolean isOrdered() {
return orderMatters;
}
@Override
public Object get(int ix) throws NamingException {
Iterator<Object> iterator = values.iterator();
try {
Object value = iterator.next();
for(int i = 0; i < ix; i++) {
value = iterator.next();
}
return value;
} catch (NoSuchElementException e) {
throw new IndexOutOfBoundsException("No value at index i");
}
}
@Override
public Object remove(int ix) {
Iterator<Object> iterator = values.iterator();
try {
Object value = iterator.next();
for(int i = 0; i < ix; i++) {
value = iterator.next();
}
iterator.remove();
return value;
} catch (NoSuchElementException e) {
throw new IndexOutOfBoundsException("No value at index i");
}
}
@Override
public void add(int ix, Object attrVal) {
throw new UnsupportedOperationException();
}
@Override
public Object set(int ix, Object attrVal) {
throw new UnsupportedOperationException();
}
@Override
public Object clone() {
return new NameAwareAttribute(this);
}
private void populateValuesAsNames(NameAwareAttribute from, NameAwareAttribute to) {
Set<Map.Entry<Name, String>> entries = from.valuesAsNames.entrySet();
for (Map.Entry<Name, String> entry : entries) {
to.valuesAsNames.put(LdapUtils.newLdapName(entry.getKey()), entry.getValue());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NameAwareAttribute that = (NameAwareAttribute) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if(this.values.size() != that.values.size()) {
return false;
}
if(this.orderMatters != that.orderMatters || this.size() != that.size()) {
return false;
}
if(this.hasValuesAsNames() != that.hasValuesAsNames()) {
return false;
}
Set<?> myValues = this.values;
Set<?> theirValues = that.values;
if(this.hasValuesAsNames()) {
// We have Name values - compare these to get
// syntactically correct comparison of the values
myValues = this.valuesAsNames.keySet();
theirValues = that.valuesAsNames.keySet();
}
if(orderMatters) {
Iterator<?> thisIterator = myValues.iterator();
Iterator<?> thatIterator = theirValues.iterator();
while(thisIterator.hasNext()) {
if(!ObjectUtils.nullSafeEquals(thisIterator.next(), thatIterator.next())) {
return false;
}
}
return true;
} else {
for (Object value : myValues) {
if(!CollectionUtils.contains(theirValues.iterator(), value)) {
return false;
}
}
return true;
}
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
int valuesHash = 7;
Set<?> myValues = this.values;
if(hasValuesAsNames()) {
myValues = valuesAsNames.keySet();
}
for (Object value : myValues) {
result += ObjectUtils.nullSafeHashCode(value);
}
result = 31 * result + valuesHash;
return result;
}
@Override
public String toString() {
return String.format("NameAwareAttribute; id: %s; hasValuesAsNames: %s; orderMatters: %s; values: %s",
id, hasValuesAsNames(), orderMatters, values);
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.core;
import org.springframework.util.Assert;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import java.util.HashMap;
import java.util.Map;
/**
* Used internally to help DirContextAdapter properly handle Names as values.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
public class NameAwareAttributes implements Attributes {
private Map<String, NameAwareAttribute> attributes = new HashMap<String, NameAwareAttribute>();
/**
* Create an empty instance
*/
public NameAwareAttributes() {
}
/**
* Create a new instance, populated with the data from the supplied instance.
* @param attributes the instance to copy.
*/
public NameAwareAttributes(Attributes attributes) {
NamingEnumeration<? extends Attribute> allAttributes = attributes.getAll();
while(allAttributes.hasMoreElements()) {
Attribute attribute = allAttributes.nextElement();
put(new NameAwareAttribute(attribute));
}
}
@Override
public boolean isCaseIgnored() {
return true;
}
@Override
public int size() {
return attributes.size();
}
@Override
public NameAwareAttribute get(String attrID) {
Assert.hasLength(attrID, "Attribute ID must not be empty");
return attributes.get(attrID.toLowerCase());
}
@Override
public NamingEnumeration<? extends Attribute> getAll() {
return new IterableNamingEnumeration<NameAwareAttribute>(attributes.values());
}
@Override
public NamingEnumeration<String> getIDs() {
return new IterableNamingEnumeration<String>(attributes.keySet());
}
@Override
public Attribute put(String attrID, Object val) {
Assert.hasLength(attrID, "Attribute ID must not be empty");
NameAwareAttribute newAttribute = new NameAwareAttribute(attrID, val);
attributes.put(attrID.toLowerCase(), newAttribute);
return newAttribute;
}
@Override
public Attribute put(Attribute attr) {
Assert.notNull(attr, "Attribute must not be null");
NameAwareAttribute newAttribute = new NameAwareAttribute(attr);
attributes.put(attr.getID().toLowerCase(), newAttribute);
return newAttribute;
}
@Override
public Attribute remove(String attrID) {
Assert.hasLength(attrID, "Attribute ID must not be empty");
return attributes.remove(attrID);
}
@Override
public Object clone() {
return new NameAwareAttributes(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NameAwareAttributes that = (NameAwareAttributes) o;
if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) return false;
return true;
}
@Override
public int hashCode() {
return attributes != null ? attributes.hashCode() : 0;
}
@Override
public String toString() {
return String.format("NameAwareAttribute; attributes: %s", attributes.toString());
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.ldap.odm.core.impl;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.odm.annotations.Attribute;
import org.springframework.ldap.odm.annotations.DnAttribute;
import org.springframework.ldap.odm.annotations.Id;
@@ -27,8 +28,13 @@ import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/*
* Extract attribute meta-data from the @Attribute annotation, the @Id annotation
@@ -60,8 +66,10 @@ import java.util.Set;
private boolean isId;
// Is this field multi-valued represented by a List
private boolean isList;
private boolean isCollection;
private Class<? extends Collection> collectionClass;
// Is this the objectClass attribute
private boolean isObjectClass;
@@ -112,18 +120,14 @@ import java.util.Set;
// Determine the class of data stored in the field
Class<?> fieldType = field.getType();
// We support only lists for multi-valued attributes, as we must allow duplicate values
if (Set.class.isAssignableFrom(fieldType)) {
throw new MetaDataException(String.format("Only lists are allowed for multivlaued attributes, errpr in field %1$s in Entry class %2$s",
field, field.getDeclaringClass()));
}
isList = List.class.isAssignableFrom(fieldType);
isCollection = Collection.class.isAssignableFrom(fieldType);
valueClass=null;
if (!isList) {
if (!isCollection) {
// It's not a list so assume its single valued - so just take the field type
valueClass = fieldType;
} else {
determineCollectionClass(fieldType);
// It's multi-valued - so we need to look at the signature in
// the class file to find the generic type - this is supported for class file
// format 49 and greater which corresponds to java 5 and later.
@@ -156,7 +160,33 @@ import java.util.Set;
field, field.getDeclaringClass()));
}
}
@SuppressWarnings("unchecked")
private void determineCollectionClass(Class<?> fieldType) {
if(fieldType.isInterface()) {
if(Collection.class.equals(fieldType) || List.class.equals(fieldType)) {
collectionClass = ArrayList.class;
} else if(SortedSet.class.equals(fieldType)) {
collectionClass = TreeSet.class;
} else if(Set.class.isAssignableFrom(fieldType)) {
collectionClass = LinkedHashSet.class;
} else {
throw new MetaDataException(String.format("Collection class %s is not supported", fieldType));
}
} else {
collectionClass = (Class<? extends Collection>) fieldType;
}
}
@SuppressWarnings("unchecked")
public Collection<Object> newCollectionInstance() {
try {
return (Collection<Object>) collectionClass.newInstance();
} catch (Exception e) {
throw new UncategorizedLdapException("Failed to instantiate collection class", e);
}
}
// Extract information from the @Id annotation:
// isId
private boolean processIdAnnotation(Field field, Class<?> fieldType) {
@@ -210,7 +240,7 @@ import java.util.Set;
}
// If this is the objectclass attribute then it must be of type List<String>
if (isObjectClass() && (!isList() || valueClass!=String.class)) {
if (isObjectClass() && (!isCollection() || valueClass!=String.class)) {
throw new MetaDataException(String.format("The type of the objectclass attribute must be List<String> in classs %1$s",
field.getDeclaringClass()));
}
@@ -233,8 +263,8 @@ import java.util.Set;
return name;
}
public boolean isList() {
return isList;
public boolean isCollection() {
return isCollection;
}
public boolean isId() {
@@ -261,6 +291,16 @@ import java.util.Set;
return valueClass;
}
public Class<?> getJndiClass() {
if(isBinary()) {
return byte[].class;
} else if(Name.class.isAssignableFrom(valueClass)) {
return Name.class;
} else {
return String.class;
}
}
/*
* (non-Javadoc)
*
@@ -269,6 +309,6 @@ import java.util.Set;
@Override
public String toString() {
return String.format("name=%1$s | field=%2$s | valueClass=%3$s | syntax=%4$s| isBinary=%5$s | isId=%6$s | isList=%7$s | isObjectClass=%8$s",
getName(), getField(), getValueClass().getName(), getSyntax(), isBinary(), isId(), isList(), isObjectClass());
getName(), getField(), getValueClass().getName(), getSyntax(), isBinary(), isId(), isCollection(), isObjectClass());
}
}

View File

@@ -19,12 +19,14 @@ package org.springframework.ldap.odm.core.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.LdapDataEntry;
import org.springframework.core.SpringVersion;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.odm.annotations.DnAttribute;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
import org.springframework.ldap.odm.typeconversion.ConverterManager;
import org.springframework.ldap.odm.typeconversion.impl.ConversionServiceConverterManager;
import org.springframework.ldap.odm.typeconversion.impl.ConverterManagerImpl;
import org.springframework.ldap.support.LdapNameBuilder;
import org.springframework.ldap.support.LdapUtils;
@@ -66,7 +68,16 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
public DefaultObjectDirectoryMapper() {
this.converterManager = new ConverterManagerImpl();
if(isAtLeast30()) {
this.converterManager = new ConversionServiceConverterManager();
} else {
this.converterManager = new ConverterManagerImpl();
}
}
private boolean isAtLeast30() {
return SpringVersion.getVersion().compareTo("3.0") > 0;
}
public void setConverterManager(ConverterManager converterManager) {
@@ -126,7 +137,7 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
for (Field field : metaData) {
AttributeMetaData attributeInfo = metaData.getAttribute(field);
if (!attributeInfo.isTransient() && !attributeInfo.isId() && !(attributeInfo.isObjectClass())) {
Class<?> jndiClass = (attributeInfo.isBinary()) ? byte[].class : String.class;
Class<?> jndiClass = attributeInfo.getJndiClass();
Class<?> javaClass = attributeInfo.getValueClass();
if (!converterManager.canConvert(jndiClass, attributeInfo.getSyntax(), javaClass)) {
throw new InvalidEntryException(String.format(
@@ -184,9 +195,9 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
if (!attributeInfo.isTransient() && !attributeInfo.isId() && !(attributeInfo.isObjectClass())) {
try {
// If this is a "binary" object the JNDI expects a byte[] otherwise a String
Class<?> targetClass = (attributeInfo.isBinary()) ? byte[].class : String.class;
Class<?> targetClass = attributeInfo.getJndiClass();
// Multi valued?
if (!attributeInfo.isList()) {
if (!attributeInfo.isCollection()) {
populateSingleValueAttribute(entry, context, field, attributeInfo, targetClass);
} else {
@@ -204,7 +215,7 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
private void populateMultiValueAttribute(Object entry, LdapDataEntry context, Field field, AttributeMetaData attributeInfo, Class<?> targetClass) throws IllegalAccessException {
// We need to build up a list of of the values
List<String> attributeValues = new ArrayList<String>();
List<Object> attributeValues = new ArrayList<Object>();
// Get the list of values
Collection<?> fieldValues = (Collection<?>)field.get(entry);
// Ignore null lists
@@ -212,7 +223,7 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
for (final Object o : fieldValues) {
// Ignore null values
if (o != null) {
attributeValues.add((String)converterManager.convert(o, attributeInfo.getSyntax(),
attributeValues.add(converterManager.convert(o, attributeInfo.getSyntax(),
targetClass));
}
}
@@ -269,7 +280,7 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
Name dn = context.getDn();
if (!attributeInfo.isTransient() && !attributeInfo.isId()) {
// Not the ID - but is is multi valued?
if (!attributeInfo.isList()) {
if (!attributeInfo.isCollection()) {
// No - its single valued, grab the JNDI attribute that corresponds to the metadata on the
// current field
populateSingleValueField(result, attributeValueMap, field, attributeInfo);
@@ -326,7 +337,7 @@ public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
private <T> void populateMultiValueField(T result, Map<CaseIgnoreString, Attribute> attributeValueMap, Field field, AttributeMetaData attributeInfo) throws NamingException, IllegalAccessException {
// We need to build up a list of values
List<Object> fieldValues = new ArrayList<Object>();
Collection<Object> fieldValues = attributeInfo.newCollectionInstance();
// Grab the attribute from the JNDI representation
Attribute currentAttribute = attributeValueMap.get(attributeInfo.getName());
// There is no guarantee that this attribute is present in the directory - so ignore nulls

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.odm.typeconversion.impl;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.ldap.odm.typeconversion.ConverterManager;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import javax.naming.Name;
/**
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
public class ConversionServiceConverterManager implements ConverterManager {
private GenericConversionService conversionService;
private final static String DEFAULT_CONVERSION_SERVICE_CLASS =
"org.springframework.core.convert.support.DefaultConversionService";
public ConversionServiceConverterManager(GenericConversionService conversionService) {
this.conversionService = conversionService;
}
public ConversionServiceConverterManager() {
ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
if(ClassUtils.isPresent(DEFAULT_CONVERSION_SERVICE_CLASS, defaultClassLoader)) {
try {
Class<?> clazz = ClassUtils.forName(DEFAULT_CONVERSION_SERVICE_CLASS, defaultClassLoader);
conversionService = (GenericConversionService) clazz.newInstance();
} catch (Exception e) {
ReflectionUtils.handleReflectionException(e);
}
} else {
conversionService = new GenericConversionService();
}
prePopulateWithNameConverter();
}
private void prePopulateWithNameConverter() {
conversionService.addConverter(new StringToNameConverter());
}
@Override
public boolean canConvert(Class<?> fromClass, String syntax, Class<?> toClass) {
return conversionService.canConvert(fromClass, toClass);
}
@Override
public <T> T convert(Object source, String syntax, Class<T> toClass) {
return conversionService.convert(source, toClass);
}
public final static class NameToStringConverter
implements org.springframework.core.convert.converter.Converter<Name, String> {
@Override
public String convert(Name source) {
if(source == null) {
return null;
}
return source.toString();
}
}
public final static class StringToNameConverter
implements org.springframework.core.convert.converter.Converter<String, Name> {
@Override
public Name convert(String source) {
if(source == null) {
return null;
}
return LdapUtils.newLdapName(source);
}
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.ldap.odm.typeconversion.impl;
/**
* @author Mattias Hellborg Arthursson
*/
public class StringConverter {
}

View File

@@ -42,7 +42,7 @@ import static org.junit.Assert.fail;
/**
* Tests the DirContextAdapter class.
*
*
* @author Andreas Ronge
* @author Mattias Hellborg Arthursson
* @author Ulrik Sandberg
@@ -1114,7 +1114,7 @@ public class DirContextAdapterTest {
/**
* Test for LDAP-15: DirContextAdapter.setAttribute(). Verifies that setting
* an Attribute should modify updatedAttrs if in update mode.
*
*
* @throws NamingException
*/
@Test
@@ -1223,4 +1223,133 @@ public class DirContextAdapterTest {
DirContextAdapter tested = new DirContextAdapter("cn=john doe, ou=company");
assertEquals(LdapUtils.newLdapName("cn=john doe, ou=company"), tested.getDn());
}
@Test
public void testAddDnAttributeValueIdentical() {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe, ou=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.addAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=john doe, ou=company"));
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(0, modificationItems.length);
}
@Test
public void testAddDnAttributeSyntacticallyEqual() {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe,OU=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.addAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=john doe, ou=company"));
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(0, modificationItems.length);
}
@Test
public void testRemoveDnAttributeSyntacticallyEqual() throws NamingException {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe,OU=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.removeAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=john doe, ou=company"));
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(1, modificationItems.length);
ModificationItem modificationItem = modificationItems[0];
assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItem.getModificationOp());
assertEquals("uniqueMember", modificationItem.getAttribute().getID());
}
@Test
public void testRemoveOneOfSeveralDnAttributeSyntacticallyEqual() throws NamingException {
BasicAttributes attributes = new BasicAttributes();
BasicAttribute attribute = new BasicAttribute("uniqueMember", "cn=john doe,OU=company");
attribute.add("cn=jane doe, ou=company");
attributes.put(attribute);
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.removeAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=john doe, ou=company"));
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(1, modificationItems.length);
ModificationItem modificationItem = modificationItems[0];
assertEquals(DirContext.REMOVE_ATTRIBUTE, modificationItem.getModificationOp());
assertEquals("uniqueMember", modificationItem.getAttribute().getID());
assertEquals("cn=john doe,OU=company", modificationItem.getAttribute().get());
}
@Test
public void testAddDnAttributeNewValue() throws NamingException {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe, ou=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.addAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=jane doe, ou=company"));
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(1, modificationItems.length);
ModificationItem modificationItem = modificationItems[0];
assertEquals(DirContext.ADD_ATTRIBUTE, modificationItem.getModificationOp());
assertEquals("uniqueMember", modificationItem.getAttribute().getID());
assertEquals("cn=jane doe, ou=company", modificationItem.getAttribute().get());
}
@Test
public void testSetDnAttributeValueIdentical() {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe, ou=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.setAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=john doe, ou=company"));
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(0, modificationItems.length);
}
@Test
public void testSetDnAttributesValueIdentical() {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe, ou=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.setAttributeValues("uniqueMember", new Object[]{LdapUtils.newLdapName("cn=john doe, ou=company")});
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(0, modificationItems.length);
}
@Test
public void testSetDnAttributesValuesOneNewEntry() throws NamingException {
BasicAttributes attributes = new BasicAttributes();
attributes.put("uniqueMember", "cn=john doe, ou=company");
DirContextAdapter tested = new DirContextAdapter(attributes, LdapUtils.newLdapName("cn=administrators, ou=groups"));
tested.setUpdateMode(true);
tested.setAttributeValues("uniqueMember", new Object[]{
LdapUtils.newLdapName("cn=john doe, ou=company"),
LdapUtils.newLdapName("cn=jane doe, ou=company")
});
ModificationItem[] modificationItems = tested.getModificationItems();
assertEquals(1, modificationItems.length);
ModificationItem modificationItem = modificationItems[0];
assertEquals(DirContext.ADD_ATTRIBUTE, modificationItem.getModificationOp());
assertEquals("uniqueMember", modificationItem.getAttribute().getID());
assertEquals("cn=jane doe, ou=company", modificationItem.getAttribute().get());
}
}

View File

@@ -0,0 +1,247 @@
package org.springframework.ldap.core;
import org.junit.Test;
import org.springframework.ldap.support.LdapUtils;
import javax.naming.NamingException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Mattias Hellborg Arthursson
*/
public class NameAwareAttributeTest {
@Test
public void testEqualsWithIdNotSame() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
NameAwareAttribute attr2 = new NameAwareAttribute("someOtherAttribute");
assertFalse(attr1.equals(attr2));
}
@Test
public void testEqualsWithSameIdNoValues() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
}
@Test
public void testEqualsUnorderedWithIdenticalAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add("value1");
attr1.add("value2");
attr1.add("value3");
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add("value1");
attr2.add("value2");
attr2.add("value3");
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
}
@Test
public void testEqualsUnorderedWithIdenticalArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 1});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(new byte[]{1, 2, 3});
attr2.add(new byte[]{3, 2, 1});
attr2.add(new byte[]{1});
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
}
@Test
public void testEqualsUnorderedWithDifferentOrderArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 1});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(new byte[]{3, 2, 1});
attr2.add(new byte[]{1});
attr2.add(new byte[]{1, 2, 3});
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
}
@Test
public void testEqualsUnorderedWithDifferentArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 2});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(new byte[]{1, 2, 3});
attr2.add(new byte[]{3, 2, 1});
attr2.add(new byte[]{1});
assertFalse(attr1.equals(attr2));
}
@Test
public void testEqualsUnorderedWithDifferentNumberOfArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 1});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(new byte[]{1, 2, 3});
attr2.add(new byte[]{1});
assertFalse(attr1.equals(attr2));
}
@Test
public void testEqualsOrderedWithIdenticalArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute", true);
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 1});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute", true);
attr2.add(new byte[]{1, 2, 3});
attr2.add(new byte[]{3, 2, 1});
attr2.add(new byte[]{1});
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
}
@Test
public void testEqualsOrderedWithArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute", true);
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 1});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute", true);
attr2.add(new byte[]{1, 2, 3});
attr2.add(new byte[]{3, 2, 1});
attr2.add(new byte[]{1});
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
}
@Test
public void testEqualsOrderedWithDifferentOrderArrayAttributes() {
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute", true);
attr1.add(new byte[]{1, 2, 3});
attr1.add(new byte[]{3, 2, 1});
attr1.add(new byte[]{1});
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute", true);
attr2.add(new byte[]{3, 2, 1});
attr2.add(new byte[]{1});
attr2.add(new byte[]{1, 2, 3});
assertFalse(attr1.equals(attr2));
}
@Test
public void testSameDistinguishedNameValue() throws NamingException {
String expectedName = "cn=John Doe,ou=People";
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(LdapUtils.newLdapName(expectedName));
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(LdapUtils.newLdapName(expectedName));
assertEquals(attr1, attr2);
assertEquals(attr1.hashCode(), attr2.hashCode());
assertEquals(expectedName, attr1.get());
assertEquals(expectedName, attr2.get());
}
@Test
public void testEqualDistinguishedNameValue() throws NamingException {
// The names here are syntactically equal, but differ in exact string representation
String expectedName1 = "cn=John Doe, OU=People";
String expectedName2 = "cn=John Doe,ou=People";
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(LdapUtils.newLdapName(expectedName1));
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(LdapUtils.newLdapName(expectedName2));
assertEquals(attr1, attr2);
assertEquals(attr1.hashCode(), attr2.hashCode());
assertEquals(expectedName1, attr1.get());
assertEquals(expectedName2, attr2.get());
}
@Test
public void testEqualDistinguishedNameValueUninitialized() throws NamingException {
// The names here are syntactically equal, but differ in exact string representation
String expectedName1 = "cn=John Doe, OU=People";
String expectedName2 = "cn=John Doe,ou=People";
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(expectedName1);
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(LdapUtils.newLdapName(expectedName2));
assertFalse(attr1.equals(attr2));
assertEquals(expectedName1, attr1.get());
assertEquals(expectedName2, attr2.get());
}
@Test
public void testEqualDistinguishedNameValueManuallyInitialized() throws NamingException {
// The names here are syntactically equal, but differ in exact string representation
String expectedName1 = "cn=John Doe, OU=People";
String expectedName2 = "cn=John Doe,ou=People";
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(expectedName1);
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(LdapUtils.newLdapName(expectedName2));
attr1.initValuesAsNames();
assertTrue(attr1.equals(attr2));
assertEquals(attr1.hashCode(), attr2.hashCode());
assertEquals(expectedName1, attr1.get());
assertEquals(expectedName2, attr2.get());
}
@Test
public void testUnequalDistinguishedNameValue() throws NamingException {
// The names here are syntactically equal, but differ in exact string representation
String expectedName1 = "cn=Jane Doe,ou=People";
String expectedName2 = "cn=John Doe,ou=People";
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(LdapUtils.newLdapName(expectedName1));
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(LdapUtils.newLdapName(expectedName2));
assertFalse(attr1.equals(attr2));
assertEquals(expectedName1, attr1.get());
assertEquals(expectedName2, attr2.get());
}
@Test
public void testComparingWDistinguishedNameValueWithInvalidName() throws NamingException {
// The names here are syntactically equal, but differ in exact string representation
String expectedName1 = "cn=Jane Doe,ou=People";
String expectedValue2 = "thisisnotavaliddn";
NameAwareAttribute attr1 = new NameAwareAttribute("someAttribute");
attr1.add(LdapUtils.newLdapName(expectedName1));
NameAwareAttribute attr2 = new NameAwareAttribute("someAttribute");
attr2.add(expectedValue2);
assertFalse(attr1.equals(attr2));
assertEquals(expectedName1, attr1.get());
assertEquals(expectedValue2, attr2.get());
}
}

View File

@@ -18,12 +18,14 @@ package org.springframework.ldap.core.support;
import org.junit.Before;
import org.junit.Test;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.NameAwareAttributes;
import org.springframework.ldap.support.LdapUtils;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import java.util.Hashtable;
@@ -54,7 +56,7 @@ public class DefaultDirObjectFactoryTest {
@Test
public void testGetObjectInstance() throws Exception {
BasicAttributes expectedAttributes = new BasicAttributes();
Attributes expectedAttributes = new NameAwareAttributes();
expectedAttributes.put("someAttribute", "someValue");
DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(contextMock, DN, null,
@@ -68,7 +70,7 @@ public class DefaultDirObjectFactoryTest {
@Test
public void testGetObjectInstance_CompositeName() throws Exception {
BasicAttributes expectedAttributes = new BasicAttributes();
Attributes expectedAttributes = new NameAwareAttributes();
expectedAttributes.put("someAttribute", "someValue");
CompositeName name = new CompositeName();
@@ -85,7 +87,7 @@ public class DefaultDirObjectFactoryTest {
@Test
public void testGetObjectInstance_nullObject() throws Exception {
BasicAttributes expectedAttributes = new BasicAttributes();
Attributes expectedAttributes = new NameAwareAttributes();
expectedAttributes.put("someAttribute", "someValue");
DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(null, DN, null, new Hashtable(),
@@ -97,7 +99,7 @@ public class DefaultDirObjectFactoryTest {
@Test
public void testGetObjectInstance_ObjectNotContext() throws Exception {
BasicAttributes expectedAttributes = new BasicAttributes();
Attributes expectedAttributes = new NameAwareAttributes();
expectedAttributes.put("someAttribute", "someValue");
DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(new Object(), DN, null,
@@ -114,7 +116,7 @@ public class DefaultDirObjectFactoryTest {
*/
@Test
public void testGetObjectInstance_BaseSet() throws Exception {
BasicAttributes expectedAttributes = new BasicAttributes();
Attributes expectedAttributes = new NameAwareAttributes();
expectedAttributes.put("someAttribute", "someValue");
when(contextMock2.getNameInNamespace()).thenReturn("dc=jayway, dc=se");

View File

@@ -51,7 +51,7 @@ public class DefaultObjectDirectoryMapperTest {
assertFalse(idAttribute.isBinary());
assertFalse(idAttribute.isDnAttribute());
assertFalse(idAttribute.isTransient());
assertFalse(idAttribute.isList());
assertFalse(idAttribute.isCollection());
assertField(entityData, "fullName", "cn", "cn", false, false, false);
assertField(entityData, "lastName", "sn", null, false, false, false);
@@ -115,7 +115,7 @@ public class DefaultObjectDirectoryMapperTest {
assertEquals(expectedBinary, attribute.isBinary());
assertEquals(expectedTransient, attribute.isTransient());
assertEquals(expectedList, attribute.isList());
assertEquals(expectedList, attribute.isCollection());
}
}
}

View File

@@ -15,6 +15,7 @@ ext.gsbaseVersion = '2.0.1'
ext.log4jVersion = '1.2.15'
ext.mockitoVersion = '1.9.5'
ext.queryDslVersion = '3.2.4'
ext.slf4jVersion = '1.7.5'
repositories {
mavenCentral()

View File

@@ -9,7 +9,9 @@ dependencies {
"org.springframework:spring-orm:$springVersion"
testCompile "org.springframework:spring-test:$springVersion",
"junit:junit:$junitVersion"
"junit:junit:$junitVersion",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}
test.enabled = false // TODO this should be enabled depending on build parameter

View File

@@ -13,7 +13,9 @@ dependencies {
"org.springframework:spring-test:$springVersion",
"gsbase:gsbase:$gsbaseVersion",
"junit:junit:$junitVersion",
"com.sun:ldapbp:1.0"
"com.sun:ldapbp:1.0",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}
test.enabled = false // TODO this should be enabled depending on build parameter

View File

@@ -18,5 +18,7 @@ dependencies {
"org.springframework:spring-context:$spring20Version",
"org.springframework:spring-core:$spring20Version",
"org.springframework:spring-dao:$spring20Version",
"org.springframework:spring-beans:$spring20Version"
"org.springframework:spring-beans:$spring20Version",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}

View File

@@ -23,5 +23,7 @@ dependencies {
"org.springframework:spring-core:$spring25Version",
"org.springframework:spring-tx:$spring25Version",
"org.springframework:spring-beans:$spring25Version",
"junit:junit:$junitVersion"
"junit:junit:$junitVersion",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}

View File

@@ -23,5 +23,6 @@ dependencies {
"org.springframework:spring-core:$spring30Version",
"org.springframework:spring-tx:$spring30Version",
"org.springframework:spring-beans:$spring30Version",
"junit:junit:$junitVersion"
"junit:junit:$junitVersion",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}

View File

@@ -13,7 +13,9 @@ dependencies {
testCompile "junit:junit:$junitVersion",
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-aop:$springVersion",
"gsbase:gsbase:$gsbaseVersion"
"gsbase:gsbase:$gsbaseVersion",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
}
test.enabled = false // TODO this should be enabled depending on build parameter

View File

@@ -46,7 +46,9 @@ dependencies {
"aspectj:aspectjweaver:1.5.3",
"hsqldb:hsqldb:1.8.0.7",
"junit:junit:$junitVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion"
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.slf4j:slf4j-log4j12:$slf4jVersion"
testCompile("org.springframework.security:spring-security-ldap:$springSecurityVersion") {
exclude group: "org.springframework.ldap", module: "spring-ldap-core"

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.itest.odm;
import org.springframework.ldap.odm.annotations.Attribute;
import org.springframework.ldap.odm.annotations.DnAttribute;
import org.springframework.ldap.odm.annotations.Entry;
import org.springframework.ldap.odm.annotations.Id;
import javax.naming.Name;
import java.util.Set;
/**
* @author Mattias Hellborg Arthursson
*/
@Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=groups")
public class Group {
@Id
private Name dn;
@Attribute(name="cn")
@DnAttribute("cn")
private String name;
@Attribute(name="uniqueMember")
private Set<Name> members;
public Name getDn() {
return dn;
}
public void setDn(Name dn) {
this.dn = dn;
}
public Set<Name> getMembers() {
return members;
}
public void setMembers(Set<Name> members) {
this.members = members;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addMember(Name member) {
members.add(member);
}
public void removeMember(Name member) {
members.remove(member);
}
}

View File

@@ -58,6 +58,16 @@ public class LdapTemplateBindUnbindITest extends
verifyCleanup();
}
@Test
public void testBindGroupOfUniqueNamesWithNameValues() {
DirContextAdapter ctx = new DirContextAdapter(LdapUtils.newLdapName("cn=TEST,ou=groups"));
ctx.addAttributeValue("cn", "TEST");
ctx.addAttributeValue("objectclass", "top");
ctx.addAttributeValue("objectclass", "groupOfUniqueNames");
ctx.addAttributeValue("uniqueMember", LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se"));
tested.bind(ctx);
}
@Test
public void testBindAndUnbindWithAttributesUsingLdapName() {
Attributes attributes = setupAttributes();

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.itest.odm;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.itest.AbstractLdapTemplateIntegrationTest;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.test.context.ContextConfiguration;
import javax.naming.Name;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
/**
* @author Mattias Hellborg Arthursson
*/
@ContextConfiguration(locations = {"/conf/ldapTemplateTestContext.xml"})
public class LdapTemplateOdmGroupManipulationITest extends AbstractLdapTemplateIntegrationTest {
@Autowired
private LdapTemplate tested;
@Test
public void testFindOne() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
assertNotNull(group);
assertEquals("ROLE_USER", group.getName());
assertEquals(5, group.getMembers().size());
Set<Name> members = group.getMembers();
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se")));
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se")));
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Norway,dc=jayway,dc=se")));
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company2,c=Sweden,dc=jayway,dc=se")));
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se")));
}
@Test
public void testRemoveMember() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
group.removeMember(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se"));
tested.update(group);
Group verification = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
Set<Name> members = verification.getMembers();
assertEquals(4, members.size());
assertFalse(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se")));
}
@Test
public void testRemoveMemberSyntacticallyEqual() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
group.removeMember(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden, DC=jayway,DC=se"));
tested.update(group);
Group verification = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
Set<Name> members = verification.getMembers();
assertEquals(4, members.size());
assertFalse(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se")));
}
@Test
public void testAddMember() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
group.addMember(LdapUtils.newLdapName("cn=Some Person+sn=Person,ou=company1,c=Norway,dc=jayway,dc=se"));
tested.update(group);
Group verification = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
Set<Name> members = verification.getMembers();
assertEquals(6, members.size());
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person+sn=Person,ou=company1,c=Norway,dc=jayway,dc=se")));
}
@Test
public void testAddMemberDuplicate() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
group.addMember(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se"));
tested.update(group);
Group verification = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
Set<Name> members = verification.getMembers();
assertEquals(5, members.size());
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se")));
}
@Test
public void testAddMemberSyntacticallyEqualDuplicate() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
group.addMember(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,DC=jayway,DC=se"));
tested.update(group);
Group verification = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
Set<Name> members = verification.getMembers();
assertEquals(5, members.size());
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se")));
}
@Test
public void testSetMembersSyntacticallyEqual() {
Group group = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
group.setMembers(new HashSet<Name>(){{
add(LdapUtils.newLdapName("CN=Some Person,OU=company1, C=Sweden, DC=jayway,DC=se"));
add(LdapUtils.newLdapName("CN=Some Person2, OU=company1,C=Sweden,DC=jayway, DC=se"));
}});
tested.update(group);
Group verification = tested.findOne(query().where("cn").is("ROLE_USER"), Group.class);
Set<Name> members = verification.getMembers();
assertEquals(2, members.size());
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se")));
assertTrue(members.contains(LdapUtils.newLdapName("cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se")));
}
}