View Javadoc
1   package org.argeo.naming;
2   
3   import static org.argeo.naming.LdapAttrs.DN;
4   import static org.argeo.naming.LdapAttrs.member;
5   import static org.argeo.naming.LdapAttrs.objectClass;
6   import static org.argeo.naming.LdapAttrs.uniqueMember;
7   
8   import java.io.IOException;
9   import java.io.OutputStream;
10  import java.io.OutputStreamWriter;
11  import java.io.Writer;
12  import java.nio.charset.Charset;
13  import java.nio.charset.StandardCharsets;
14  import java.util.Base64;
15  import java.util.Map;
16  import java.util.SortedSet;
17  import java.util.TreeSet;
18  
19  import javax.naming.NamingEnumeration;
20  import javax.naming.NamingException;
21  import javax.naming.directory.Attribute;
22  import javax.naming.directory.Attributes;
23  import javax.naming.ldap.LdapName;
24  import javax.naming.ldap.Rdn;
25  
26  import org.argeo.osgi.useradmin.UserDirectoryException;
27  
28  /** Basic LDIF writer */
29  public class LdifWriter {
30  	private final static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
31  	private final Writer writer;
32  
33  	/** Writer must be closed by caller */
34  	public LdifWriter(Writer writer) {
35  		this.writer = writer;
36  	}
37  
38  	/** Stream must be closed by caller */
39  	public LdifWriter(OutputStream out) {
40  		this(new OutputStreamWriter(out, DEFAULT_CHARSET));
41  	}
42  
43  	public void writeEntry(LdapName name, Attributes attributes) throws IOException {
44  		try {
45  			// check consistency
46  			Rdn nameRdn = name.getRdn(name.size() - 1);
47  			Attribute nameAttr = attributes.get(nameRdn.getType());
48  			if (!nameAttr.get().equals(nameRdn.getValue()))
49  				throw new UserDirectoryException(
50  						"Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + name);
51  
52  			writer.append(DN + ": ").append(name.toString()).append('\n');
53  			Attribute objectClassAttr = attributes.get(objectClass.name());
54  			if (objectClassAttr != null)
55  				writeAttribute(objectClassAttr);
56  			attributes: for (NamingEnumeration<? extends Attribute> attrs = attributes.getAll(); attrs.hasMore();) {
57  				Attribute attribute = attrs.next();
58  				if (attribute.getID().equals(DN) || attribute.getID().equals(objectClass.name()))
59  					continue attributes;// skip DN attribute
60  				if (attribute.getID().equals(member.name()) || attribute.getID().equals(uniqueMember.name()))
61  					continue attributes;// skip member and uniqueMember attributes, so that they are always written last
62  				writeAttribute(attribute);
63  			}
64  			// write member and uniqueMember attributes last
65  			for (NamingEnumeration<? extends Attribute> attrs = attributes.getAll(); attrs.hasMore();) {
66  				Attribute attribute = attrs.next();
67  				if (attribute.getID().equals(member.name()) || attribute.getID().equals(uniqueMember.name()))
68  					writeMemberAttribute(attribute);
69  			}
70  			writer.append('\n');
71  			writer.flush();
72  		} catch (NamingException e) {
73  			throw new UserDirectoryException("Cannot write LDIF", e);
74  		}
75  	}
76  
77  	public void write(Map<LdapName, Attributes> entries) throws IOException {
78  		for (LdapName dn : entries.keySet())
79  			writeEntry(dn, entries.get(dn));
80  	}
81  
82  	protected void writeAttribute(Attribute attribute) throws NamingException, IOException {
83  		for (NamingEnumeration<?> attrValues = attribute.getAll(); attrValues.hasMore();) {
84  			Object value = attrValues.next();
85  			if (value instanceof byte[]) {
86  				String encoded = Base64.getEncoder().encodeToString((byte[]) value);
87  				writer.append(attribute.getID()).append(":: ").append(encoded).append('\n');
88  			} else {
89  				writer.append(attribute.getID()).append(": ").append(value.toString()).append('\n');
90  			}
91  		}
92  	}
93  
94  	protected void writeMemberAttribute(Attribute attribute) throws NamingException, IOException {
95  		// Note: duplicate entries will be swallowed
96  		SortedSet<String> values = new TreeSet<>();
97  		for (NamingEnumeration<?> attrValues = attribute.getAll(); attrValues.hasMore();) {
98  			String value = attrValues.next().toString();
99  			values.add(value);
100 		}
101 
102 		for (String value : values) {
103 			writer.append(attribute.getID()).append(": ").append(value).append('\n');
104 		}
105 	}
106 }