X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.enterprise%2Fsrc%2Forg%2Fargeo%2Fosgi%2Fuseradmin%2FLdifUser.java;h=90b8daa0eff0603c3f50e1398586a505e9dee192;hb=2111a76162534100967eb0e7733632e80d9ddc5f;hp=866c48c6b4c8d64d0abfe879e9482c8fd74770a9;hpb=e66b9893b0e511f8ab295e3cee42b7dc966f1597;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java index 866c48c6b..90b8daa0e 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java @@ -1,9 +1,8 @@ package org.argeo.osgi.useradmin; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -22,6 +21,10 @@ import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.ldap.LdapName; +import org.argeo.naming.AuthPassword; +import org.argeo.naming.LdapAttrs; +import org.argeo.naming.SharedSecret; + /** Directory user implementation */ class LdifUser implements DirectoryUser { private final AbstractUserDirectory userAdmin; @@ -71,11 +74,72 @@ class LdifUser implements DirectoryUser { public boolean hasCredential(String key, Object value) { if (key == null) { // TODO check other sources (like PKCS12) + // String pwd = new String((char[]) value); + // authPassword (RFC 312 https://tools.ietf.org/html/rfc3112) char[] password = toChars(value); + AuthPassword authPassword = AuthPassword.matchAuthValue(getAttributes(), password); + if (authPassword != null) { + if (authPassword.getAuthScheme().equals(SharedSecret.X_SHARED_SECRET)) { + SharedSecret onceToken = new SharedSecret(authPassword); + if (onceToken.isExpired()) { + // AuthPassword.remove(getAttributes(), onceToken); + return false; + } else { + // boolean wasRemoved = AuthPassword.remove(getAttributes(), onceToken); + return true; + } + // TODO delete expired tokens? + } else { + // TODO implement SHA + throw new UnsupportedOperationException( + "Unsupported authPassword scheme " + authPassword.getAuthScheme()); + } + } + + // Regular password byte[] hashedPassword = hash(password); - return hasCredential(LdifName.userPassword.name(), hashedPassword); + if (hasCredential(LdapAttrs.userPassword.name(), hashedPassword)) + return true; + // if (hasCredential(LdapAttrs.authPassword.name(), pwd)) + // return true; + return false; } + // authPassword (RFC 3112 https://tools.ietf.org/html/rfc3112) + // if (key.startsWith(ClientToken.X_CLIENT_TOKEN)) { + // return ClientToken.checkAttribute(getAttributes(), key, value); + // } else if (key.startsWith(OnceToken.X_ONCE_TOKEN)) { + // return OnceToken.checkAttribute(getAttributes(), key, value); + // } + // StringTokenizer st = new StringTokenizer((String) storedValue, "$ "); + // // TODO make it more robust, deal with bad formatting + // String authScheme = st.nextToken(); + // String authInfo = st.nextToken(); + // String authValue = st.nextToken(); + // if (authScheme.equals(UriToken.X_URI_TOKEN)) { + // UriToken token = new UriToken((String)storedValue); + // try { + // URI uri = new URI(authInfo); + // Map> query = NamingUtils.queryToMap(uri); + // String expiryTimestamp = NamingUtils.getQueryValue(query, + // LdapAttrs.modifyTimestamp.name()); + // if (expiryTimestamp != null) { + // Instant expiryOdt = NamingUtils.ldapDateToInstant(expiryTimestamp); + // if (expiryOdt.isBefore(Instant.now())) + // return false; + // } else { + // throw new UnsupportedOperationException("An expiry timestamp " + // + LdapAttrs.modifyTimestamp.name() + " must be set in the URI query"); + // } + // byte[] hash = Base64.getDecoder().decode(authValue); + // byte[] hashedInput = DigestUtils.sha1((authInfo + + // value).getBytes(StandardCharsets.US_ASCII)); + // return Arrays.equals(hash, hashedInput); + // } catch (URISyntaxException e) { + // throw new UserDirectoryException("Badly formatted " + authInfo, e); + // } + // } + Object storedValue = getCredentials().get(key); if (storedValue == null || value == null) return false; @@ -91,16 +155,16 @@ class LdifUser implements DirectoryUser { /** Hash and clear the password */ private byte[] hash(char[] password) { byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1(toBytes(password)))) - .getBytes(); - Arrays.fill(password, '\u0000'); + .getBytes(StandardCharsets.UTF_8); + // Arrays.fill(password, '\u0000'); return hashedPassword; } private byte[] toBytes(char[] chars) { CharBuffer charBuffer = CharBuffer.wrap(chars); - ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer); + ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); - Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data + // Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data return bytes; } @@ -111,7 +175,7 @@ class LdifUser implements DirectoryUser { 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); + CharBuffer toBuffer = StandardCharsets.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 @@ -250,14 +314,14 @@ class LdifUser implements DirectoryUser { return null; Object value = attr.get(); if (value instanceof byte[]) { - if (key.equals(LdifName.userPassword.name())) + if (key.equals(LdapAttrs.userPassword.name())) // TODO other cases (certificates, images) return value; - value = new String((byte[]) value, Charset.forName("UTF-8")); + value = new String((byte[]) value, StandardCharsets.UTF_8); } if (attr.size() == 1) return value; - if (!attr.getID().equals(LdifName.objectClass.name())) + if (!attr.getID().equals(LdapAttrs.objectClass.name())) return value; // special case for object class NamingEnumeration en = attr.getAll(); @@ -284,7 +348,10 @@ class LdifUser implements DirectoryUser { // TODO persist to other sources (like PKCS12) char[] password = toChars(value); byte[] hashedPassword = hash(password); - return put(LdifName.userPassword.name(), hashedPassword); + return put(LdapAttrs.userPassword.name(), hashedPassword); + } + if (key.startsWith("X-")) { + return put(LdapAttrs.authPassword.name(), value); } userAdmin.checkEdit(); @@ -301,13 +368,10 @@ class LdifUser implements DirectoryUser { try { Attribute attribute = getModifiedAttributes().get(key.toString()); - attribute = new BasicAttribute(key.toString()); + if (attribute == null) + attribute = new BasicAttribute(key.toString()); if (value instanceof String && !isAsciiPrintable(((String) value))) - try { - attribute.add(((String) value).getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new UserDirectoryException("Cannot encode " + value, e); - } + attribute.add(((String) value).getBytes(StandardCharsets.UTF_8)); else attribute.add(value); Attribute previousAttribute = getModifiedAttributes().put(attribute);