package org.argeo.osgi.useradmin;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import javax.naming.directory.BasicAttribute;
import javax.naming.ldap.LdapName;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.argeo.osgi.useradmin.AbstractUserDirectory.WorkingCopy;
+
+/** Directory user implementation */
class LdifUser implements DirectoryUser {
private final AbstractUserDirectory userAdmin;
private final boolean frozen;
private Attributes publishedAttributes;
- private Attributes modifiedAttributes = null;
private final AttributeDictionary properties;
private final AttributeDictionary credentials;
@Override
public boolean hasCredential(String key, Object value) {
+ if (key == null) {
+ // TODO check other sources (like PKCS12)
+ char[] password = toChars(value);
+ byte[] hashedPassword = hash(password);
+ return hasCredential(LdifName.userpassword.name(), hashedPassword);
+ }
+
Object storedValue = getCredentials().get(key);
if (storedValue == null || value == null)
return false;
return false;
}
+ /** Hash and clear the password */
+ private byte[] hash(char[] password) {
+ byte[] hashedPassword = ("{SHA}" + Base64
+ .encodeBase64String(DigestUtils.sha1(toBytes(password))))
+ .getBytes();
+ Arrays.fill(password, '\u0000');
+ return hashedPassword;
+ }
+
+ private byte[] toBytes(char[] chars) {
+ CharBuffer charBuffer = CharBuffer.wrap(chars);
+ ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
+ byte[] bytes = Arrays.copyOfRange(byteBuffer.array(),
+ byteBuffer.position(), byteBuffer.limit());
+ Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
+ Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
+ return bytes;
+ }
+
+ private char[] toChars(Object obj) {
+ if (obj instanceof char[])
+ return (char[]) obj;
+ if (!(obj instanceof byte[]))
+ throw new IllegalArgumentException(obj.getClass()
+ + " is not a byte array");
+ ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj);
+ CharBuffer toBuffer = Charset.forName("UTF-8").decode(fromBuffer);
+ char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(),
+ toBuffer.limit());
+ Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data
+ Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data
+ Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data
+ return res;
+ }
+
@Override
public LdapName getDn() {
return dn;
@Override
public synchronized Attributes getAttributes() {
- return isEditing() ? modifiedAttributes : publishedAttributes;
+ return isEditing() ? getModifiedAttributes() : publishedAttributes;
+ }
+
+ /** Should only be called from working copy thread. */
+ private synchronized Attributes getModifiedAttributes() {
+ assert getWc() != null;
+ return getWc().getAttributes(getDn());
}
protected synchronized boolean isEditing() {
- return userAdmin.isEditing() && modifiedAttributes != null;
+ return getWc() != null && getModifiedAttributes() != null;
+ }
+
+ private synchronized WorkingCopy getWc() {
+ return userAdmin.getWorkingCopy();
}
protected synchronized void startEditing() {
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();
+ assert getModifiedAttributes() == null;
+ getWc().startEditing(this);
+ // modifiedAttributes = (Attributes) publishedAttributes.clone();
}
- protected synchronized void stopEditing(boolean apply) {
- assert modifiedAttributes != null;
- if (apply)
- publishedAttributes = modifiedAttributes;
- modifiedAttributes = null;
+ public synchronized void publishAttributes(Attributes modifiedAttributes) {
+ publishedAttributes = modifiedAttributes;
}
+ // protected synchronized void stopEditing(boolean apply) {
+ // assert getModifiedAttributes() != null;
+ // if (apply)
+ // publishedAttributes = getModifiedAttributes();
+ // // modifiedAttributes = null;
+ // }
+
public DirectoryUser getPublished() {
return new LdifUser(userAdmin, dn, publishedAttributes, true);
}
@Override
public Object put(String key, Object value) {
+ if (key == null) {
+ // TODO persist to other sources (like PKCS12)
+ char[] password = toChars(value);
+ byte[] hashedPassword = hash(password);
+ return put(LdifName.userpassword.name(), hashedPassword);
+ }
+
userAdmin.checkEdit();
if (!isEditing())
startEditing();
throw new IllegalArgumentException("Key " + key + " excluded");
try {
- Attribute attribute = modifiedAttributes.get(key.toString());
+ Attribute attribute = getModifiedAttributes().get(
+ key.toString());
attribute = new BasicAttribute(key.toString());
attribute.add(value);
- Attribute previousAttribute = modifiedAttributes.put(attribute);
+ Attribute previousAttribute = getModifiedAttributes().put(
+ attribute);
if (previousAttribute != null)
return previousAttribute.get();
else
throw new IllegalArgumentException("Key " + key + " excluded");
try {
- Attribute attr = modifiedAttributes.remove(key.toString());
+ Attribute attr = getModifiedAttributes().remove(key.toString());
if (attr != null)
return attr.get();
else