From b57960e67b69040394c9dcb687af52305fd47ab8 Mon Sep 17 00:00:00 2001
From: Manuel Gaupp <m.gaupp@scanplus.de>
Date: Mon, 13 Jan 2014 09:45:09 +0000
Subject: [PATCH] CR-1602 (OPENDJ-883) Implement certificateExactMatch matching rule
---
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/GSERParserTestCase.java | 380 ++++++++++
opends/src/server/org/opends/server/schema/CertificateExactMatchingRuleFactory.java | 72 ++
opends/src/server/org/opends/server/protocols/asn1/GSERParser.java | 476 +++++++++++++
opends/tests/unit-tests-testng/src/server/org/opends/server/schema/CertificateExactMatchingRuleTest.java | 245 ++++++
opends/src/server/org/opends/server/protocols/asn1/GSERException.java | 77 ++
opends/src/messages/messages/protocol.properties | 15
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java | 59 +
opends/src/messages/messages/schema.properties | 15
opends/resource/config/config.ldif | 10
opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java | 7
opends/src/server/org/opends/server/types/SearchFilter.java | 53 +
opends/src/server/org/opends/server/schema/SchemaConstants.java | 49 +
opends/src/server/org/opends/server/schema/CertificateExactAssertionSyntax.java | 215 +++++
opends/src/server/org/opends/server/schema/CertificateSyntax.java | 5
opends/src/server/org/opends/server/schema/CertificateExactMatchingRule.java | 418 +++++++++++
opends/tests/unit-tests-testng/src/server/org/opends/server/controls/MatchedValuesControlTest.java | 48 +
opends/resource/schema/00-core.ldif | 4
opends/src/server/org/opends/server/controls/MatchedValuesFilter.java | 11
18 files changed, 2,138 insertions(+), 21 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 5c7fe98..dd2d7a4 100644
--- a/opends/resource/config/config.ldif
+++ b/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
diff --git a/opends/resource/schema/00-core.ldif b/opends/resource/schema/00-core.ldif
index 3581ade..22b9777 100644
--- a/opends/resource/schema/00-core.ldif
+++ b/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' )
diff --git a/opends/src/messages/messages/protocol.properties b/opends/src/messages/messages/protocol.properties
index cc1fb4a..cc3b1b3 100644
--- a/opends/src/messages/messages/protocol.properties
+++ b/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
diff --git a/opends/src/messages/messages/schema.properties b/opends/src/messages/messages/schema.properties
index ff59978..e1ec32c 100644
--- a/opends/src/messages/messages/schema.properties
+++ b/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
\ No newline at end of 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"
diff --git a/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java b/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
index f63eb58..2b7b651 100644
--- a/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
+++ b/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)
diff --git a/opends/src/server/org/opends/server/controls/MatchedValuesFilter.java b/opends/src/server/org/opends/server/controls/MatchedValuesFilter.java
index 52af02a..8d49f56 100644
--- a/opends/src/server/org/opends/server/controls/MatchedValuesFilter.java
+++ b/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)
{
diff --git a/opends/src/server/org/opends/server/protocols/asn1/GSERException.java b/opends/src/server/org/opends/server/protocols/asn1/GSERException.java
new file mode 100644
index 0000000..841cb67
--- /dev/null
+++ b/opends/src/server/org/opends/server/protocols/asn1/GSERException.java
@@ -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);
+ }
+}
+
diff --git a/opends/src/server/org/opends/server/protocols/asn1/GSERParser.java b/opends/src/server/org/opends/server/protocols/asn1/GSERParser.java
new file mode 100644
index 0000000..ac33e1a
--- /dev/null
+++ b/opends/src/server/org/opends/server/protocols/asn1/GSERParser.java
@@ -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 <identifier> 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 ; " (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);
+ }
+
+
+}
diff --git a/opends/src/server/org/opends/server/schema/CertificateExactAssertionSyntax.java b/opends/src/server/org/opends/server/schema/CertificateExactAssertionSyntax.java
new file mode 100644
index 0000000..3cd393f
--- /dev/null
+++ b/opends/src/server/org/opends/server/schema/CertificateExactAssertionSyntax.java
@@ -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;
+ }
+}
+
diff --git a/opends/src/server/org/opends/server/schema/CertificateExactMatchingRule.java b/opends/src/server/org/opends/server/schema/CertificateExactMatchingRule.java
new file mode 100644
index 0000000..b3c450c
--- /dev/null
+++ b/opends/src/server/org/opends/server/schema/CertificateExactMatchingRule.java
@@ -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();
+ }
+
+}
+
diff --git a/opends/src/server/org/opends/server/schema/CertificateExactMatchingRuleFactory.java b/opends/src/server/org/opends/server/schema/CertificateExactMatchingRuleFactory.java
new file mode 100644
index 0000000..c05740e
--- /dev/null
+++ b/opends/src/server/org/opends/server/schema/CertificateExactMatchingRuleFactory.java
@@ -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);
+ }
+}
diff --git a/opends/src/server/org/opends/server/schema/CertificateSyntax.java b/opends/src/server/org/opends/server/schema/CertificateSyntax.java
index 93285aa..0d6dc0b 100644
--- a/opends/src/server/org/opends/server/schema/CertificateSyntax.java
+++ b/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 =
diff --git a/opends/src/server/org/opends/server/schema/SchemaConstants.java b/opends/src/server/org/opends/server/schema/SchemaConstants.java
index d582d4d..9dcf78f 100644
--- a/opends/src/server/org/opends/server/schema/SchemaConstants.java
+++ b/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";
diff --git a/opends/src/server/org/opends/server/types/SearchFilter.java b/opends/src/server/org/opends/server/types/SearchFilter.java
index ba6c269..5bc9b5e 100644
--- a/opends/src/server/org/opends/server/types/SearchFilter.java
+++ b/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;
}
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/MatchedValuesControlTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/MatchedValuesControlTest.java
index f90ae5a..0ddc0cd 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/MatchedValuesControlTest.java
+++ b/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));
+ }
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/GSERParserTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/GSERParserTestCase.java
new file mode 100644
index 0000000..41b1fbb
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/GSERParserTestCase.java
@@ -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);
+ }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/CertificateExactMatchingRuleTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/CertificateExactMatchingRuleTest.java
new file mode 100644
index 0000000..6acae1c
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/CertificateExactMatchingRuleTest.java
@@ -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);
+ }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
index aa40f22..c1c6ee4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
+++ b/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);
+ }
+
+
+
+
}
--
Gitblit v1.10.0