001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.authorization.dseecompat;
029 import org.opends.messages.Message;
030
031 import org.opends.server.types.*;
032 import org.opends.server.core.DirectoryServer;
033 import org.opends.server.api.EqualityMatchingRule;
034 import static org.opends.messages.AccessControlMessages.
035 WARN_PATTERN_DN_TYPE_CONTAINS_SUBSTRINGS;
036 import static org.opends.messages.AccessControlMessages.
037 WARN_PATTERN_DN_TYPE_WILDCARD_IN_MULTIVALUED_RDN;
038 import java.util.List;
039 import java.util.LinkedHashSet;
040 import java.util.ArrayList;
041 import java.util.TreeMap;
042 import java.util.Set;
043 import java.util.Iterator;
044
045 /**
046 * This class is used to match RDN patterns containing wildcards in either
047 * the attribute types or the attribute values.
048 * Substring matching on the attribute types is not supported.
049 */
050 public class PatternRDN
051 {
052 /**
053 * Indicate whether the RDN contains a wildcard in any of its attribute
054 * types.
055 */
056 private boolean hasTypeWildcard = false;
057
058
059 /**
060 * The set of attribute type patterns.
061 */
062 private String[] typePatterns;
063
064
065 /**
066 * The set of attribute value patterns.
067 * The value pattern is split into a list according to the positions of any
068 * wildcards. For example, the value "A*B*C" is represented as a
069 * list of three elements A, B and C. The value "A" is represented as
070 * a list of one element A. The value "*A*" is represented as a list
071 * of three elements "", A and "".
072 */
073 private ArrayList<ArrayList<ByteString>> valuePatterns;
074
075
076 /**
077 * The number of attribute-value pairs in this RDN pattern.
078 */
079 private int numValues;
080
081
082 /**
083 * Create a new RDN pattern composed of a single attribute-value pair.
084 * @param type The attribute type pattern.
085 * @param valuePattern The attribute value pattern.
086 * @param dnString The DN pattern containing the attribute-value pair.
087 * @throws DirectoryException If the attribute-value pair is not valid.
088 */
089 public PatternRDN(String type, ArrayList<ByteString> valuePattern,
090 String dnString)
091 throws DirectoryException
092 {
093 // Only Whole-Type wildcards permitted.
094 if (type.contains("*"))
095 {
096 if (!type.equals("*"))
097 {
098 Message message =
099 WARN_PATTERN_DN_TYPE_CONTAINS_SUBSTRINGS.get(dnString);
100 throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
101 message);
102 }
103 hasTypeWildcard = true;
104 }
105
106 numValues = 1;
107 typePatterns = new String[] { type };
108 valuePatterns = new ArrayList<ArrayList<ByteString>>(1);
109 valuePatterns.add(valuePattern);
110 }
111
112
113 /**
114 * Add another attribute-value pair to the pattern.
115 * @param type The attribute type pattern.
116 * @param valuePattern The attribute value pattern.
117 * @param dnString The DN pattern containing the attribute-value pair.
118 * @throws DirectoryException If the attribute-value pair is not valid.
119 * @return <CODE>true</CODE> if the type-value pair was added to
120 * this RDN, or <CODE>false</CODE> if it was not (e.g., it
121 * was already present).
122 */
123 public boolean addValue(String type, ArrayList<ByteString> valuePattern,
124 String dnString)
125 throws DirectoryException
126 {
127 // No type wildcards permitted in multi-valued patterns.
128 if (hasTypeWildcard || type.contains("*"))
129 {
130 Message message =
131 WARN_PATTERN_DN_TYPE_WILDCARD_IN_MULTIVALUED_RDN.get(dnString);
132 throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
133 message);
134 }
135
136 numValues++;
137
138 String[] newTypes = new String[numValues];
139 System.arraycopy(typePatterns, 0, newTypes, 0,
140 typePatterns.length);
141 newTypes[typePatterns.length] = type;
142 typePatterns = newTypes;
143
144 valuePatterns.add(valuePattern);
145
146 return true;
147 }
148
149
150 /**
151 * Retrieves the number of attribute-value pairs contained in this
152 * RDN pattern.
153 *
154 * @return The number of attribute-value pairs contained in this
155 * RDN pattern.
156 */
157 public int getNumValues()
158 {
159 return numValues;
160 }
161
162
163 /**
164 * Determine whether a given RDN matches the pattern.
165 * @param rdn The RDN to be matched.
166 * @return true if the RDN matches the pattern.
167 */
168 public boolean matchesRDN(RDN rdn)
169 {
170 if (getNumValues() == 1)
171 {
172 // Check for ",*," matching any RDN.
173 if (typePatterns[0].equals("*") && valuePatterns.get(0) == null)
174 {
175 return true;
176 }
177
178 if (rdn.getNumValues() != 1)
179 {
180 return false;
181 }
182
183 AttributeType thatType = rdn.getAttributeType(0);
184 if (!typePatterns[0].equals("*"))
185 {
186 AttributeType thisType =
187 DirectoryServer.getAttributeType(typePatterns[0].toLowerCase());
188 if (thisType == null || !thisType.equals(thatType))
189 {
190 return false;
191 }
192 }
193
194 return matchValuePattern(valuePatterns.get(0), thatType,
195 rdn.getAttributeValue(0));
196 }
197
198 if (hasTypeWildcard)
199 {
200 return false;
201 }
202
203 if (numValues != rdn.getNumValues())
204 {
205 return false;
206 }
207
208 // Sort the attribute-value pairs by attribute type.
209 TreeMap<String,ArrayList<ByteString>> patternMap =
210 new TreeMap<String, ArrayList<ByteString>>();
211 TreeMap<String,AttributeValue> rdnMap =
212 new TreeMap<String, AttributeValue>();
213
214 for (int i = 0; i < rdn.getNumValues(); i++)
215 {
216 rdnMap.put(rdn.getAttributeType(i).getNameOrOID(),
217 rdn.getAttributeValue(i));
218 }
219
220 for (int i = 0; i < numValues; i++)
221 {
222 String lowerName = typePatterns[i].toLowerCase();
223 AttributeType type = DirectoryServer.getAttributeType(lowerName);
224 if (type == null)
225 {
226 return false;
227 }
228 patternMap.put(type.getNameOrOID(), valuePatterns.get(i));
229 }
230
231 Set<String> patternKeys = patternMap.keySet();
232 Set<String> rdnKeys = rdnMap.keySet();
233 Iterator<String> patternKeyIter = patternKeys.iterator();
234 for (String rdnKey : rdnKeys)
235 {
236 if (!rdnKey.equals(patternKeyIter.next()))
237 {
238 return false;
239 }
240
241 if (!matchValuePattern(patternMap.get(rdnKey),
242 DirectoryServer.getAttributeType(rdnKey),
243 rdnMap.get(rdnKey)))
244 {
245 return false;
246 }
247 }
248
249 return true;
250 }
251
252
253 /**
254 * Determine whether a value pattern matches a given attribute-value pair.
255 * @param pattern The value pattern where each element of the list is a
256 * substring of the pattern appearing between wildcards.
257 * @param type The attribute type of the attribute-value pair.
258 * @param value The value of the attribute-value pair.
259 * @return true if the value pattern matches the attribute-value pair.
260 */
261 private boolean matchValuePattern(List<ByteString> pattern,
262 AttributeType type,
263 AttributeValue value)
264 {
265 if (pattern == null)
266 {
267 return true;
268 }
269
270 try
271 {
272 if (pattern.size() > 1)
273 {
274 // Handle this just like a substring filter.
275
276 ByteString subInitial = pattern.get(0);
277 if (subInitial.value().length == 0)
278 {
279 subInitial = null;
280 }
281
282 ByteString subFinal = pattern.get(pattern.size() - 1);
283 if (subFinal.value().length == 0)
284 {
285 subFinal = null;
286 }
287
288 List<ByteString> subAnyElements;
289 if (pattern.size() > 2)
290 {
291 subAnyElements = pattern.subList(1, pattern.size()-1);
292 }
293 else
294 {
295 subAnyElements = null;
296 }
297
298 LinkedHashSet<AttributeValue> values =
299 new LinkedHashSet<AttributeValue>(1);
300 values.add(value);
301 Attribute attr = new Attribute(type, type.getNameOrOID(), values);
302
303 switch (attr.matchesSubstring(subInitial, subAnyElements, subFinal))
304 {
305 case TRUE:
306 return true;
307
308 case FALSE:
309 case UNDEFINED:
310 default:
311 return false;
312 }
313 }
314 else
315 {
316 ByteString thisNormValue = type.normalize(pattern.get(0));
317 ByteString thatNormValue = value.getNormalizedValue();
318 EqualityMatchingRule mr = type.getEqualityMatchingRule();
319 return mr.areEqual(thisNormValue, thatNormValue);
320 }
321 }
322 catch (DirectoryException e)
323 {
324 return false;
325 }
326 }
327
328
329 }