X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.enterprise%2Fsrc%2Forg%2Fargeo%2Fosgi%2Fuseradmin%2FLdifUser.java;h=4eab8cd87023a0cd5b3f791979af0b1e70ef52ce;hb=6338d85d3f970dd0eb8845693ddad90a93b99d03;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..4eab8cd87 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,11 @@ package org.argeo.osgi.useradmin; -import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -13,7 +15,9 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.StringTokenizer; import javax.naming.NamingEnumeration; import javax.naming.NamingException; @@ -22,6 +26,9 @@ import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.ldap.LdapName; +import org.argeo.naming.LdapAttrs; +import org.argeo.naming.NamingUtils; + /** Directory user implementation */ class LdifUser implements DirectoryUser { private final AbstractUserDirectory userAdmin; @@ -71,12 +78,47 @@ 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); char[] password = toChars(value); 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; } Object storedValue = getCredentials().get(key); + + // authPassword (RFC 312 https://tools.ietf.org/html/rfc3112) + if (LdapAttrs.authPassword.name().equals(key)) { + 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("X-Node-Token")) { + 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); + } + } + } + if (storedValue == null || value == null) return false; if (!(value instanceof String || value instanceof byte[])) @@ -91,16 +133,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 +153,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 +292,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 +326,7 @@ 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); } userAdmin.checkEdit(); @@ -303,11 +345,7 @@ class LdifUser implements DirectoryUser { Attribute attribute = getModifiedAttributes().get(key.toString()); 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);