mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Manuel Gaupp
13.45.2014 b57960e67b69040394c9dcb687af52305fd47ab8
CR-1602 (OPENDJ-883) Implement certificateExactMatch matching rule
7 files added
11 files modified
2159 ■■■■■ changed files
opends/resource/config/config.ldif 10 ●●●●● patch | view | raw | blame | history
opends/resource/schema/00-core.ldif 4 ●●● patch | view | raw | blame | history
opends/src/messages/messages/protocol.properties 15 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/schema.properties 15 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java 7 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/controls/MatchedValuesFilter.java 11 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/asn1/GSERException.java 77 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/asn1/GSERParser.java 476 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/CertificateExactAssertionSyntax.java 215 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/CertificateExactMatchingRule.java 418 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/CertificateExactMatchingRuleFactory.java 72 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/CertificateSyntax.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/SchemaConstants.java 49 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/SearchFilter.java 53 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/controls/MatchedValuesControlTest.java 48 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/GSERParserTestCase.java 380 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/schema/CertificateExactMatchingRuleTest.java 245 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java 59 ●●●●● patch | view | raw | blame | history
opends/resource/config/config.ldif
@@ -21,7 +21,7 @@
#
#      Copyright 2006-2010 Sun Microsystems, Inc.
#      Portions Copyright 2010-2013 ForgeRock AS.
#      Portions Copyright 2012-2013 Manuel Gaupp
#      Portions Copyright 2012-2014 Manuel Gaupp
#
#
# This file contains the primary Directory Server configuration.  It must not
@@ -1006,6 +1006,14 @@
ds-cfg-java-class: org.opends.server.schema.CaseIgnoreListSubstringMatchingRuleFactory
ds-cfg-enabled: true
dn: cn=Certificate Exact Matching Rule,cn=Matching Rules,cn=config
objectClass: top
objectClass: ds-cfg-matching-rule
objectClass: ds-cfg-equality-matching-rule
cn: Certificate Exact Matching Rule
ds-cfg-java-class: org.opends.server.schema.CertificateExactMatchingRuleFactory
ds-cfg-enabled: true
dn: cn=Collation Matching Rule,cn=Matching Rules,cn=config
objectClass: top
objectClass: ds-cfg-matching-rule
opends/resource/schema/00-core.ldif
@@ -22,7 +22,7 @@
#
#      Copyright 2006-2010 Sun Microsystems, Inc.
#      Portions Copyright 2011-2012 ForgeRock AS
#      Portions Copyright 2013 Manuel Gaupp
#      Portions Copyright 2013-2014 Manuel Gaupp
#
#
# This file contains a core set of attribute type and objectlass definitions
@@ -129,8 +129,10 @@
attributeTypes: ( 2.5.4.35 NAME 'userPassword'
  SYNTAX 1.3.6.1.4.1.26027.1.3.1 X-ORIGIN 'RFC 4519' )
attributeTypes: ( 2.5.4.36 NAME 'userCertificate'
  EQUALITY certificateExactMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 X-ORIGIN 'RFC 4523' )
attributeTypes: ( 2.5.4.37 NAME 'cACertificate'
  EQUALITY certificateExactMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 X-ORIGIN 'RFC 4523' )
attributeTypes: ( 2.5.4.38 NAME 'authorityRevocationList'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 X-ORIGIN 'RFC 4523' )
opends/src/messages/messages/protocol.properties
@@ -21,6 +21,7 @@
#
#      Copyright 2006-2009 Sun Microsystems, Inc.
#      Portions copyright 2013 ForgeRock AS
#      Portions copyright 2013-2014 Manuel Gaupp
@@ -892,3 +893,17 @@
MILD_ERR_CONNHANDLER_CONFIG_CHANGES_REQUIRE_RESTART_1516=The server received \
 configuration changes that require a restart of the %s connection handler \
 to take effect
MILD_ERR_GSER_PATTERN_NO_MATCH_1517=The GSER value does not contain a \
 String matching the pattern %s at the current position: %s
MILD_ERR_GSER_NO_VALID_SEPARATOR_1518=The GSER value does not contain a \
 separator at the current position: %s
MILD_ERR_GSER_NO_VALID_STRING_1519=The GSER value does not contain a valid \
 String value at the current position: %s
MILD_ERR_GSER_NO_VALID_INTEGER_1520=The GSER value does not contain a valid \
 integer value at the current position: %s
MILD_ERR_GSER_NO_VALID_IDENTIFIER_1521=The GSER value does not contain a \
 valid identifier at the current position: %s
MILD_ERR_GSER_SPACE_CHAR_EXPECTED_1522=The GSER value does not contain a \
 whitespace character at the current position: %s
MILD_ERR_GSER_NO_VALID_IDENTIFIEDCHOICE_1523=The GSER value does not \
 contain a valid IdentifiedChoiceValue at the current position: %s
opends/src/messages/messages/schema.properties
@@ -21,7 +21,7 @@
#
#      Copyright 2006-2010 Sun Microsystems, Inc.
#      Portions Copyright 2011 ForgeRock AS
#      Portions Copyright 2012 Manuel Gaupp
#      Portions Copyright 2012-2014 Manuel Gaupp
#
@@ -894,4 +894,15 @@
MILD_ERR_ATTR_SYNTAX_COUNTRY_NO_VALID_ISO_CODE_333=The provided value "%s" \
 is not a valid ISO 3166 country code
SEVERE_ERR_ATTR_SYNTAX_ILLEGAL_X_SCHEMA_FILE_334=The provided value "%s" is \
 not safe for X-SCHEMA-FILE
 not safe for X-SCHEMA-FILE
SEVERE_WARN_CERTIFICATE_MATCH_PARSE_ERROR_335=The value could not be parsed as \
 an X.509 certificate: "%s"
MILD_ERR_CERTIFICATE_MATCH_INVALID_DN_336=The provided value "%s" could \
 not be parsed as a valid distinguished name because an error occurred while \
 trying to parse the DN portion:  %s
MILD_ERR_CERTIFICATE_MATCH_IDENTIFIER_NOT_FOUND_337=The identifier "%s" could \
 not be found at the correct position
MILD_ERR_CERTIFICATE_MATCH_EXPECTED_END_338=The GSER value contains additional \
 characters at the end of the assertion
MILD_ERR_CERTIFICATE_MATCH_GSER_INVALID_339=An error occured while parsing the \
 the GSER String: "%s"
opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -23,6 +23,7 @@
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2013 ForgeRock AS
 *      Portions Copyright 2014 Manuel Gaupp
 */
