From 700f034e0772a4d3e33a31227b85ef6a8486e78d Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 24 Aug 2015 14:29:04 +0000 Subject: [PATCH] Introduce LDIF parser git-svn-id: https://svn.argeo.org/commons/trunk@8332 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- org.argeo.security.core/bnd.bnd | 2 + org.argeo.security.core/build.properties | 1 - .../argeo/osgi/useradmin/LdifParserTest.java | 31 ++++++ .../test/org/argeo/osgi/useradmin/test.ldif | 47 ++++++++ .../org/argeo/osgi/useradmin/LdifParser.java | 105 ++++++++++++++++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java create mode 100644 org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/test.ldif create mode 100644 org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java diff --git a/org.argeo.security.core/bnd.bnd b/org.argeo.security.core/bnd.bnd index 9124816cb..4d6f374a1 100644 --- a/org.argeo.security.core/bnd.bnd +++ b/org.argeo.security.core/bnd.bnd @@ -2,4 +2,6 @@ Bundle-ActivationPolicy: lazy Import-Package:org.bouncycastle.*;resolution:=optional,\ org.springframework.util,\ javax.jcr.security,\ +org.apache.commons.codec,\ +org.apache.commons.codec.digest,\ * diff --git a/org.argeo.security.core/build.properties b/org.argeo.security.core/build.properties index 3ad70fd32..1047348bb 100644 --- a/org.argeo.security.core/build.properties +++ b/org.argeo.security.core/build.properties @@ -2,5 +2,4 @@ source.. = src/,\ ext/test/ additional.bundles = org.junit,\ org.slf4j.commons.logging,\ - org.apache.commons.codec,\ org.springframework.security.core diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java new file mode 100644 index 000000000..dd32dca17 --- /dev/null +++ b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java @@ -0,0 +1,31 @@ +package org.argeo.osgi.useradmin; + +import java.util.SortedMap; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; + +import junit.framework.TestCase; + +public class LdifParserTest extends TestCase { + public void testSimpleLdif() throws Exception { + LdifParser ldifParser = new LdifParser(); + SortedMap res = ldifParser.read(getClass() + .getResourceAsStream("test.ldif")); + LdapName rootDn = new LdapName( + "uid=root,ou=People,dc=demo,dc=example,dc=org"); + Attributes rootAttributes = res.get(rootDn); + assertNotNull(rootAttributes); + assertEquals("Superuser", rootAttributes.get("description").get()); + byte[] rawPwEntry = (byte[]) rootAttributes.get("userpassword").get(); + assertEquals("{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=", + new String(rawPwEntry)); + byte[] hashedPassword = DigestUtils.sha1("demo".getBytes()); + assertEquals("{SHA}" + Base64.encodeBase64String(hashedPassword), + new String(rawPwEntry)); + + } +} diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/test.ldif b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/test.ldif new file mode 100644 index 000000000..0d2e8ba8f --- /dev/null +++ b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/test.ldif @@ -0,0 +1,47 @@ +dn: dc=demo,dc=example,dc=org +objectClass: domain +objectClass: extensibleObject +objectClass: top +dc: demo + +dn: ou=Roles,dc=demo,dc=example,dc=org +objectClass: organizationalUnit +objectClass: top +ou: Roles + +dn: ou=People,dc=demo,dc=example,dc=org +objectClass: organizationalUnit +objectClass: top +ou: People + +dn: uid=demo,ou=People,dc=demo,dc=example,dc=org +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: demo User +description: Demo user +givenname: Demo +mail: demo@localhost +sn: User +uid: demo +userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 + +dn: uid=root,ou=People,dc=demo,dc=example,dc=org +objectClass: person +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: top +cn: demo User +description: Superuser +givenname: Root +mail: root@localhost +sn: Root +uid: root +userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 + +dn: cn=admin,ou=Roles,dc=demo,dc=example,dc=org +objectClass: groupOfNames +objectClass: top +cn: admin +member: uid=root,ou=People,dc=demo,dc=example,dc=org diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java new file mode 100644 index 000000000..1e9390a5c --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifParser.java @@ -0,0 +1,105 @@ +package org.argeo.osgi.useradmin; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.naming.InvalidNameException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.ldap.LdapName; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** Basic LDIF parser. */ +class LdifParser { + private final static Log log = LogFactory.getLog(LdifParser.class); + + SortedMap read(InputStream in) throws IOException { + SortedMap res = new TreeMap(); + try { + List lines = IOUtils.readLines(in); + + LdapName currentDn = null; + Attributes currentAttributes = null; + StringBuilder currentEntry = new StringBuilder(); + + readLines: for (String line : lines) { + if (line.startsWith(" ")) { + currentEntry.append(line.substring(1)); + continue readLines; + } else { + if (currentEntry.length() != 0) { + // read previous attribute + StringBuilder attrId = new StringBuilder(8); + boolean isBase64 = false; + readAttrId: for (int i = 0; i < currentEntry.length(); i++) { + char c = currentEntry.charAt(i); + if (c == ':') { + if (i + 1 < currentEntry.length() + && currentEntry.charAt(i + 1) == ':') + isBase64 = true; + currentEntry.delete(0, i + (isBase64 ? 2 : 1)); + break readAttrId; + } else { + attrId.append(c); + } + } + + String attributeId = attrId.toString(); + String cleanValueStr = currentEntry.toString().trim(); + Object attributeValue = isBase64 ? Base64 + .decodeBase64(cleanValueStr) : cleanValueStr; + + // manage DN attributes + if (attributeId.equals("dn")) { + if (currentDn != null) { + Attributes previous = res.put(currentDn, + currentAttributes); + if (previous != null) { + log.warn("There was already an entry with DN " + + currentDn + + ", which has been discarded by a subsequent one."); + } + } + + try { + currentDn = new LdapName( + attributeValue.toString()); + currentAttributes = new BasicAttributes(); + } catch (InvalidNameException e) { + log.error(attributeValue + + " not a valid DN, skipping the entry."); + currentDn = null; + currentAttributes = null; + } + } + + // store attribute + if (currentAttributes != null) { + Attribute attribute = currentAttributes + .get(attributeId); + if (attribute == null) { + attribute = new BasicAttribute(attributeId); + currentAttributes.put(attribute); + } + attribute.add(attributeValue); + } + currentEntry = new StringBuilder(); + } + currentEntry.append(line); + } + } + } finally { + IOUtils.closeQuietly(in); + } + return res; + } +} \ No newline at end of file -- 2.30.2