/*
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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.
*/
package org.opends.server.authorization.dseecompat;
import org.opends.messages.Message;
import org.opends.server.types.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.api.EqualityMatchingRule;
import static org.opends.messages.AccessControlMessages.
WARN_PATTERN_DN_TYPE_CONTAINS_SUBSTRINGS;
import static org.opends.messages.AccessControlMessages.
WARN_PATTERN_DN_TYPE_WILDCARD_IN_MULTIVALUED_RDN;
import java.util.List;
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.Set;
import java.util.Iterator;
/**
* 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
{
/**
* Indicate whether the RDN contains a wildcard in any of its attribute
* types.
*/
private boolean hasTypeWildcard = false;
/**
* 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("*"))
{
Message 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 = new ArrayList<ArrayList<ByteString>>(1);
valuePatterns.add(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("*"))
{
Message 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.getAttributeType(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<String, ArrayList<ByteString>>();
TreeMap<String,AttributeValue> rdnMap =
new TreeMap<String, AttributeValue>();
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.getAttributeType(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;
}
if (!matchValuePattern(patternMap.get(rdnKey),
DirectoryServer.getAttributeType(rdnKey),
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,
AttributeValue value)
{
if (pattern == null)
{
return true;
}
try
{
if (pattern.size() > 1)
{
// 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);
switch (attr.matchesSubstring(subInitial, subAnyElements, subFinal))
{
case TRUE:
return true;
case FALSE:
case UNDEFINED:
default:
return false;
}
}
else
{
ByteString thisNormValue =
type.getEqualityMatchingRule().normalizeValue(pattern.get(0));
ByteString thatNormValue = value.getNormalizedValue();
EqualityMatchingRule mr = type.getEqualityMatchingRule();
return mr.areEqual(thisNormValue, thatNormValue);
}
}
catch (DirectoryException e)
{
return false;
}
}
}