package org.opends.server.backends.jeb;
import org.opends.messages.Message;
@@ -971,8 +972,10 @@
    try
    {
      // Make a key from the normalized assertion value.
      byte[] keyBytes =
          equalityFilter.getAssertionValue().getNormalizedValue().toByteArray();
      EqualityMatchingRule equalityRule = equalityFilter.getAttributeType().
        getEqualityMatchingRule();
      byte[] keyBytes = equalityRule.normalizeAssertionValue(equalityFilter.
        getAssertionValue().getValue()).toByteArray();
      DatabaseEntry key = new DatabaseEntry(keyBytes);
      if(debugBuffer != null)
opends/src/server/org/opends/server/controls/MatchedValuesFilter.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.controls;
import org.opends.messages.Message;
@@ -1321,14 +1322,14 @@
    {
      case EQUALITY_MATCH_TYPE:
        if ((attributeType != null) && (type != null) &&
            attributeType.equals(type) && (assertionValue != null) &&
            attributeType.equals(type) && (rawAssertionValue != null) &&
            (value != null) && (equalityMatchingRule != null))
        {
          try
          {
            return equalityMatchingRule.areEqual(
                        assertionValue.getNormalizedValue(),
                        value.getNormalizedValue());
              equalityMatchingRule.normalizeAssertionValue(rawAssertionValue),
              value.getNormalizedValue());
          }
          catch (Exception e)
          {
@@ -1509,8 +1510,8 @@
          try
          {
            return equalityMatchingRule.areEqual(
                        assertionValue.getNormalizedValue(),
                        value.getNormalizedValue());
              equalityMatchingRule.normalizeAssertionValue(rawAssertionValue),
              value.getNormalizedValue());
          }
          catch (Exception e)
          {
opends/src/server/org/opends/server/protocols/asn1/GSERException.java
New file
@@ -0,0 +1,77 @@
/*
 * 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 2013-2014 Manuel Gaupp
 */
package org.opends.server.protocols.asn1;
import org.opends.messages.Message;
import org.opends.server.types.IdentifiedException;
/**
 * This class defines an exception that may be thrown if a problem occurs while
 * interacting with a GSER String.
 */
public final class GSERException
       extends IdentifiedException
{
  /**
   * The serial version identifier required to satisfy the compiler because this
   * class extends <CODE>java.lang.Exception</CODE>, which implements the
   * <CODE>java.io.Serializable</CODE> interface.  This value was generated
   * using the <CODE>serialver</CODE> command-line utility included with the
   * Java SDK.
   */
  private static final long serialVersionUID = 3655637382448481369L;
  /**
   * Creates a new GSER exception with the provided message.
   *
   * @param  message    The message that explains the problem that occurred.
   */
  public GSERException(Message message)
  {
    super(message);
  }
  /**
   * Creates a new GSER exception with the provided message and root
   * cause.
   *
   * @param  message    The message that explains the problem that occurred.
   * @param  cause      The exception that was caught to trigger this exception.
   */
  public GSERException(Message message, Throwable cause)
  {
    super(message, cause);
  }
}
opends/src/server/org/opends/server/protocols/asn1/GSERParser.java
New file
@@ -0,0 +1,476 @@
/*
 * 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 2013-2014 Manuel Gaupp
 */
package org.opends.server.protocols.asn1;
import java.math.BigInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.opends.messages.Message;
import static org.opends.messages.ProtocolMessages.*;
import static org.opends.server.util.Validator.*;
/**
 * This class implements a parser for strings which are encoded using the
 * Generic String Encoding Rules (GSER) defined in RFC 3641.
 *
 * @see <a href="http://tools.ietf.org/html/rfc3641">RFC 3641 -
 *      Generic String Encoding Rules (GSER) for ASN.1 Types
 *      </a>
 */
public class GSERParser
{
  private String gserValue;
  private int pos;
  private int length;
  /**
   * Pattern to match an identifier defined in RFC 3641, section 3.4.
   * <pre>
   * An &lt;identifier&gt; conforms to the definition of an identifier in ASN.1
   * notation (Clause 11.3 of X.680 [8]).  It begins with a lowercase
   * letter and is followed by zero or more letters, digits, and hyphens.
   * A hyphen is not permitted to be the last character, nor is it to be
   * followed by another hyphen.  The case of letters in an identifier is
   * always significant.
   *
   *    identifier    = lowercase *alphanumeric *(hyphen 1*alphanumeric)
   *    alphanumeric  = uppercase / lowercase / decimal-digit
   *    uppercase     = %x41-5A  ; "A" to "Z"
   *    lowercase     = %x61-7A  ; "a" to "z"
   *    decimal-digit = %x30-39  ; "0" to "9"
   *    hyphen        = "-"
   * </pre>
   */
  private static Pattern GSER_IDENTIFIER = Pattern
          .compile("^([a-z]([A-Za-z0-9]|(-[A-Za-z0-9]))*)");
  /**
   * Pattern to match the identifier part (including the colon) of an
   * IdentifiedChoiceValue defined in RFC 3641, section 3.12.
   * <pre>
   *    IdentifiedChoiceValue = identifier ":" Value
   * </pre>
   */
  private static Pattern GSER_CHOICE_IDENTIFIER = Pattern
          .compile("^([a-z]([A-Za-z0-9]|(-[A-Za-z0-9]))*:)");
  /**
   * Pattern to match "sp", containing zero, one or more space characters.
   * <pre>
   *    sp = *%x20  ; zero, one or more space characters
   * </pre>
   */
  private static Pattern GSER_SP = Pattern.compile("^( *)");
  /**
   * Pattern to match "msp", containing at least one space character.
   * <pre>
   *    msp = 1*%x20  ; one or more space characters
   * </pre>
   */
  private static Pattern GSER_MSP = Pattern.compile("^( +)");
  /**
   * Pattern to match an Integer value.
   */
  private static Pattern GSER_INTEGER = Pattern.compile("^(\\d+)");
  /**
   * Pattern to match a GSER StringValue, defined in RFC 3641, section 3.2:
   * <pre>
   * Any embedded double quotes in the resulting UTF-8 character string
   * are escaped by repeating the double quote characters.
   *
   * [...]
   *
   *    StringValue       = dquote *SafeUTF8Character dquote
   *    dquote            = %x22 ; &quot; (double quote)
   * </pre>
   */
  private static Pattern GSER_STRING = Pattern
          .compile("^(\"([^\"]|(\"\"))*\")");
  /**
   * Pattern to match the beginning of a GSER encoded Sequence.
   * <pre>
   *    SequenceValue = ComponentList
   *    ComponentList = "{" [ sp NamedValue *( "," sp NamedValue) ] sp "}"
   * </pre>
   */
  private static Pattern GSER_SEQUENCE_START = Pattern.compile("^(\\{)");
  /**
   * Pattern to match the end of a GSER encoded Sequence.
   * <pre>
   *    SequenceValue = ComponentList
   *    ComponentList = "{" [ sp NamedValue *( "," sp NamedValue) ] sp "}"
   * </pre>
   */
  private static Pattern GSER_SEQUENCE_END = Pattern.compile("^(\\})");
  /**
   * Pattern to match the separator used in GSER encoded sequences.
   */
  private static Pattern GSER_SEP = Pattern.compile("^(,)");
  /**
   * Creates a new GSER Parser.
   *
   * @param value the GSER encoded String value
   */
  public GSERParser(String value)
  {
    ensureNotNull(value);
    this.gserValue = value;
    this.pos = 0;
    this.length = value.length();
  }
  /**
   * Determines if the GSER String contains at least one character to be read.
   *
   * @return <code>true</code> if there is at least one remaining character or
   *         <code>false</code> otherwise.
   */
  public boolean hasNext()
  {
    return (pos < length);
  }
  /**
   * Determines if the remaining GSER String matches the provided pattern.
   *
   * @param pattern the pattern to search for
   *
   * @return <code>true</code> if the remaining string matches the pattern or
   *         <code>false</code> otherwise.
   */
  private boolean hasNext(Pattern pattern)
  {
    if (!hasNext())
    {
      return false;
    }
    Matcher matcher = pattern.matcher(gserValue.substring(pos,length));
    return matcher.find();
  }
  /**
   * Returns the String matched by the first capturing group of the pattern.
   * The parser advances past the input matched by the first capturing group.
   *
   * @param pattern the pattern to search for
   *
   * @return the String matched by the first capturing group of the pattern
   *
   * @throws GSERException
   *           If no match could be found
   */
  private String next(Pattern pattern) throws GSERException
  {
    Matcher matcher = pattern.matcher(gserValue.substring(pos,length));
    if (matcher.find() &&  matcher.groupCount() >= 1)
    {
      pos += matcher.end(1);
      return matcher.group(1);
    }
    else
    {
      Message msg = ERR_GSER_PATTERN_NO_MATCH.get(pattern.pattern(),
                      gserValue.substring(pos,length));
      throw new GSERException(msg);
    }
  }
  /**
   * Skips the input matched by the first capturing group.
   *
   * @param pattern the pattern to search for
   *
   * @throws GSERException
   *           If no match could be found
   */
  private void skip(Pattern pattern) throws GSERException
  {
    Matcher matcher = pattern.matcher(gserValue.substring(pos,length));
    if (matcher.find() && matcher.groupCount() >= 1)
    {
      pos += matcher.end(1);
    }
    else
    {
      Message msg = ERR_GSER_PATTERN_NO_MATCH.get(pattern.pattern(),
                      gserValue.substring(pos,length));
      throw new GSERException(msg);
    }
  }
  /**
   * Skips the input matching zero, one or more space characters.
   *
   * @return reference to this GSERParser
   *
   * @throws GSERException
   *           If no match could be found
   */
  public GSERParser skipSP() throws GSERException
  {
    skip(GSER_SP);
    return this;
  }
  /**
   * Skips the input matching one or more space characters.
   *
   * @return reference to this GSERParser
   *
   * @throws GSERException
   *           If no match could be found
   */
  public GSERParser skipMSP() throws GSERException
  {
    skip(GSER_MSP);
    return this;
  }
  /**
   * Skips the input matching the start of a sequence and subsequent space
   * characters.
   *
   * @return reference to this GSERParser
   *
   * @throws GSERException
   *           If the input does not match the start of a sequence
   */
  public GSERParser readStartSequence() throws GSERException
  {
    next(GSER_SEQUENCE_START);
    skip(GSER_SP);
    return this;
  }
  /**
   * Skips the input matching the end of a sequence and preceding space
   * characters.
   *
   * @return reference to this GSERParser
   *
   * @throws GSERException
   *           If the input does not match the end of a sequence
   */
  public GSERParser readEndSequence() throws GSERException
  {
    skip(GSER_SP);
    next(GSER_SEQUENCE_END);
    return this;
  }
  /**
   * Skips the input matching the separator pattern (",") and subsequenct space
   * characters.
   *
   * @return reference to this GSERParser
   *
   * @throws GSERException
   *           If the input does not match the separator pattern.
   */
  public GSERParser skipSeparator() throws GSERException
  {
    if (!hasNext(GSER_SEP))
    {
      Message msg = ERR_GSER_NO_VALID_SEPARATOR.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    skip(GSER_SEP);
    skip(GSER_SP);
    return this;
  }
  /**
   * Returns the next element as a String.
   *
   * @return the input matching the String pattern
   *
   * @throws GSERException
   *           If the input does not match the string pattern.
   */
  public String nextString() throws GSERException
  {
    if (!hasNext(GSER_STRING))
    {
      Message msg = ERR_GSER_NO_VALID_STRING.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    String str = next(GSER_STRING);
    // Strip leading and trailing dquotes; unescape double dquotes
    return str.substring(1, str.length() - 1).replace("\"\"","\"");
  }
  /**
   * Returns the next element as an Integer.
   *
   * @return the input matching the integer pattern
   *
   * @throws GSERException
   *           If the input does not match the integer pattern
   */
  public int nextInteger() throws GSERException
  {
    if (!hasNext(GSER_INTEGER))
    {
      Message msg = ERR_GSER_NO_VALID_INTEGER.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    return Integer.valueOf(next(GSER_INTEGER)).intValue();
  }
  /**
   * Returns the next element as a BigInteger.
   *
   * @return the input matching the integer pattern
   *
   * @throws GSERException
   *           If the input does not match the integer pattern
   */
  public BigInteger nextBigInteger() throws GSERException
  {
    if (!hasNext(GSER_INTEGER))
    {
      Message msg = ERR_GSER_NO_VALID_INTEGER.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    return new BigInteger(next(GSER_INTEGER));
  }
  /**
   * Returns the identifier of the next NamedValue element.
   *
   * @return the identifier of the NamedValue element
   *
   * @throws GSERException
   *           If the input does not match the identifier pattern of a
   *           NamedValue
   */
  public String nextNamedValueIdentifier() throws GSERException
  {
    if (!hasNext(GSER_IDENTIFIER))
    {
      Message msg = ERR_GSER_NO_VALID_IDENTIFIER.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    String identifier = next(GSER_IDENTIFIER);
    if (!hasNext(GSER_MSP))
    {
      Message msg = ERR_GSER_SPACE_CHAR_EXPECTED.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    skipMSP();
    return identifier;
  }
  /**
   * Return the identifier of the next IdentifiedChoiceValue element.
   *
   * @return the identifier of the IdentifiedChoiceValue element
   *
   * @throws GSERException
   *           If the input does not match the identifier pattern of an
   *           IdentifiedChoiceValue
   */
  public String nextChoiceValueIdentifier() throws GSERException
  {
    if (!hasNext(GSER_CHOICE_IDENTIFIER))
    {
      Message msg = ERR_GSER_NO_VALID_IDENTIFIEDCHOICE.get(gserValue
                      .substring(pos,length));
      throw new GSERException(msg);
    }
    String identifier = next(GSER_CHOICE_IDENTIFIER);
    // Remove the colon at the end of the identifier
    return identifier.substring(0, identifier.length() - 1);
  }
}
opends/src/server/org/opends/server/schema/CertificateExactAssertionSyntax.java
New file
@@ -0,0 +1,215 @@
/*
 * 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 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2012 ForgeRock AS
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.schema;
import org.opends.server.admin.std.server.AttributeSyntaxCfg;
import org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
import org.opends.server.api.EqualityMatchingRule;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ByteSequence;
import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.messages.SchemaMessages.*;
import org.opends.messages.MessageBuilder;
import static org.opends.server.schema.SchemaConstants.*;
/**
 * This class defines the Certificate Exact Assertion attribute syntax,
 * which contains components for matching X.509 certificates.
 */
public class CertificateExactAssertionSyntax
       extends AttributeSyntax<AttributeSyntaxCfg>
{
  // The default equality matching rule for this syntax.
  private EqualityMatchingRule defaultEqualityMatchingRule;
  // The default ordering matching rule for this syntax.
  private OrderingMatchingRule defaultOrderingMatchingRule;
  // The default substring matching rule for this syntax.
  private SubstringMatchingRule defaultSubstringMatchingRule;
  /**
   * Creates a new instance of this syntax.  Note that the only thing that
   * should be done here is to invoke the default constructor for the
   * superclass.  All initialization should be performed in the
   * <CODE>initializeSyntax</CODE> method.
   */
  public CertificateExactAssertionSyntax()
  {
    super();
  }
  /**
   * {@inheritDoc}
   */
  public void initializeSyntax(AttributeSyntaxCfg configuration)
         throws ConfigException
  {
    defaultEqualityMatchingRule =
         DirectoryServer.getEqualityMatchingRule(EMR_CASE_IGNORE_OID);
    if (defaultEqualityMatchingRule == null)
    {
      logError(ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get(
          EMR_CASE_IGNORE_OID, SYNTAX_CERTIFICATE_EXACT_ASSERTION_NAME));
    }
    defaultOrderingMatchingRule =
         DirectoryServer.getOrderingMatchingRule(OMR_CASE_IGNORE_OID);
    if (defaultOrderingMatchingRule == null)
    {
      logError(ERR_ATTR_SYNTAX_UNKNOWN_ORDERING_MATCHING_RULE.get(
          OMR_CASE_IGNORE_OID, SYNTAX_CERTIFICATE_EXACT_ASSERTION_NAME));
    }
    defaultSubstringMatchingRule =
         DirectoryServer.getSubstringMatchingRule(SMR_CASE_IGNORE_OID);
    if (defaultSubstringMatchingRule == null)
    {
      logError(ERR_ATTR_SYNTAX_UNKNOWN_SUBSTRING_MATCHING_RULE.get(
          SMR_CASE_IGNORE_OID, SYNTAX_CERTIFICATE_EXACT_ASSERTION_NAME));
    }
  }
  /**
   * {@inheritDoc}
   */
  public String getSyntaxName()
  {
    return SYNTAX_CERTIFICATE_EXACT_ASSERTION_NAME;
  }
  /**
   * {@inheritDoc}
   */
  public String getOID()
  {
    return SYNTAX_CERTIFICATE_EXACT_ASSERTION_OID;
  }
  /**
   * {@inheritDoc}
   */
  public String getDescription()
  {
    return SYNTAX_CERTIFICATE_EXACT_ASSERTION_DESCRIPTION;
  }
  /**
   * {@inheritDoc}
   */
  public EqualityMatchingRule getEqualityMatchingRule()
  {
    return defaultEqualityMatchingRule;
  }
  /**
   * {@inheritDoc}
   */
  public OrderingMatchingRule getOrderingMatchingRule()
  {
    return defaultOrderingMatchingRule;
  }
  /**
   * {@inheritDoc}
   */
  public SubstringMatchingRule getSubstringMatchingRule()
  {
    return defaultSubstringMatchingRule;
  }
  /**
   * {@inheritDoc}
   */
  public ApproximateMatchingRule getApproximateMatchingRule()
  {
    // Approximate matching will not be allowed by default.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public boolean valueIsAcceptable(ByteSequence value,
                                   MessageBuilder invalidReason)
  {
    // This method will never be called because this syntax is only used
    // within assertions.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isBinary()
  {
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isHumanReadable()
  {
    return true;
  }
}
opends/src/server/org/opends/server/schema/CertificateExactMatchingRule.java
New file
@@ -0,0 +1,418 @@
/*
 * 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 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.schema;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.messages.SchemaMessages.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.Collection;
import java.util.Collections;
import org.opends.messages.Message;
import org.opends.server.api.EqualityMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.ErrorLogger;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.asn1.GSERException;
import org.opends.server.protocols.asn1.GSERParser;
import org.opends.server.types.ByteSequence;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringBuilder;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.ResultCode;
import org.opends.server.util.StaticUtils;
/**
 * This class implements the certificateExactMatch matching rule defined
 * in X.509 and referenced in RFC 4523.
 */
class CertificateExactMatchingRule
       extends EqualityMatchingRule
{
  /**
   * The GSER identifier for the serialNumber named value.
   */
  private static final String GSER_ID_SERIALNUMBER = "serialNumber";
  /**
   * The GSER identifier for the issuer named value.
   */
  private static final String GSER_ID_ISSUER = "issuer";
  /**
   * The GSER identifier for the rdnSequence IdentifiedChoiceValue.
   */
  private static final String GSER_ID_RDNSEQUENCE = "rdnSequence";
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * Creates a new instance of this certificateExactMatch matching rule.
   */
  public CertificateExactMatchingRule()
  {
    super();
  }
  /**
   * {@inheritDoc}
   */
  public Collection<String> getAllNames()
  {
    return Collections.singleton(getName());
  }
  /**
   * Retrieves the common name for this matching rule.
   *
   * @return  The common name for this matching rule, or <CODE>null</CODE> if
   * it does not have a name.
   */
  public String getName()
  {
    return EMR_CERTIFICATE_EXACT_NAME;
  }
  /**
   * Retrieves the OID for this matching rule.
   *
   * @return  The OID for this matching rule.
   */
  public String getOID()
  {
    return EMR_CERTIFICATE_EXACT_OID;
  }
  /**
   * Retrieves the description for this matching rule.
   *
   * @return  The description for this matching rule, or <CODE>null</CODE> if
   *          there is none.
   */
  public String getDescription()
  {
    return EMR_CERTIFICATE_EXACT_DESCRIPTION;
  }
  /**
   * Retrieves the OID of the syntax with which this matching rule is
   * associated.
   *
   * @return  The OID of the syntax with which this matching rule is associated.
   */
  public String getSyntaxOID()
  {
    return SYNTAX_CERTIFICATE_EXACT_ASSERTION_OID;
  }
  /**
   * Retrieves the normalized form of the provided value, which is best suited
   * for efficiently performing matching operations on that value.
   *
   * @param  value  The value to be normalized.
   *
   * @return  The normalized version of the provided value.
   *
   * @throws  DirectoryException  If the provided value is invalid according to
   *                              the associated attribute syntax.
   */
  public ByteString normalizeValue(ByteSequence value)
         throws DirectoryException
  {
    // The normalized form of this value is the GSER encoded ....
    final BigInteger serialNumber;
    final String dnstring;
    String certificateIssuer;
    // Read the X.509 Certificate and extract serialNumber and issuerDN
    try
    {
      CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
      InputStream inputStream = new ByteArrayInputStream(value.toByteArray());
      X509Certificate certValue = (X509Certificate) certFactory
              .generateCertificate(inputStream);
      serialNumber = certValue.getSerialNumber();
      X500Principal issuer = certValue.getIssuerX500Principal();
      dnstring = issuer.getName(X500Principal.RFC2253);
    }
    catch (CertificateException ce)
    {
      // There seems to be a problem while parsing the certificate.
      Message message = WARN_CERTIFICATE_MATCH_PARSE_ERROR.get(ce.getMessage());
      if (debugEnabled())
      {
         TRACER.debugWarning(message.toString());
      }
      // return the raw bytes as a fall back
      return value.toByteString();
    }
    // Normalize the DN
    try
    {
      DN dn = DN.decode(dnstring);
      certificateIssuer = dn.toNormalizedString();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      // We couldn't normalize the DN for some reason.  If we're supposed to use
      // strict syntax enforcement, then throw an exception.  Otherwise, log a
      // message and just try our best.
      Message message = ERR_CERTIFICATE_MATCH_INVALID_DN.get(
              dnstring, getExceptionMessage(e));
      switch (DirectoryServer.getSyntaxEnforcementPolicy())
      {
        case REJECT:
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
        case WARN:
          ErrorLogger.logError(message);
          certificateIssuer= toLowerCase(dnstring);
          break;
        default:
          certificateIssuer= toLowerCase(dnstring);
          break;
      }
    }
    // Create the encoded value
    return createEncodedValue(serialNumber,certificateIssuer);
  }
  /**
   * {@inheritDoc}
   */
  public ByteString normalizeAssertionValue(ByteSequence value)
         throws DirectoryException
  {
    // validate and normalize the GSER structure
    // according to the definitions from RFC 4523, Appendix A.1
    final BigInteger serialNumber;
    final String dnstring;
    String certificateIssuer;
    final GSERParser parser;
    String identifier;
    parser = new GSERParser(value.toString());
    try
    {
      // the String starts with a sequence
      parser.readStartSequence();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      // Assume the assertion value is a certificate and parse issuer and serial
      // number. If the value is not even a certificate then the raw bytes will
      // be returned.
      return normalizeValue(value);
    }
    try
    {
      // the first namedValue is serialNumber
      identifier = parser.nextNamedValueIdentifier();
      if (!identifier.equals(GSER_ID_SERIALNUMBER))
      {
        Message message = ERR_CERTIFICATE_MATCH_IDENTIFIER_NOT_FOUND
                            .get(GSER_ID_SERIALNUMBER);
        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
      }
      // The value for the serialNumber
      serialNumber = parser.nextBigInteger();
      // separator
      parser.skipSeparator();
      // the next namedValue is issuer
      identifier = parser.nextNamedValueIdentifier();
      if (!identifier.equals(GSER_ID_ISSUER))
      {
        Message message = ERR_CERTIFICATE_MATCH_IDENTIFIER_NOT_FOUND
                            .get(GSER_ID_ISSUER);
        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
      }
      // expecting "rdnSequence:"
      identifier = parser.nextChoiceValueIdentifier();
      if (!identifier.equals(GSER_ID_RDNSEQUENCE))
      {
        Message message = ERR_CERTIFICATE_MATCH_IDENTIFIER_NOT_FOUND
                            .get(GSER_ID_RDNSEQUENCE);
        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
      }
      // now the issuer dn
      dnstring = parser.nextString();
      // Closing the Sequence
      parser.readEndSequence();
      // There should not be additional characters
      if (parser.hasNext())
      {
        Message message = ERR_CERTIFICATE_MATCH_EXPECTED_END.get();
        switch (DirectoryServer.getSyntaxEnforcementPolicy())
        {
          case REJECT:
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
          case WARN:
            ErrorLogger.logError(message);
            break;
        }
      }
    }
    catch (GSERException e)
    {
      Message message = ERR_CERTIFICATE_MATCH_GSER_INVALID.get(
                          getExceptionMessage(e));
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
    }
    // Normalize the DN
    try
    {
      DN dn = DN.decode(dnstring);
      certificateIssuer = dn.toNormalizedString();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      // We couldn't normalize the DN for some reason.  If we're supposed to use
      // strict syntax enforcement, then throw an exception.  Otherwise, log a
      // message and just try our best.
      Message message = ERR_CERTIFICATE_MATCH_INVALID_DN.get(
              dnstring, getExceptionMessage(e));
      switch (DirectoryServer.getSyntaxEnforcementPolicy())
      {
        case REJECT:
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                       message);
        case WARN:
          ErrorLogger.logError(message);
          certificateIssuer= toLowerCase(dnstring);
          break;
        default:
          certificateIssuer= toLowerCase(dnstring);
          break;
      }
    }
    // Create the encoded value
    return createEncodedValue(serialNumber,certificateIssuer);
  }
  /**
   * Creates the value containing serialNumber and issuer DN.
   *
   * @param serial the serialNumber
   * @param issuerDN the issuer DN String
   *
   * @return the encoded ByteString
   */
  private static ByteString createEncodedValue(BigInteger serial,
                                               String issuerDN)
  {
    ByteStringBuilder builder = new ByteStringBuilder();
    builder.append(StaticUtils.getBytes(issuerDN));
    builder.append((byte) 0); // Separator
    builder.append(serial.toByteArray());
    return builder.toByteString();
  }
}
opends/src/server/org/opends/server/schema/CertificateExactMatchingRuleFactory.java
New file
@@ -0,0 +1,72 @@
/*
 * 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 2013-2014 Manuel Gaupp
 */
package org.opends.server.schema;
import java.util.Collection;
import java.util.Collections;
import org.opends.server.api.MatchingRuleFactory;
import org.opends.server.admin.std.server.MatchingRuleCfg;
import org.opends.server.api.MatchingRule;
import org.opends.server.config.ConfigException;
import org.opends.server.types.InitializationException;
/**
 * This class is a factory class for
 * {@link CertificateExactMatchingRule}.
 */
public final class CertificateExactMatchingRuleFactory
        extends MatchingRuleFactory<MatchingRuleCfg>
{
  //Associated Matching Rule.
  private MatchingRule matchingRule;
 /**
  * {@inheritDoc}
  */
 @Override
 public final void initializeMatchingRule(MatchingRuleCfg configuration)
         throws ConfigException, InitializationException
 {
   matchingRule = new CertificateExactMatchingRule();
 }
 /**
  * {@inheritDoc}
  */
 @Override
 public final Collection<MatchingRule> getMatchingRules()
 {
    return Collections.singleton(matchingRule);
 }
}
opends/src/server/org/opends/server/schema/CertificateSyntax.java
@@ -23,6 +23,7 @@
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2012 Forgerock AS
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.schema;
@@ -99,11 +100,11 @@
         throws ConfigException
  {
    defaultEqualityMatchingRule =
         DirectoryServer.getEqualityMatchingRule(EMR_OCTET_STRING_OID);
         DirectoryServer.getEqualityMatchingRule(EMR_CERTIFICATE_EXACT_OID);
    if (defaultEqualityMatchingRule == null)
    {
      logError(ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get(
          EMR_OCTET_STRING_OID, SYNTAX_CERTIFICATE_NAME));
          EMR_CERTIFICATE_EXACT_OID, SYNTAX_CERTIFICATE_NAME));
    }
    defaultOrderingMatchingRule =
opends/src/server/org/opends/server/schema/SchemaConstants.java
@@ -23,6 +23,7 @@
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011-2013 ForgeRock AS
 *      Portions copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.schema;
@@ -318,6 +319,29 @@
  /**
   * The description for the certificateExactMatch equality matching rule.
   */
  public static final String EMR_CERTIFICATE_EXACT_DESCRIPTION =
       "X.509 Certificate Exact Match";
  /**
   * The name for the certificateExactMatch equality matching rule.
   */
  public static final String EMR_CERTIFICATE_EXACT_NAME =
       "certificateExactMatch";
  /**
   * The OID for the certificateExactMatch equality matching rule.
   */
  public static final String EMR_CERTIFICATE_EXACT_OID = "2.5.13.34";
  /**
   * The name for the directoryStringFirstComponentMatch equality matching rule.
   */
  public static final String EMR_DIRECTORY_STRING_FIRST_COMPONENT_NAME =
@@ -966,6 +990,31 @@
  /**
   * The description for the certificate exact assertion attribute syntax.
   */
  public static final String SYNTAX_CERTIFICATE_EXACT_ASSERTION_DESCRIPTION =
       "X.509 Certificate Exact Assertion";
  /**
   * The name for the certificate exact assertion attribute syntax.
   */
  public static final String SYNTAX_CERTIFICATE_EXACT_ASSERTION_NAME =
       "CertificateExactAssertion";
  /**
   * The OID for the Certificate Exact Assertion syntax used for assertion
   * values in extensible match filters.
   */
  public static final String SYNTAX_CERTIFICATE_EXACT_ASSERTION_OID =
       "1.3.6.1.1.15.1";
  /**
   * The description for the certificate attribute syntax.
   */
  public static final String SYNTAX_CERTIFICATE_DESCRIPTION = "Certificate";
opends/src/server/org/opends/server/types/SearchFilter.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.types;
import org.opends.messages.Message;
@@ -2736,20 +2737,56 @@
      return ConditionResult.FALSE;
    }
    // Iterate through all the attributes and see if we can find a
    // match.
    // Get the equality matching rule for the given attribute type
    MatchingRule matchingRule = attributeType.getEqualityMatchingRule();
    if (matchingRule == null)
    {
      if (debugEnabled())
      {
        TRACER.debugInfo(
         "Attribute type %s does not have an equality matching " +
         "rule -- returning undefined.",
         attributeType.getNameOrOID());
      }
      return ConditionResult.UNDEFINED;
    }
    // Normalize the assertion value
    ByteString value = assertionValue.getValue();
    ByteString normalizedValue;
    try
    {
        normalizedValue = matchingRule.normalizeAssertionValue(value);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      // We can't normalize the assertion value, so the result must be
      // undefined.
      return ConditionResult.UNDEFINED;
    }
    ConditionResult result = ConditionResult.FALSE;
    // Iterate through all the attributes and see if we can find a match.
    AttributeValue dummyAttributeValue = AttributeValues.create(value,
                                           normalizedValue);
    for (Attribute a : attrs)
    {
      if (a.contains(assertionValue))
      if (a.contains(dummyAttributeValue))
      {
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning TRUE for equality component %s in " +
              "filter %s for entry %s",
                       this, completeFilter, entry.getDN());
        }
        return ConditionResult.TRUE;
            "Returning TRUE for equality component %s in " +
            "filter %s for entry %s", this, completeFilter, entry.getDN());
         }
         return ConditionResult.TRUE;
      }
    }
opends/tests/unit-tests-testng/src/server/org/opends/server/controls/MatchedValuesControlTest.java
@@ -22,9 +22,11 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.controls;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
@@ -37,6 +39,7 @@
import org.opends.server.types.*;
import org.opends.server.protocols.asn1.ASN1Writer;
import org.opends.server.protocols.asn1.ASN1;
import org.opends.server.util.Base64;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -964,4 +967,49 @@
    }
  }
  /**
   *
   */
  @DataProvider(name = "differentNormalization")
  public Object[][] differentNormalizationData() throws ParseException
  {
    final String BASE64_CERT_VALUE =
      "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
      "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl" +
      "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa" +
      "Fw0xMjEyMjExNjM0MzVaMFYxCzAJBgNVBAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRp" +
      "b25lMRwwGgYDVQQLExNQcm9kdWN0IERldmVsb3BtZW50MRQwEgYDVQQDEwtCYWJz" +
      "IEplbnNlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApysa0c9qc8FB8gIJ" +
      "8zAb1pbJ4HzC7iRlVGhRJjFORkGhyvU4P5o2wL0iz/uko6rL9/pFhIlIMbwbV8sm" +
      "mKeNUPitwiKOjoFDmtimcZ4bx5UTAYLbbHMpEdwSpMC5iF2UioM7qdiwpAfZBd6Z" +
      "69vqNxuUJ6tP+hxtr/aSgMH2i8ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB" +
      "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE" +
      "FLlZD3aKDa8jdhzoByOFMAJDs2osMB8GA1UdIwQYMBaAFLlZD3aKDa8jdhzoByOF" +
      "MAJDs2osMA0GCSqGSIb3DQEBBQUAA4GBAE5vccY8Ydd7by2bbwiDKgQqVyoKrkUg" +
      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" +
      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" +
      "1AIUXiE3Qcck";
    final String CERT_EXACT_ASSERTION =
      "{ serialNumber 13233831500277100508, issuer rdnSequence:\""+
      "CN=Babs Jensen,OU=Product Development,L=Cupertione,C=US\" }";
    return new Object[][]{
      {"userCertificate", ByteString.wrap(Base64.decode(BASE64_CERT_VALUE)),
        CERT_EXACT_ASSERTION}};
  }
  /**
   *
   */
  @Test(dataProvider = "differentNormalization")
  public void testDifferentNormalization(String type, ByteString value,
                                         String assertion)
  {
    MatchedValuesFilter mvf;
    AttributeType attrType = DirectoryServer.getAttributeType("usercertificate");
    AttributeValue attrValue = AttributeValues.create(attrType, value);
    mvf = MatchedValuesFilter.createEqualityFilter(type, ByteString.valueOf(assertion));
    assertTrue(mvf.valueMatches(attrType, attrValue));
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/GSERParserTestCase.java
New file
@@ -0,0 +1,380 @@
/*
 * 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 2013-2014 Manuel Gaupp
 */
package org.opends.server.protocols.asn1;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.opends.server.protocols.asn1.GSERParser;
import org.opends.server.protocols.asn1.GSERException;
import org.opends.server.DirectoryServerTestCase;
/**
 * This class tests the GSERParser.
 */
public class GSERParserTestCase extends DirectoryServerTestCase
{
  /**
   * Try to create a GSER Parser with <CODE>null</CODE> as parameter.
   */
  @Test(expectedExceptions = { AssertionError.class })
  public void testGSERParserInitWithNull () throws Exception
  {
    GSERParser parser = new GSERParser(null);
  }
  /**
   * Test the <CODE>hasNext</CODE> method.
   */
  @Test()
  public void testHasNext() throws Exception
  {
    GSERParser parser = new GSERParser("0");
    assertTrue(parser.hasNext());
    assertEquals(parser.nextInteger(),0);
    assertFalse(parser.hasNext());
  }
  /**
   * Test the <CODE>skipSP</CODE> method.
   */
  @Test()
  public void testSkipSP() throws Exception
  {
    String[] values = {" 42","  42","42"};
    for (String value : values)
    {
      GSERParser parser = new GSERParser(value);
      assertEquals(parser.skipSP().nextInteger(),42);
      assertFalse(parser.hasNext());
    }
  }
  /**
   * Test the <CODE>skipMSP</CODE> method.
   */
  @Test()
  public void testSkipMSP() throws Exception
  {
    String[] values = {" 42","  42","           42"};
    for (String value : values)
    {
      GSERParser parser = new GSERParser(value);
      assertEquals(parser.skipMSP().nextInteger(),42);
      assertFalse(parser.hasNext());
    }
  }
  /**
   * Verify that <CODE>skipMSP</CODE> requires at least one space.
   */
  @Test(expectedExceptions = { GSERException.class })
  public void testSkipMSPwithZeroSpaces() throws Exception
  {
    GSERParser parser = new GSERParser("42");
    parser.skipMSP();
  }
  /**
   * Create data for the <CODE>testSequence</CODE> test case.
   */
  @DataProvider(name="sequenceValues")
  public Object[][] createSequenceValues()
  {
    return new Object[][] {
      {"{123,122}", true},
      {"{ 123,1}", true },
      {"{ 123   ,   1   }", true },
      {"{0123,}", false},
      {"{0123 42 }", false},
      {"{123  , 11 ", false},
      {" {123  , 11 ", false},
      {" 123  , 11}", false}
    };
  }
  /**
   * Test sequence parsing.
   */
  @Test(dataProvider="sequenceValues")
  public void testSequence(String value, boolean expectedResult) throws Exception
  {
    GSERParser parser = new GSERParser(value);
    boolean result = true;
    try
    {
      parser.readStartSequence();
      parser.nextInteger();
      parser.skipSP().skipSeparator();
      parser.nextInteger();
      parser.readEndSequence();
      if (parser.hasNext())
      {
        result = false;
      }
    }
    catch (GSERException e)
    {
      result = false;
    }
    assertEquals(expectedResult,result);
  }
  /**
   * Create data for the <CODE>testString</CODE> test case.
   */
  @DataProvider(name="stringValues")
  public Object[][] createStringValues()
  {
    return new Object[][] {
      {"\"\"", true},
      {"\"escaped\"\"dquotes\"", true },
      {"\"valid Unicode \u00D6\u00C4\"", true },
      {"\"only one \" \"", false},
      {"invalid without dquotes", false},
      {"\"missing end", false},
      {"\"valid string\" with extra trailing characters", false}
    };
  }
  /**
   * Test the parsing of String values.
   */
  @Test(dataProvider="stringValues")
  public void testString(String value, boolean expectedResult) throws Exception
  {
    GSERParser parser = new GSERParser(value);
    boolean result = true;
    try
    {
      assertNotNull(parser.nextString());
      if (parser.hasNext())
      {
        result = false;
      }
    }
    catch (GSERException e)
    {
      result = false;
    }
    assertEquals(expectedResult,result);
  }
  /**
   * Create data for the <CODE>testInteger</CODE> test case.
   */
  @DataProvider(name="integerValues")
  public Object[][] createIntegerValues()
  {
    return new Object[][] {
      {"0123456", true},
      {"42", true},
      {"0", true },
      {"", false},
      {"0xFF", false},
      {"NULL", false},
      {"Not a Number", false}
    };
  }
  /**
   * Create data for the <CODE>testBigInteger</CODE> test case.
   */
  @DataProvider(name="bigIntegerValues")
  public Object[][] createBigIntegerValues()
  {
    return new Object[][] {
      {"0123456", true},
      {"42", true},
      {"0", true },
      {"", false},
      {"0xFF", false},
      {"NULL", false},
      {"Not a Number", false},
      {"2147483648",true}
    };
  }
  /**
   * Test the parsing of Integer values.
   */
  @Test(dataProvider="integerValues")
  public void testInteger(String value, boolean expectedResult) throws Exception
  {
    GSERParser parser = new GSERParser(value);
    boolean result = true;
    try
    {
      parser.nextInteger();
      if (parser.hasNext())
      {
        result = false;
      }
    }
    catch (GSERException e)
    {
      result = false;
    }
    assertEquals(expectedResult,result);
  }
  /**
   * Test the parsing of BigInteger values.
   */
  @Test(dataProvider="bigIntegerValues")
  public void testBigInteger(String value, boolean expectedResult) throws Exception
  {
    GSERParser parser = new GSERParser(value);
    boolean result = true;
    try
    {
      parser.nextBigInteger();
      if (parser.hasNext())
      {
        result = false;
      }
    }
    catch (GSERException e)
    {
      result = false;
    }
    assertEquals(expectedResult,result);
  }
  /**
   * Create data for the <CODE>testNamedValueIdentifier</CODE> test case.
   */
  @DataProvider(name="namedValueIdentifierValues")
  public Object[][] createNamedValueIdentifierValues()
  {
    return new Object[][] {
      {"serialNumber ", true},
      {"issuer ", true},
      {"Serialnumber ", false},
      {"0serialnumber ", false},
      {"serial Number ", false},
      {"missingSpace",false}
    };
  }
  /**
   * Test the parsing of NamedValue identifiers.
   */
  @Test(dataProvider="namedValueIdentifierValues")
  public void testNamedValueIdentifier(String value, boolean expectedResult) throws Exception
  {
    GSERParser parser = new GSERParser(value);
    boolean result = true;
    try
    {
      assertNotNull(parser.nextNamedValueIdentifier());
      if (parser.hasNext())
      {
        result = false;
      }
    }
    catch (GSERException e)
    {
      result = false;
    }
    assertEquals(expectedResult,result);
  }
  /**
   * Create data for the <CODE>testIdentifiedChoiceIdentifier</CODE> test case.
   */
  @DataProvider(name="identifiedChoicdeIdentifierValues")
  public Object[][] createIdentifiedChoicdeIdentifierValues()
  {
    return new Object[][] {
      {"serialNumber:", true},
      {"issuer1:", true},
      {"Serialnumber:", false},
      {"0serialnumber:", false},
      {"serial Number:", false},
      {"missingColon",false}
    };
  }
  /**
   * Test the parsing of IdentifiedChoice identifiers.
   */
  @Test(dataProvider="identifiedChoicdeIdentifierValues")
  public void testIdentifiedChoicdeIdentifier(String value, boolean expectedResult) throws Exception
  {
    GSERParser parser = new GSERParser(value);
    boolean result = true;
    try
    {
      assertNotNull(parser.nextChoiceValueIdentifier());
      if (parser.hasNext())
      {
        result = false;
      }
    }
    catch (GSERException e)
    {
      result = false;
    }
    assertEquals(expectedResult,result);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/schema/CertificateExactMatchingRuleTest.java
New file
@@ -0,0 +1,245 @@
/*
 * 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 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.schema;
import org.opends.server.types.ByteString;
import org.opends.server.types.DirectoryException;
import org.opends.server.util.Base64;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
 * This class tests the certificateExactMatch matching rule.
 */
public class CertificateExactMatchingRuleTest extends SchemaTestCase
{
  /**
   * Generate data for the certificateExactMatch matching rule test.
   */
  @DataProvider(name="certificateExactMatchingRules")
  public Object[][] createCertificateExactMatchingRuleTest()
    throws Exception
  {
    String validcert1 =
      "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
      "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl" +
      "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa" +
      "Fw0xMjEyMjExNjM0MzVaMFYxCzAJBgNVBAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRp" +
      "b25lMRwwGgYDVQQLExNQcm9kdWN0IERldmVsb3BtZW50MRQwEgYDVQQDEwtCYWJz" +
      "IEplbnNlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApysa0c9qc8FB8gIJ" +
      "8zAb1pbJ4HzC7iRlVGhRJjFORkGhyvU4P5o2wL0iz/uko6rL9/pFhIlIMbwbV8sm" +
      "mKeNUPitwiKOjoFDmtimcZ4bx5UTAYLbbHMpEdwSpMC5iF2UioM7qdiwpAfZBd6Z" +
      "69vqNxuUJ6tP+hxtr/aSgMH2i8ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB" +
      "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE" +
      "FLlZD3aKDa8jdhzoByOFMAJDs2osMB8GA1UdIwQYMBaAFLlZD3aKDa8jdhzoByOF" +
      "MAJDs2osMA0GCSqGSIb3DQEBBQUAA4GBAE5vccY8Ydd7by2bbwiDKgQqVyoKrkUg" +
      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" +
      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" +
      "1AIUXiE3Qcck";
    return new Object[][] {
      {ByteString.wrap(Base64.decode(validcert1)),ByteString.valueOf(
        "{ serialNumber 13233831500277100508, issuer rdnSequence:\""+
        "CN=Babs Jensen,OU=Product Development,L=Cupertione,C=US\" }"), true },
      {ByteString.wrap(Base64.decode(validcert1)),ByteString.valueOf(
        "{    serialNumber     13233831500277100508,  issuer  rdnSequence:\""+
        "CN=Babs Jensen,OU=Product Development, L=Cupertione,C=US\" }"), true },
      {ByteString.wrap(Base64.decode(validcert1)),ByteString.valueOf(
        "{ serialNumber 13233831500277100508, issuer rdnSequence:\""+
        "cn=BABS Jensen,ou=Product Development,L=Cupertione,c=#5553\" }"), true },
      {ByteString.wrap(Base64.decode(validcert1)),ByteString.valueOf(
        "{ serialNumber 13233831511277100508, issuer rdnSequence:\""+
        "CN=Babs Jensen,OU=Product Development,L=Cupertione,C=US\" }"), false },
      {ByteString.wrap(Base64.decode(
        "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV")),
       ByteString.wrap(Base64.decode(
        "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV")), true}
    };
  }
  /**
   * Generate valid assertion values for the certificateExactMatch matching
   * rule test.
   */
  @DataProvider(name="certificateExactMatchValidAssertionValues")
  public Object[][] createCertificateExactMatchingRuleValidAssertionValues()
  {
    return new Object[][] {
      {"{serialNumber 123,issuer rdnSequence:\"c=DE\"}"},
      {"{serialNumber 123,issuer rdnSequence:\"\"}"},
      {"{serialNumber 0123,issuer rdnSequence:\"cn=issuer\"}"},
      {"{  serialNumber  123,  issuer  rdnSequence:\"c=DE\"  }"},
      {"{serialNumber 123,issuer rdnSequence:\"cn=escaped\"\"dquotes\"}"},
      {"{serialNumber 123,issuer rdnSequence:\"cn=\u00D6\u00C4\"}"}
    };
  }
  /**
   * Generate invalid assertion values for the certificateExactMatch matching
   * rule test.
   */
  @DataProvider(name="certificateExactMatchInvalidAssertionValues")
  public Object[][] createCertificateExactMatchingRuleInvalidAssertionValues()
  {
    return new Object[][] {
      {"{serialnumber 123,issuer rdnSequence:\"c=DE\"}"},
      {"{serialNumber 123,issuer rdnSequence:\"invalid\"}"},
      {"{serialNumber 0123,issuer rdnSequence: \"cn=issuer\"}"},
      {"{  serialNumber  123  ,  issuer  rdnSequence:\"c=DE\"  }  trailing"}
    };
  }
  /**
   * Generate invalid atribute values for the certificateExactMatch matching
   * rule test.
   */
  @DataProvider(name="certificateExactMatchInvalidAttributeValues")
  public Object[][] createCertificateExactMatchingRuleInvalidAttributeValues()
    throws Exception
  {
    String invalidcert1 =
      "MIICpTCCAg6gAwIBBQIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
      "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl" +
      "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa" +
      "Fw0xMjEyMjExNjM0MzVaMFYxCzAJBgNVBAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRp" +
      "b25lMRwwGgYDVQQLExNQcm9kdWN0IERldmVsb3BtZW50MRQwEgYDVQQDEwtCYWJz" +
      "IEplbnNlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApysa0c9qc8FB8gIJ" +
      "8zAb1pbJ4HzC7iRlVGhRJjFORkGhyvU4P5o2wL0iz/uko6rL9/pFhIlIMbwbV8sm" +
      "mKeNUPitwiKOjoFDmtimcZ4bx5UTAYLbbHMpEdwSpMC5iF2UioM7qdiwpAfZBd6Z" +
      "69vqNxuUJ6tP+hxtr/aSgMH2i8ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB" +
      "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE" +
      "FLlZD3aKDa8jdhzoByOFMAJDs2osMB8GA1UdIwQYMBaAFLlZD3aKDa8jdhzoByOF" +
      "MAJDs2osMA0GCSqGSIb3DQEBBQUAA4GBAE5vccY8Ydd7by2bbwiDKgQqVyoKrkUg" +
      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" +
      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" +
      "1AIUXiE3Qcck";
    String brokencert1 =
      "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV";
    return new Object[][] {
        {ByteString.wrap(Base64.decode(invalidcert1))},
        {ByteString.wrap(Base64.decode(brokencert1))}
     };
  }
  /**
   * Get an instance of the matching rule.
   *
   * @return An instance of the matching rule to test.
   */
  protected CertificateExactMatchingRule getRule()
  {
    return new CertificateExactMatchingRule();
  }
  /**
   * Test the normalization and the comparison of valid values.
   */
  @Test(dataProvider= "certificateExactMatchingRules")
  public void certificateExactMatchingRules(ByteString attributeValue,
          ByteString assertionValue, Boolean result) throws Exception
  {
    CertificateExactMatchingRule rule = getRule();
    // normalize the 2 provided values and check that they are equals
    ByteString normalizedAttributeValue =
      rule.normalizeValue(attributeValue);
    ByteString normalizedAssertionValue =
      rule.normalizeAssertionValue(assertionValue);
    Boolean liveResult = rule.areEqual(normalizedAttributeValue,
                                       normalizedAssertionValue);
    assertEquals(result, liveResult);
  }
  /**
   * Test that valid assertion values are accepted.
   */
  @Test(dataProvider= "certificateExactMatchValidAssertionValues")
  public void certificateExactMatchingRuleValidAssertionValues(String value)
              throws Exception
    {
    // Get the instance of the rule to be tested.
    CertificateExactMatchingRule rule = getRule();
    // normalize the provided assertion values
    rule.normalizeAssertionValue(ByteString.valueOf(value));
  }
  /**
   * Test that invalid assertion values are rejected.
   */
  @Test(dataProvider= "certificateExactMatchInvalidAssertionValues",
        expectedExceptions={ DirectoryException.class })
  public void certificateExactMatchingRuleInvalidAssertionValues(String value)
              throws Exception
  {
    // Get the instance of the rule to be tested.
    CertificateExactMatchingRule rule = getRule();
    // normalize the provided assertion value
    rule.normalizeAssertionValue(ByteString.valueOf(value));
  }
  /**
   * Test that invalid attribute values are returned with the original ByteString.
   */
  @Test(dataProvider= "certificateExactMatchInvalidAttributeValues")
  public void certificateExactMatchingRuleInvalidAttributeValues(ByteString value)
              throws Exception
    {
    // Get the instance of the rule to be tested.
    CertificateExactMatchingRule rule = getRule();
    // normalize the provided assertion value
    ByteString normalizedValue = rule.normalizeAssertionValue(value);
    assertEquals(value,normalizedValue);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
@@ -22,13 +22,16 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 */
package org.opends.server.types;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.util.StaticUtils;
import org.opends.server.util.Base64;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.RawFilter;
import org.opends.server.core.DirectoryServer;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -39,6 +42,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.text.ParseException;
import static java.util.Arrays.asList;
import static org.opends.server.util.StaticUtils.*;
@@ -1142,5 +1146,60 @@
    assertEquals(actualEquals, expectStringEquals,
                 "Expected " + filter1 + (expectStringEquals ? " == " : " != ") + filter2);
  }
  /**
   * Dataprovider for testing different normalization for value and assertion
   */
  @DataProvider(name = "differentNormalization")
  public Object[][] differentNormalizationData() throws ParseException
  {
    final String BASE64_CERT_VALUE =
      "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
      "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl" +
      "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa" +
      "Fw0xMjEyMjExNjM0MzVaMFYxCzAJBgNVBAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRp" +
      "b25lMRwwGgYDVQQLExNQcm9kdWN0IERldmVsb3BtZW50MRQwEgYDVQQDEwtCYWJz" +
      "IEplbnNlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApysa0c9qc8FB8gIJ" +
      "8zAb1pbJ4HzC7iRlVGhRJjFORkGhyvU4P5o2wL0iz/uko6rL9/pFhIlIMbwbV8sm" +
      "mKeNUPitwiKOjoFDmtimcZ4bx5UTAYLbbHMpEdwSpMC5iF2UioM7qdiwpAfZBd6Z" +
      "69vqNxuUJ6tP+hxtr/aSgMH2i8ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB" +
      "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE" +
      "FLlZD3aKDa8jdhzoByOFMAJDs2osMB8GA1UdIwQYMBaAFLlZD3aKDa8jdhzoByOF" +
      "MAJDs2osMA0GCSqGSIb3DQEBBQUAA4GBAE5vccY8Ydd7by2bbwiDKgQqVyoKrkUg" +
      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" +
      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" +
      "1AIUXiE3Qcck";
    final String CERT_EXACT_ASSERTION =
      "{ serialNumber 13233831500277100508, issuer rdnSequence:\""+
      "CN=Babs Jensen,OU=Product Development,L=Cupertione,C=US\" }";
    final String CERTIFICATE_LDIF = TestCaseUtils.makeLdif(
          "dn: cn=John Smith,dc=example,dc=com",
          "objectclass: inetorgperson",
          "cn: John Smith",
          "sn: Smith",
          "userCertificate;binary:: "+BASE64_CERT_VALUE
          );
    StringBuilder builder = new StringBuilder();
    RawFilter.valueToFilterString(builder,ByteString.wrap(Base64.decode(BASE64_CERT_VALUE)));
    final String CERTIFICATE_ENCODED = builder.toString();
    return new Object[][]{
            {CERTIFICATE_LDIF, "userCertificate="+CERT_EXACT_ASSERTION, true},
            {CERTIFICATE_LDIF, "userCertificate="+CERTIFICATE_ENCODED, true}};
  }
  @Test(dataProvider = "differentNormalization")
  public void testdifferentNormalization(String ldifEntry, String filterStr,
                                         boolean expectMatch) throws Exception
  {
    Entry entry = TestCaseUtils.entryFromLdifString(ldifEntry);
    boolean matches = SearchFilter.createFilterFromString(filterStr).matchesEntry(entry);
    Assert.assertEquals(matches, expectMatch, "Filter=" + filterStr + "\nEntry=" + entry);
  }
}