]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.enterprise/src/org/argeo/naming/LdifParser.java
Come back to previous approach for rejecting WebSocket based on
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / naming / LdifParser.java
1 package org.argeo.naming;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.io.Reader;
8 import java.nio.charset.Charset;
9 import java.nio.charset.StandardCharsets;
10 import java.util.ArrayList;
11 import java.util.Base64;
12 import java.util.List;
13 import java.util.SortedMap;
14 import java.util.TreeMap;
15
16 import javax.naming.InvalidNameException;
17 import javax.naming.NamingException;
18 import javax.naming.directory.Attribute;
19 import javax.naming.directory.Attributes;
20 import javax.naming.directory.BasicAttribute;
21 import javax.naming.directory.BasicAttributes;
22 import javax.naming.ldap.LdapName;
23 import javax.naming.ldap.Rdn;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.argeo.osgi.useradmin.UserDirectoryException;
28
29 /** Basic LDIF parser. */
30 public class LdifParser {
31 private final static Log log = LogFactory.getLog(LdifParser.class);
32 private final static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
33
34 protected Attributes addAttributes(SortedMap<LdapName, Attributes> res, int lineNumber, LdapName currentDn,
35 Attributes currentAttributes) {
36 try {
37 Rdn nameRdn = currentDn.getRdn(currentDn.size() - 1);
38 Attribute nameAttr = currentAttributes.get(nameRdn.getType());
39 if (nameAttr == null)
40 currentAttributes.put(nameRdn.getType(), nameRdn.getValue());
41 else if (!nameAttr.get().equals(nameRdn.getValue()))
42 throw new UserDirectoryException(
43 "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + currentDn
44 + " (shortly before line " + lineNumber + " in LDIF file)");
45 Attributes previous = res.put(currentDn, currentAttributes);
46 if (log.isTraceEnabled())
47 log.trace("Added " + currentDn);
48 return previous;
49 } catch (NamingException e) {
50 throw new UserDirectoryException("Cannot add " + currentDn, e);
51 }
52 }
53
54 /** With UTF-8 charset */
55 public SortedMap<LdapName, Attributes> read(InputStream in) throws IOException {
56 try (Reader reader = new InputStreamReader(in, DEFAULT_CHARSET)) {
57 return read(reader);
58 } finally {
59 try {
60 in.close();
61 } catch (IOException e) {
62 if (log.isTraceEnabled())
63 log.error("Cannot close stream", e);
64 }
65 }
66 }
67
68 /** Will close the reader. */
69 public SortedMap<LdapName, Attributes> read(Reader reader) throws IOException {
70 SortedMap<LdapName, Attributes> res = new TreeMap<LdapName, Attributes>();
71 try {
72 List<String> lines = new ArrayList<>();
73 try (BufferedReader br = new BufferedReader(reader)) {
74 String line;
75 while ((line = br.readLine()) != null) {
76 lines.add(line);
77 }
78 }
79 if (lines.size() == 0)
80 return res;
81 // add an empty new line since the last line is not checked
82 if (!lines.get(lines.size() - 1).equals(""))
83 lines.add("");
84
85 LdapName currentDn = null;
86 Attributes currentAttributes = null;
87 StringBuilder currentEntry = new StringBuilder();
88
89 readLines: for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
90 String line = lines.get(lineNumber);
91 boolean isLastLine = false;
92 if (lineNumber == lines.size() - 1)
93 isLastLine = true;
94 if (line.startsWith(" ")) {
95 currentEntry.append(line.substring(1));
96 if (!isLastLine)
97 continue readLines;
98 }
99
100 if (currentEntry.length() != 0 || isLastLine) {
101 // read previous attribute
102 StringBuilder attrId = new StringBuilder(8);
103 boolean isBase64 = false;
104 readAttrId: for (int i = 0; i < currentEntry.length(); i++) {
105 char c = currentEntry.charAt(i);
106 if (c == ':') {
107 if (i + 1 < currentEntry.length() && currentEntry.charAt(i + 1) == ':')
108 isBase64 = true;
109 currentEntry.delete(0, i + (isBase64 ? 2 : 1));
110 break readAttrId;
111 } else {
112 attrId.append(c);
113 }
114 }
115
116 String attributeId = attrId.toString();
117 // TODO should we really trim the end of the string as well?
118 String cleanValueStr = currentEntry.toString().trim();
119 Object attributeValue = isBase64 ? Base64.getDecoder().decode(cleanValueStr) : cleanValueStr;
120
121 // manage DN attributes
122 if (attributeId.equals(LdapAttrs.DN) || isLastLine) {
123 if (currentDn != null) {
124 //
125 // ADD
126 //
127 Attributes previous = addAttributes(res, lineNumber, currentDn, currentAttributes);
128 if (previous != null) {
129 log.warn("There was already an entry with DN " + currentDn
130 + ", which has been discarded by a subsequent one.");
131 }
132 }
133
134 if (attributeId.equals(LdapAttrs.DN))
135 try {
136 currentDn = new LdapName(attributeValue.toString());
137 currentAttributes = new BasicAttributes(true);
138 } catch (InvalidNameException e) {
139 log.error(attributeValue + " not a valid DN, skipping the entry.");
140 currentDn = null;
141 currentAttributes = null;
142 }
143 }
144
145 // store attribute
146 if (currentAttributes != null) {
147 Attribute attribute = currentAttributes.get(attributeId);
148 if (attribute == null) {
149 attribute = new BasicAttribute(attributeId);
150 currentAttributes.put(attribute);
151 }
152 attribute.add(attributeValue);
153 }
154 currentEntry = new StringBuilder();
155 }
156 currentEntry.append(line);
157 }
158 } finally {
159 try {
160 reader.close();
161 } catch (IOException e) {
162 if (log.isTraceEnabled())
163 log.error("Cannot close stream", e);
164 }
165 }
166 return res;
167 }
168 }