/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Portions Copyright 2014-2015 ForgeRock AS */ package org.opends.server.authorization.dseecompat; import static org.opends.messages.AccessControlMessages.*; import static org.opends.server.util.CollectionUtils.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeMap; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.DecodeException; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.opends.server.core.DirectoryServer; import org.opends.server.types.*; /** * This class is used to match RDN patterns containing wildcards in either * the attribute types or the attribute values. * Substring matching on the attribute types is not supported. */ public class PatternRDN { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** Indicate whether the RDN contains a wildcard in any of its attribute types. */ private boolean hasTypeWildcard; /** The set of attribute type patterns. */ private String[] typePatterns; /** * The set of attribute value patterns. * The value pattern is split into a list according to the positions of any * wildcards. For example, the value "A*B*C" is represented as a * list of three elements A, B and C. The value "A" is represented as * a list of one element A. The value "*A*" is represented as a list * of three elements "", A and "". */ private ArrayList<ArrayList<ByteString>> valuePatterns; /** The number of attribute-value pairs in this RDN pattern. */ private int numValues; /** * Create a new RDN pattern composed of a single attribute-value pair. * @param type The attribute type pattern. * @param valuePattern The attribute value pattern. * @param dnString The DN pattern containing the attribute-value pair. * @throws DirectoryException If the attribute-value pair is not valid. */ public PatternRDN(String type, ArrayList<ByteString> valuePattern, String dnString) throws DirectoryException { // Only Whole-Type wildcards permitted. if (type.contains("*")) { if (!type.equals("*")) { LocalizableMessage message = WARN_PATTERN_DN_TYPE_CONTAINS_SUBSTRINGS.get(dnString); throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message); } hasTypeWildcard = true; } numValues = 1; typePatterns = new String[] { type }; valuePatterns = newArrayList(valuePattern); } /** * Add another attribute-value pair to the pattern. * @param type The attribute type pattern. * @param valuePattern The attribute value pattern. * @param dnString The DN pattern containing the attribute-value pair. * @throws DirectoryException If the attribute-value pair is not valid. * @return <CODE>true</CODE> if the type-value pair was added to * this RDN, or <CODE>false</CODE> if it was not (e.g., it * was already present). */ public boolean addValue(String type, ArrayList<ByteString> valuePattern, String dnString) throws DirectoryException { // No type wildcards permitted in multi-valued patterns. if (hasTypeWildcard || type.contains("*")) { LocalizableMessage message = WARN_PATTERN_DN_TYPE_WILDCARD_IN_MULTIVALUED_RDN.get(dnString); throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message); } numValues++; String[] newTypes = new String[numValues]; System.arraycopy(typePatterns, 0, newTypes, 0, typePatterns.length); newTypes[typePatterns.length] = type; typePatterns = newTypes; valuePatterns.add(valuePattern); return true; } /** * Retrieves the number of attribute-value pairs contained in this * RDN pattern. * * @return The number of attribute-value pairs contained in this * RDN pattern. */ public int getNumValues() { return numValues; } /** * Determine whether a given RDN matches the pattern. * @param rdn The RDN to be matched. * @return true if the RDN matches the pattern. */ public boolean matchesRDN(RDN rdn) { if (getNumValues() == 1) { // Check for ",*," matching any RDN. if (typePatterns[0].equals("*") && valuePatterns.get(0) == null) { return true; } if (rdn.getNumValues() != 1) { return false; } AttributeType thatType = rdn.getAttributeType(0); if (!typePatterns[0].equals("*")) { AttributeType thisType = DirectoryServer.getAttributeTypeOrNull(typePatterns[0].toLowerCase()); if (thisType == null || !thisType.equals(thatType)) { return false; } } return matchValuePattern(valuePatterns.get(0), thatType, rdn.getAttributeValue(0)); } if (hasTypeWildcard) { return false; } if (numValues != rdn.getNumValues()) { return false; } // Sort the attribute-value pairs by attribute type. TreeMap<String,ArrayList<ByteString>> patternMap = new TreeMap<>(); TreeMap<String, ByteString> rdnMap = new TreeMap<>(); for (int i = 0; i < rdn.getNumValues(); i++) { rdnMap.put(rdn.getAttributeType(i).getNameOrOID(), rdn.getAttributeValue(i)); } for (int i = 0; i < numValues; i++) { String lowerName = typePatterns[i].toLowerCase(); AttributeType type = DirectoryServer.getAttributeTypeOrNull(lowerName); if (type == null) { return false; } patternMap.put(type.getNameOrOID(), valuePatterns.get(i)); } Set<String> patternKeys = patternMap.keySet(); Set<String> rdnKeys = rdnMap.keySet(); Iterator<String> patternKeyIter = patternKeys.iterator(); for (String rdnKey : rdnKeys) { if (!rdnKey.equals(patternKeyIter.next())) { return false; } AttributeType rdnAttrType = DirectoryServer.getAttributeTypeOrNull(rdnKey); if (!matchValuePattern(patternMap.get(rdnKey), rdnAttrType, rdnMap.get(rdnKey))) { return false; } } return true; } /** * Determine whether a value pattern matches a given attribute-value pair. * @param pattern The value pattern where each element of the list is a * substring of the pattern appearing between wildcards. * @param type The attribute type of the attribute-value pair. * @param value The value of the attribute-value pair. * @return true if the value pattern matches the attribute-value pair. */ private boolean matchValuePattern(List<ByteString> pattern, AttributeType type, ByteString value) { if (pattern == null) { return true; } try { if (pattern.size() == 1) { // Handle this just like an equality filter. MatchingRule rule = type.getEqualityMatchingRule(); ByteString thatNormValue = rule.normalizeAttributeValue(value); return rule.getAssertion(pattern.get(0)).matches(thatNormValue).toBoolean(); } // Handle this just like a substring filter. ByteString subInitial = pattern.get(0); if (subInitial.length() == 0) { subInitial = null; } ByteString subFinal = pattern.get(pattern.size() - 1); if (subFinal.length() == 0) { subFinal = null; } List<ByteString> subAnyElements; if (pattern.size() > 2) { subAnyElements = pattern.subList(1, pattern.size()-1); } else { subAnyElements = null; } Attribute attr = Attributes.create(type, value); return attr.matchesSubstring(subInitial, subAnyElements, subFinal).toBoolean(); } catch (DecodeException e) { logger.traceException(e); return false; } } }