]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java
Improve documentation
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / LdifUser.java
index cd5401a55794273ffd350c9e341d025d4bd6425c..4f0a56a6075ac7e3b6dbfb120984bc49c61b1e22 100644 (file)
@@ -1,5 +1,8 @@
 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;
@@ -15,6 +18,11 @@ import javax.naming.directory.Attributes;
 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;
 
@@ -22,7 +30,6 @@ class LdifUser implements DirectoryUser {
 
        private final boolean frozen;
        private Attributes publishedAttributes;
-       private Attributes modifiedAttributes = null;
 
        private final AttributeDictionary properties;
        private final AttributeDictionary credentials;
@@ -63,6 +70,13 @@ class LdifUser implements DirectoryUser {
 
        @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;
@@ -75,6 +89,41 @@ class LdifUser implements DirectoryUser {
                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;
@@ -82,11 +131,21 @@ class LdifUser implements DirectoryUser {
 
        @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() {
@@ -94,17 +153,22 @@ class LdifUser implements DirectoryUser {
                        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);
        }
@@ -211,6 +275,13 @@ class LdifUser implements DirectoryUser {
 
                @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();
@@ -226,10 +297,12 @@ class LdifUser implements DirectoryUser {
                                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
@@ -253,7 +326,7 @@ class LdifUser implements DirectoryUser {
                                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