package org.argeo.osgi.useradmin;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Dictionary;
-import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+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.ldap.LdapName;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
+class LdifUser implements DirectoryUser {
+ private final AbstractUserDirectory userAdmin;
-class LdifUser implements User {
private final LdapName dn;
- private final Attributes attributes;
- LdifUser(LdapName dn, Attributes attributes) {
+ private final boolean frozen;
+ private Attributes publishedAttributes;
+ private Attributes modifiedAttributes = null;
+
+ private final AttributeDictionary properties;
+ private final AttributeDictionary credentials;
+
+ LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) {
+ this(userAdmin, dn, attributes, false);
+ }
+
+ private LdifUser(AbstractUserDirectory userAdmin, LdapName dn,
+ Attributes attributes, boolean frozen) {
+ this.userAdmin = userAdmin;
this.dn = dn;
- this.attributes = attributes;
+ this.publishedAttributes = attributes;
+ properties = new AttributeDictionary(false);
+ credentials = new AttributeDictionary(true);
+ this.frozen = frozen;
}
@Override
@Override
public Dictionary<String, Object> getProperties() {
- if (attributes == null)
- throw new ArgeoUserAdminException(
- "Must be loaded from user admin service");
- return new AttributeDictionary(attributes);
+ return properties;
}
@Override
public Dictionary<String, Object> getCredentials() {
- // TODO Auto-generated method stub
- return null;
+ return credentials;
}
@Override
public boolean hasCredential(String key, Object value) {
- // TODO Auto-generated method stub
+ Object storedValue = getCredentials().get(key);
+ if (storedValue == null || value == null)
+ return false;
+ if (!(value instanceof String || value instanceof byte[]))
+ return false;
+ if (storedValue instanceof String && value instanceof String)
+ return storedValue.equals(value);
+ if (storedValue instanceof byte[] && value instanceof byte[])
+ return Arrays.equals((byte[]) storedValue, (byte[]) value);
return false;
}
- protected LdapName getDn() {
+ @Override
+ public LdapName getDn() {
return dn;
}
- protected Attributes getAttributes() {
- return attributes;
+ @Override
+ public synchronized Attributes getAttributes() {
+ return isEditing() ? modifiedAttributes : publishedAttributes;
+ }
+
+ protected synchronized boolean isEditing() {
+ return userAdmin.isEditing() && modifiedAttributes != null;
+ }
+
+ protected synchronized void startEditing() {
+ if (frozen)
+ throw new UserDirectoryException("Cannot edit frozen view");
+ if (getUserAdmin().isReadOnly())
+ throw new UserDirectoryException("User directory is read-only");
+ assert modifiedAttributes == null;
+ modifiedAttributes = (Attributes) publishedAttributes.clone();
+ }
+
+ protected synchronized void stopEditing(boolean apply) {
+ assert modifiedAttributes != null;
+ if (apply)
+ publishedAttributes = modifiedAttributes;
+ modifiedAttributes = null;
+ }
+
+ public DirectoryUser getPublished() {
+ return new LdifUser(userAdmin, dn, publishedAttributes, true);
+ }
+
+ @Override
+ public int hashCode() {
+ return dn.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj instanceof LdifUser) {
+ LdifUser that = (LdifUser) obj;
+ return this.dn.equals(that.dn);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return dn.toString();
+ }
+
+ protected AbstractUserDirectory getUserAdmin() {
+ return userAdmin;
+ }
+
+ private class AttributeDictionary extends Dictionary<String, Object> {
+ private final List<String> effectiveKeys = new ArrayList<String>();
+ private final List<String> attrFilter;
+ private final Boolean includeFilter;
+
+ public AttributeDictionary(Boolean includeFilter) {
+ this.attrFilter = userAdmin.getCredentialAttributeIds();
+ this.includeFilter = includeFilter;
+ try {
+ NamingEnumeration<String> ids = getAttributes().getIDs();
+ while (ids.hasMore()) {
+ String id = ids.next();
+ if (includeFilter && attrFilter.contains(id))
+ effectiveKeys.add(id);
+ else if (!includeFilter && !attrFilter.contains(id))
+ effectiveKeys.add(id);
+ }
+ } catch (NamingException e) {
+ throw new UserDirectoryException(
+ "Cannot initialise attribute dictionary", e);
+ }
+ }
+
+ @Override
+ public int size() {
+ return effectiveKeys.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return effectiveKeys.size() == 0;
+ }
+
+ @Override
+ public Enumeration<String> keys() {
+ return Collections.enumeration(effectiveKeys);
+ }
+
+ @Override
+ public Enumeration<Object> elements() {
+ final Iterator<String> it = effectiveKeys.iterator();
+ return new Enumeration<Object>() {
+
+ @Override
+ public boolean hasMoreElements() {
+ return it.hasNext();
+ }
+
+ @Override
+ public Object nextElement() {
+ String key = it.next();
+ try {
+ return getAttributes().get(key).get();
+ } catch (NamingException e) {
+ throw new UserDirectoryException(
+ "Cannot get value for key " + key, e);
+ }
+ }
+
+ };
+ }
+
+ @Override
+ public Object get(Object key) {
+ try {
+ Attribute attr = getAttributes().get(key.toString());
+ if (attr == null)
+ return null;
+ return attr.get();
+ } catch (NamingException e) {
+ throw new UserDirectoryException(
+ "Cannot get value for attribute " + key, e);
+ }
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ userAdmin.checkEdit();
+ if (!isEditing())
+ startEditing();
+
+ if (!(value instanceof String || value instanceof byte[]))
+ throw new IllegalArgumentException(
+ "Value must be String or byte[]");
+
+ if (includeFilter && !attrFilter.contains(key))
+ throw new IllegalArgumentException("Key " + key
+ + " not included");
+ else if (!includeFilter && attrFilter.contains(key))
+ throw new IllegalArgumentException("Key " + key + " excluded");
+
+ try {
+ Attribute attribute = modifiedAttributes.get(key.toString());
+ attribute = new BasicAttribute(key.toString());
+ attribute.add(value);
+ Attribute previousAttribute = modifiedAttributes.put(attribute);
+ if (previousAttribute != null)
+ return previousAttribute.get();
+ else
+ return null;
+ } catch (NamingException e) {
+ throw new UserDirectoryException(
+ "Cannot get value for attribute " + key, e);
+ }
+ }
+
+ @Override
+ public Object remove(Object key) {
+ userAdmin.checkEdit();
+ if (!isEditing())
+ startEditing();
+
+ if (includeFilter && !attrFilter.contains(key))
+ throw new IllegalArgumentException("Key " + key
+ + " not included");
+ else if (!includeFilter && attrFilter.contains(key))
+ throw new IllegalArgumentException("Key " + key + " excluded");
+
+ try {
+ Attribute attr = modifiedAttributes.remove(key.toString());
+ if (attr != null)
+ return attr.get();
+ else
+ return null;
+ } catch (NamingException e) {
+ throw new UserDirectoryException("Cannot remove attribute "
+ + key, e);
+ }
+ }
}
}