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 }