From 86c9d1fa27d47c82fce9794f49ab717dcbcd58e0 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 26 Feb 2007 20:40:46 +0000
Subject: [PATCH] Add three new certificate mappers to the server:
---
opendj-sdk/opends/src/server/org/opends/server/extensions/FingerprintCertificateMapper.java | 760 ++++++++
opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java | 584 ++++++
opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java | 31
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapperTestCase.java | 851 ++++++++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapperTestCase.java | 710 ++++++++
opendj-sdk/opends/resource/config/config.ldif | 33
opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java | 806 +++++++++
opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java | 525 ++++++
opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java | 49
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FingerprintCertificateMapperTestCase.java | 681 ++++++++
opendj-sdk/opends/resource/schema/02-config.ldif | 41
11 files changed, 5,069 insertions(+), 2 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index 1756208..c8e3e55 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -279,10 +279,39 @@
dn: cn=Subject Equals DN,cn=Certificate Mappers,cn=config
objectClass: top
objectClass: ds-cfg-certificate-mapper
-cn: Certificate Mapper
+cn: Subject Equals DN
ds-cfg-certificate-mapper-class: org.opends.server.extensions.SubjectEqualsDNCertificateMapper
ds-cfg-certificate-mapper-enabled: true
+dn: cn=Subject DN to User Attribute,cn=Certificate Mappers,cn=config
+objectClass: top
+objectClass: ds-cfg-certificate-mapper
+objectClass: ds-cfg-subject-dn-to-user-attribute-certificate-mapper
+cn: Subject DN to User Attribute
+ds-cfg-certificate-mapper-class: org.opends.server.extensions.SubjectDNToUserAttributeCertificateMapper
+ds-cfg-certificate-mapper-enabled: true
+ds-cfg-certificate-subject-attribute-type: ds-certificate-subject-dn
+
+dn: cn=Subject Attribute to User Attribute,cn=Certificate Mappers,cn=config
+objectClass: top
+objectClass: ds-cfg-certificate-mapper
+objectClass: ds-cfg-subject-attribute-to-user-attribute-certificate-mapper
+cn: Subject Attribute to User Attribute
+ds-cfg-certificate-mapper-class: org.opends.server.extensions.SubjectAttributeToUserAttributeCertificateMapper
+ds-cfg-certificate-mapper-enabled: true
+ds-cfg-certificate-subject-attribute-mapping: cn:cn
+ds-cfg-certificate-subject-attribute-mapping: e:mail
+
+dn: cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config
+objectClass: top
+objectClass: ds-cfg-certificate-mapper
+objectClass: ds-cfg-fingerprint-certificate-mapper
+cn: Fingerprint Mapper
+ds-cfg-certificate-mapper-class: org.opends.server.extensions.FingerprintCertificateMapper
+ds-cfg-certificate-mapper-enabled: true
+ds-cfg-certificate-fingerprint-attribute-type: ds-certificate-fingerprint
+ds-cfg-certificate-fingerprint-algorithm: MD5
+
dn: cn=Connection Handlers,cn=config
objectClass: top
objectClass: ds-cfg-branch
@@ -1587,7 +1616,7 @@
ds-cfg-trust-manager-provider-class: org.opends.server.extensions.FileBasedTrustManagerProvider
ds-cfg-trust-manager-provider-enabled: false
ds-cfg-trust-store-type: PKCS12
-ds-cfg-trust-store-file: config/truststore
+ds-cfg-trust-store-file: config/truststore.p12
dn: cn=Virtual Attributes,cn=config
objectClass: top
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index b04876a..a7d8860 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1057,6 +1057,29 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.311
NAME 'ds-cfg-trust-manager-provider-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.312
+ NAME 'ds-cfg-certificate-subject-attribute-type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.313
+ NAME 'ds-cfg-certificate-user-base-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.314
+ NAME 'ds-certificate-subject-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.315
+ NAME 'ds-cfg-certificate-subject-attribute-mapping'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.316 NAME 'ds-certificate-fingerprint'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.317
+ NAME 'ds-cfg-certificate-fingerprint-attribute-type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.318
+ NAME 'ds-cfg-certificate-fingerprint-algorithm'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1449,4 +1472,22 @@
objectClasses: ( 1.3.6.1.4.1.26027.1.2.82 NAME 'ds-cfg-root-dn-base' SUP top
STRUCTURAL MUST cn MAY ds-cfg-default-root-privilege-name
X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.83 NAME 'ds-certificate-user' SUP top
+ AUXILIARY MAY ( userCertificate $ ds-certificate-subject-dn $
+ ds-certificate-fingerprint ) X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.84
+ NAME 'ds-cfg-subject-dn-to-user-attribute-certificate-mapper'
+ SUP ds-cfg-certificate-mapper STRUCTURAL
+ MUST ds-cfg-certificate-subject-attribute-type
+ MAY ds-cfg-certificate-user-base-dn X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.85
+ NAME 'ds-cfg-subject-attribute-to-user-attribute-certificate-mapper'
+ SUP ds-cfg-certificate-mapper STRUCTURAL
+ MUST ds-cfg-certificate-subject-attribute-mapping
+ MAY ds-cfg-certificate-user-base-dn X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.86
+ NAME 'ds-cfg-fingerprint-certificate-mapper' SUP ds-cfg-certificate-mapper
+ STRUCTURAL MUST ( ds-cfg-certificate-fingerprint-attribute-type $
+ ds-cfg-certificate-fingerprint-algorithm )
+ MAY ds-cfg-certificate-user-base-dn X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
index 87d3575..dcbb8ed 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -480,6 +480,55 @@
/**
+ * The name of the configuration attribute that holds the name of the
+ * attribute type that should be used when mapping a certificate fingerprint
+ * to a user entry.
+ */
+ public static final String ATTR_CERTIFICATE_FINGERPRINT_ATTR =
+ "ds-cfg-certificate-fingerprint-attribute-type";
+
+
+
+ /**
+ * The name of the configuration attribute that holds the name of the
+ * algorithm that should be used to generate the certificate fingerprint.
+ */
+ public static final String ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM =
+ "ds-cfg-certificate-fingerprint-algorithm";
+
+
+
+ /**
+ * The name of the configuration attribute that holds the name of the
+ * attribute type that should be used when mapping a certificate subject to a
+ * user entry.
+ */
+ public static final String ATTR_CERTIFICATE_SUBJECT_ATTR =
+ "ds-cfg-certificate-subject-attribute-type";
+
+
+
+ /**
+ * The name of the configuration attribute that holds the name of the
+ * attribute type that should be used when mapping attributes in a certificate
+ * subject to a user entry.
+ */
+ public static final String ATTR_CERTIFICATE_SUBJECT_ATTR_MAP =
+ "ds-cfg-certificate-subject-attribute-mapping";
+
+
+
+ /**
+ * The name of the configuration attribute that holds the name of the
+ * attribute type that should be used when mapping a certificate subject to a
+ * user entry.
+ */
+ public static final String ATTR_CERTIFICATE_SUBJECT_BASEDN =
+ "ds-cfg-certificate-user-base-dn";
+
+
+
+ /**
* The name of the configuration attribute that holds the fully-qualified name
* of the Java class for the certificate mapper implementation.
*/
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/FingerprintCertificateMapper.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/FingerprintCertificateMapper.java
new file mode 100644
index 0000000..26f1a8c
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/FingerprintCertificateMapper.java
@@ -0,0 +1,760 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.security.MessageDigest;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import javax.security.auth.x500.X500Principal;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.api.CertificateMapper;
+import org.opends.server.api.ConfigurableComponent;
+import org.opends.server.config.ConfigAttribute;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.config.DNConfigAttribute;
+import org.opends.server.config.MultiChoiceConfigAttribute;
+import org.opends.server.config.StringConfigAttribute;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class implements a very simple Directory Server certificate mapper that
+ * will map a certificate to a user only if that user's entry contains an
+ * attribute with the fingerprint of the client certificate. There must be
+ * exactly one matching user entry for the mapping to be successful.
+ */
+public class FingerprintCertificateMapper
+ extends CertificateMapper
+ implements ConfigurableComponent
+{
+ /**
+ * The fully-qualified name of this class for debugging purposes.
+ */
+ private static final String CLASS_NAME =
+ "org.opends.server.extensions.FingerprintCertificateMapper";
+
+
+
+ /**
+ * The set of allowed fingerprint algorithms.
+ */
+ private static final Set<String> FINGERPRINT_ALGORITHMS;
+
+
+
+ // The attribute type that will be used to map the certificate's fingerprint.
+ private AttributeType fingerprintAttributeType;
+
+ // The DN of the configuration entry for this certificate mapper.
+ private DN configEntryDN;
+
+ // The set of base DNs below which the search will be performed.
+ private DN[] baseDNs;
+
+ // The algorithm that will be used to generate the fingerprint.
+ private String fingerprintAlgorithm;
+
+
+
+ static
+ {
+ LinkedHashSet<String> algorithmSet = new LinkedHashSet<String>(2);
+ algorithmSet.add("md5");
+ algorithmSet.add("sha1");
+ FINGERPRINT_ALGORITHMS = algorithmSet;
+ }
+
+
+
+ /**
+ * Creates a new instance of this certificate mapper. Note that all actual
+ * initialization should be done in the
+ * <CODE>initializeCertificateMapper</CODE> method.
+ */
+ public FingerprintCertificateMapper()
+ {
+ super();
+
+ assert debugConstructor(CLASS_NAME);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initializeCertificateMapper(ConfigEntry configEntry)
+ throws ConfigException, InitializationException
+ {
+ assert debugEnter(CLASS_NAME, "initializeCertificateMapper",
+ String.valueOf(configEntry));
+
+ this.configEntryDN = configEntry.getDN();
+
+ // Get the attribute type that will be used to hold the fingerprint.
+ int msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ATTR;
+ StringConfigAttribute attrStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_FINGERPRINT_ATTR,
+ getMessage(msgID), true, false, false);
+ try
+ {
+ StringConfigAttribute attrAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(attrStub);
+ if (attrAttr == null)
+ {
+ msgID = MSGID_FCM_NO_FINGERPRINT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_FINGERPRINT_ATTR);
+ throw new ConfigException(msgID, message);
+ }
+ else
+ {
+ String attrName = attrAttr.pendingValue();
+ String lowerName = toLowerCase(attrName);
+ fingerprintAttributeType =
+ DirectoryServer.getAttributeType(lowerName, false);
+ if (fingerprintAttributeType == null)
+ {
+ msgID = MSGID_FCM_NO_SUCH_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ attrName);
+ throw new ConfigException(msgID, message);
+ }
+ }
+ }
+ catch (ConfigException ce)
+ {
+ throw ce;
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_FCM_CANNOT_GET_FINGERPRINT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+
+ // Get the fingerprint algorithm.
+ msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ALGORITHM;
+ MultiChoiceConfigAttribute algorithmStub =
+ new MultiChoiceConfigAttribute(ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM,
+ getMessage(msgID), true, false, false,
+ FINGERPRINT_ALGORITHMS);
+ try
+ {
+ MultiChoiceConfigAttribute algorithmAttr =
+ (MultiChoiceConfigAttribute)
+ configEntry.getConfigAttribute(algorithmStub);
+ if (algorithmAttr == null)
+ {
+ msgID = MSGID_FCM_NO_FINGERPRINT_ALGORITHM;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM);
+ throw new ConfigException(msgID, message);
+ }
+ else
+ {
+ fingerprintAlgorithm = algorithmAttr.pendingValue();
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_FCM_CANNOT_GET_FINGERPRINT_ALGORITHM;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ baseDNs = null;
+ msgID = MSGID_FCM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ baseDNs = new DN[dnList.size()];
+ dnList.toArray(baseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_FCM_CANNOT_GET_BASE_DN;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+ DirectoryServer.registerConfigurableComponent(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void finalizeCertificateMapper()
+ {
+ assert debugEnter(CLASS_NAME, "finalizeCertificateMapper");
+
+ DirectoryServer.deregisterConfigurableComponent(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Entry mapCertificateToUser(Certificate[] certificateChain)
+ throws DirectoryException
+ {
+ assert debugEnter(CLASS_NAME, "mapCertificateToUser",
+ String.valueOf(certificateChain));
+
+
+ // Make sure that a peer certificate was provided.
+ if ((certificateChain == null) || (certificateChain.length == 0))
+ {
+ int msgID = MSGID_FCM_NO_PEER_CERTIFICATE;
+ String message = getMessage(msgID);
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Get the first certificate in the chain. It must be an X.509 certificate.
+ X509Certificate peerCertificate;
+ try
+ {
+ peerCertificate = (X509Certificate) certificateChain[0];
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "mapCertificateToUser", e);
+
+ int msgID = MSGID_FCM_PEER_CERT_NOT_X509;
+ String message =
+ getMessage(msgID, String.valueOf(certificateChain[0].getType()));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Get the signature from the peer certificate and create a digest of it
+ // using the configured algorithm.
+ String fingerprintString;
+ try
+ {
+ MessageDigest digest = MessageDigest.getInstance(fingerprintAlgorithm);
+ byte[] fingerprintBytes = digest.digest(peerCertificate.getEncoded());
+ fingerprintString = bytesToColonDelimitedHex(fingerprintBytes);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "mapCertificateToUser", e);
+
+ String peerSubject = peerCertificate.getSubjectX500Principal().getName(
+ X500Principal.RFC2253);
+
+ int msgID = MSGID_FCM_CANNOT_CALCULATE_FINGERPRINT;
+ String message = getMessage(msgID, peerSubject,
+ stackTraceToSingleLineString(e));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Create the search filter from the fingerprint.
+ AttributeValue value =
+ new AttributeValue(fingerprintAttributeType, fingerprintString);
+ SearchFilter filter =
+ SearchFilter.createEqualityFilter(fingerprintAttributeType, value);
+
+
+ // If we have an explicit set of base DNs, then use it. Otherwise, use the
+ // set of public naming contexts in the server.
+ DN[] bases = baseDNs;
+ if (bases == null)
+ {
+ bases = new DN[0];
+ bases = DirectoryServer.getPublicNamingContexts().keySet().toArray(bases);
+ }
+
+
+ // For each base DN, issue an internal search in an attempt to map the
+ // certificate.
+ Entry userEntry = null;
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ for (DN baseDN : bases)
+ {
+ InternalSearchOperation searchOperation =
+ conn.processSearch(baseDN, SearchScope.WHOLE_SUBTREE, filter);
+ for (SearchResultEntry entry : searchOperation.getSearchEntries())
+ {
+ if (userEntry == null)
+ {
+ userEntry = entry;
+ }
+ else
+ {
+ int msgID = MSGID_FCM_MULTIPLE_MATCHING_ENTRIES;
+ String message = getMessage(msgID, fingerprintString,
+ String.valueOf(userEntry.getDN()),
+ String.valueOf(entry.getDN()));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+ }
+ }
+
+
+ // If we've gotten here, then we either found exactly one user entry or we
+ // didn't find any. Either way, return the entry or null to the caller.
+ return userEntry;
+ }
+
+
+
+ /**
+ * Retrieves the DN of the configuration entry with which this
+ * component is associated.
+ *
+ * @return The DN of the configuration entry with which this
+ * component is associated.
+ */
+ public DN getConfigurableComponentEntryDN()
+ {
+ assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
+
+ return configEntryDN;
+ }
+
+
+
+ /**
+ * Retrieves the set of configuration attributes that are associated
+ * with this configurable component.
+ *
+ * @return The set of configuration attributes that are associated
+ * with this configurable component.
+ */
+ public List<ConfigAttribute> getConfigurationAttributes()
+ {
+ assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
+
+ LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
+
+ int msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ATTR;
+ attrList.add(new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR,
+ getMessage(msgID), true, false, false,
+ fingerprintAttributeType.getNameOrOID()));
+
+ msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ALGORITHM;
+ attrList.add(new MultiChoiceConfigAttribute(
+ ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM,
+ getMessage(msgID), true, false, false,
+ FINGERPRINT_ALGORITHMS, fingerprintAlgorithm));
+
+ LinkedList<DN> dnList = new LinkedList<DN>();
+ if (baseDNs != null)
+ {
+ for (DN baseDN : baseDNs)
+ {
+ dnList.add(baseDN);
+ }
+ }
+
+ msgID = MSGID_FCM_DESCRIPTION_BASE_DN;
+ attrList.add(new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false,
+ dnList));
+
+ return attrList;
+ }
+
+
+
+ /**
+ * Indicates whether the provided configuration entry has an
+ * acceptable configuration for this component. If it does not,
+ * then detailed information about the problem(s) should be added to
+ * the provided list.
+ *
+ * @param configEntry The configuration entry for which to
+ * make the determination.
+ * @param unacceptableReasons A list that can be used to hold
+ * messages about why the provided
+ * entry does not have an acceptable
+ * configuration.
+ *
+ * @return <CODE>true</CODE> if the provided entry has an
+ * acceptable configuration for this component, or
+ * <CODE>false</CODE> if not.
+ */
+ public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
+ List<String> unacceptableReasons)
+ {
+ assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
+ String.valueOf(configEntry), "java.util.List<String>");
+
+ DN configEntryDN = configEntry.getDN();
+ boolean configAcceptable = true;
+
+
+ // Get the attribute type that will be used to hold the fingerprint.
+ AttributeType newFingerprintType = null;
+ int msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ATTR;
+ StringConfigAttribute attrStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_FINGERPRINT_ATTR,
+ getMessage(msgID), true, false, false);
+ try
+ {
+ StringConfigAttribute attrAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(attrStub);
+ if (attrAttr == null)
+ {
+ msgID = MSGID_FCM_NO_FINGERPRINT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_FINGERPRINT_ATTR);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ else
+ {
+ String attrName = attrAttr.pendingValue();
+ String lowerName = toLowerCase(attrName);
+ newFingerprintType =
+ DirectoryServer.getAttributeType(lowerName, false);
+ if (newFingerprintType == null)
+ {
+ msgID = MSGID_FCM_NO_SUCH_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ attrName);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_FCM_CANNOT_GET_FINGERPRINT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ // Get the fingerprint algorithm.
+ String newFingerprintAlgorithm = null;
+ msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ALGORITHM;
+ MultiChoiceConfigAttribute algorithmStub =
+ new MultiChoiceConfigAttribute(ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM,
+ getMessage(msgID), true, false, false,
+ FINGERPRINT_ALGORITHMS);
+ try
+ {
+ MultiChoiceConfigAttribute algorithmAttr =
+ (MultiChoiceConfigAttribute)
+ configEntry.getConfigAttribute(algorithmStub);
+ if (algorithmAttr == null)
+ {
+ msgID = MSGID_FCM_NO_FINGERPRINT_ALGORITHM;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ else
+ {
+ newFingerprintAlgorithm = algorithmAttr.pendingValue();
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_FCM_CANNOT_GET_FINGERPRINT_ALGORITHM;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ DN[] newBaseDNs = null;
+ msgID = MSGID_FCM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ newBaseDNs = new DN[dnList.size()];
+ dnList.toArray(newBaseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_FCM_CANNOT_GET_BASE_DN;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ return configAcceptable;
+ }
+
+
+
+ /**
+ * Makes a best-effort attempt to apply the configuration contained
+ * in the provided entry. Information about the result of this
+ * processing should be added to the provided message list.
+ * Information should always be added to this list if a
+ * configuration change could not be applied. If detailed results
+ * are requested, then information about the changes applied
+ * successfully (and optionally about parameters that were not
+ * changed) should also be included.
+ *
+ * @param configEntry The entry containing the new
+ * configuration to apply for this
+ * component.
+ * @param detailedResults Indicates whether detailed information
+ * about the processing should be added to
+ * the list.
+ *
+ * @return Information about the result of the configuration
+ * update.
+ */
+ public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
+ boolean detailedResults)
+ {
+ assert debugEnter(CLASS_NAME, "applyNewConfiguration",
+ String.valueOf(configEntry),
+ String.valueOf(detailedResults));
+
+ DN configEntryDN = configEntry.getDN();
+ ResultCode resultCode = ResultCode.SUCCESS;
+ ArrayList<String> messages = new ArrayList<String>();
+ boolean adminActionRequired = false;
+
+
+ // Get the attribute type that will be used to hold the fingerprint.
+ AttributeType newFingerprintType = null;
+ int msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ATTR;
+ StringConfigAttribute attrStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_FINGERPRINT_ATTR,
+ getMessage(msgID), true, false, false);
+ try
+ {
+ StringConfigAttribute attrAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(attrStub);
+ if (attrAttr == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.OBJECTCLASS_VIOLATION;
+ }
+
+ msgID = MSGID_FCM_NO_FINGERPRINT_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_FINGERPRINT_ATTR));
+ }
+ else
+ {
+ String attrName = attrAttr.pendingValue();
+ String lowerName = toLowerCase(attrName);
+ newFingerprintType =
+ DirectoryServer.getAttributeType(lowerName, false);
+ if (newFingerprintType == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
+ }
+
+ msgID = MSGID_FCM_NO_SUCH_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ attrName));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ }
+
+ msgID = MSGID_FCM_CANNOT_GET_FINGERPRINT_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ // Get the fingerprint algorithm.
+ String newFingerprintAlgorithm = null;
+ msgID = MSGID_FCM_DESCRIPTION_FINGERPRINT_ALGORITHM;
+ MultiChoiceConfigAttribute algorithmStub =
+ new MultiChoiceConfigAttribute(ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM,
+ getMessage(msgID), true, false, false,
+ FINGERPRINT_ALGORITHMS);
+ try
+ {
+ MultiChoiceConfigAttribute algorithmAttr =
+ (MultiChoiceConfigAttribute)
+ configEntry.getConfigAttribute(algorithmStub);
+ if (algorithmAttr == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.OBJECTCLASS_VIOLATION;
+ }
+
+ msgID = MSGID_FCM_NO_FINGERPRINT_ALGORITHM;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_FINGERPRINT_ALGORITHM));
+ }
+ else
+ {
+ newFingerprintAlgorithm = algorithmAttr.pendingValue();
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ }
+
+ msgID = MSGID_FCM_CANNOT_GET_FINGERPRINT_ALGORITHM;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ DN[] newBaseDNs = null;
+ msgID = MSGID_FCM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ newBaseDNs = new DN[dnList.size()];
+ dnList.toArray(newBaseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ }
+
+ msgID = MSGID_FCM_CANNOT_GET_BASE_DN;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ fingerprintAttributeType = newFingerprintType;
+ fingerprintAlgorithm = newFingerprintAlgorithm;
+ baseDNs = newBaseDNs;
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java
new file mode 100644
index 0000000..f5777fa
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java
@@ -0,0 +1,806 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import javax.security.auth.x500.X500Principal;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.server.api.CertificateMapper;
+import org.opends.server.api.ConfigurableComponent;
+import org.opends.server.config.ConfigAttribute;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.config.DNConfigAttribute;
+import org.opends.server.config.StringConfigAttribute;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.RDN;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class implements a very simple Directory Server certificate mapper that
+ * will map a certificate to a user based on attributes contained in both the
+ * certificate subject and the user's entry. The configuration may include
+ * mappings from certificate attributes to attributes in user entries, and all
+ * of those certificate attributes that are present in the subject will be used
+ * to search for matching user entries.
+ */
+public class SubjectAttributeToUserAttributeCertificateMapper
+ extends CertificateMapper
+ implements ConfigurableComponent
+{
+ /**
+ * The fully-qualified name of this class for debugging purposes.
+ */
+ private static final String CLASS_NAME =
+ "org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper";
+
+
+
+ // The DN of the configuration entry for this certificate mapper.
+ private DN configEntryDN;
+
+ // The set of base DNs below which the search will be performed.
+ private DN[] baseDNs;
+
+ // The mappings between certificate attribute names and user attribute types.
+ private LinkedHashMap<String,AttributeType> attributeMap;
+
+
+
+ /**
+ * Creates a new instance of this certificate mapper. Note that all actual
+ * initialization should be done in the
+ * <CODE>initializeCertificateMapper</CODE> method.
+ */
+ public SubjectAttributeToUserAttributeCertificateMapper()
+ {
+ super();
+
+ assert debugConstructor(CLASS_NAME);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initializeCertificateMapper(ConfigEntry configEntry)
+ throws ConfigException, InitializationException
+ {
+ assert debugEnter(CLASS_NAME, "initializeCertificateMapper",
+ String.valueOf(configEntry));
+
+ this.configEntryDN = configEntry.getDN();
+
+ // Get the attribute that will be used to map subject attributes to user
+ // attributes.
+ attributeMap = new LinkedHashMap<String,AttributeType>();
+ int msgID = MSGID_SATUACM_DESCRIPTION_ATTR_MAP;
+ StringConfigAttribute mapStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR_MAP,
+ getMessage(msgID), true, true, false);
+ try
+ {
+ StringConfigAttribute mapAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(mapStub);
+ if (mapAttr == null)
+ {
+ msgID = MSGID_SATUACM_NO_MAP_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_SUBJECT_ATTR_MAP);
+ throw new ConfigException(msgID, message);
+ }
+ else
+ {
+ for (String mapStr : mapAttr.pendingValues())
+ {
+ String lowerMap = toLowerCase(mapStr);
+ int colonPos = lowerMap.indexOf(':');
+ if (colonPos <= 0)
+ {
+ msgID = MSGID_SATUACM_INVALID_MAP_FORMAT;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ mapStr);
+ throw new ConfigException(msgID, message);
+ }
+
+ String certAttrName = lowerMap.substring(0, colonPos).trim();
+ String userAttrName = lowerMap.substring(colonPos+1).trim();
+ if ((certAttrName.length() == 0) || (userAttrName.length() == 0))
+ {
+ msgID = MSGID_SATUACM_INVALID_MAP_FORMAT;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ mapStr);
+ throw new ConfigException(msgID, message);
+ }
+
+ if (attributeMap.containsKey(certAttrName))
+ {
+ msgID = MSGID_SATUACM_DUPLICATE_CERT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ certAttrName);
+ throw new ConfigException(msgID, message);
+ }
+
+ AttributeType userAttrType =
+ DirectoryServer.getAttributeType(userAttrName, false);
+ if (userAttrType == null)
+ {
+ msgID = MSGID_SATUACM_NO_SUCH_ATTR;
+ String message = getMessage(msgID, mapStr,
+ String.valueOf(configEntryDN),
+ userAttrName);
+ throw new ConfigException(msgID, message);
+ }
+
+ for (AttributeType attrType : attributeMap.values())
+ {
+ if (attrType.equals(userAttrType))
+ {
+ msgID = MSGID_SATUACM_DUPLICATE_USER_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ attrType.getNameOrOID());
+ throw new ConfigException(msgID, message);
+ }
+ }
+
+ attributeMap.put(certAttrName, userAttrType);
+ }
+ }
+ }
+ catch (ConfigException ce)
+ {
+ throw ce;
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_SATUACM_CANNOT_GET_ATTR_MAP;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ baseDNs = null;
+ msgID = MSGID_SATUACM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ baseDNs = new DN[dnList.size()];
+ dnList.toArray(baseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_SATUACM_CANNOT_GET_BASE_DN;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+ DirectoryServer.registerConfigurableComponent(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void finalizeCertificateMapper()
+ {
+ assert debugEnter(CLASS_NAME, "finalizeCertificateMapper");
+
+ DirectoryServer.deregisterConfigurableComponent(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Entry mapCertificateToUser(Certificate[] certificateChain)
+ throws DirectoryException
+ {
+ assert debugEnter(CLASS_NAME, "mapCertificateToUser",
+ String.valueOf(certificateChain));
+
+
+ // Make sure that a peer certificate was provided.
+ if ((certificateChain == null) || (certificateChain.length == 0))
+ {
+ int msgID = MSGID_SATUACM_NO_PEER_CERTIFICATE;
+ String message = getMessage(msgID);
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Get the first certificate in the chain. It must be an X.509 certificate.
+ X509Certificate peerCertificate;
+ try
+ {
+ peerCertificate = (X509Certificate) certificateChain[0];
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "mapCertificateToUser", e);
+
+ int msgID = MSGID_SATUACM_PEER_CERT_NOT_X509;
+ String message =
+ getMessage(msgID, String.valueOf(certificateChain[0].getType()));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Get the subject from the peer certificate and use it to create a search
+ // filter.
+ DN peerDN;
+ X500Principal peerPrincipal = peerCertificate.getSubjectX500Principal();
+ String peerName = peerPrincipal.getName(X500Principal.RFC2253);
+ try
+ {
+ peerDN = DN.decode(peerName);
+ }
+ catch (DirectoryException de)
+ {
+ int msgID = MSGID_SATUACM_CANNOT_DECODE_SUBJECT_AS_DN;
+ String message = getMessage(msgID, peerName, de.getErrorMessage());
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID, de);
+ }
+
+ LinkedList<SearchFilter> filterComps = new LinkedList<SearchFilter>();
+ for (int i=0; i < peerDN.getNumComponents(); i++)
+ {
+ RDN rdn = peerDN.getRDN(i);
+ for (int j=0; j < rdn.getNumValues(); j++)
+ {
+ String lowerName = toLowerCase(rdn.getAttributeName(j));
+ AttributeType attrType = attributeMap.get(lowerName);
+ if (attrType != null)
+ {
+ filterComps.add(SearchFilter.createEqualityFilter(attrType,
+ rdn.getAttributeValue(j)));
+ }
+ }
+ }
+
+ if (filterComps.isEmpty())
+ {
+ int msgID = MSGID_SATUACM_NO_MAPPABLE_ATTRIBUTES;
+ String message = getMessage(msgID, peerName);
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+ SearchFilter filter = SearchFilter.createANDFilter(filterComps);
+
+
+ // If we have an explicit set of base DNs, then use it. Otherwise, use the
+ // set of public naming contexts in the server.
+ DN[] bases = baseDNs;
+ if (bases == null)
+ {
+ bases = new DN[0];
+ bases = DirectoryServer.getPublicNamingContexts().keySet().toArray(bases);
+ }
+
+
+ // For each base DN, issue an internal search in an attempt to map the
+ // certificate.
+ Entry userEntry = null;
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ for (DN baseDN : bases)
+ {
+ InternalSearchOperation searchOperation =
+ conn.processSearch(baseDN, SearchScope.WHOLE_SUBTREE, filter);
+ for (SearchResultEntry entry : searchOperation.getSearchEntries())
+ {
+ if (userEntry == null)
+ {
+ userEntry = entry;
+ }
+ else
+ {
+ int msgID = MSGID_SATUACM_MULTIPLE_MATCHING_ENTRIES;
+ String message = getMessage(msgID, peerName,
+ String.valueOf(userEntry.getDN()),
+ String.valueOf(entry.getDN()));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+ }
+ }
+
+
+ // If we've gotten here, then we either found exactly one user entry or we
+ // didn't find any. Either way, return the entry or null to the caller.
+ return userEntry;
+ }
+
+
+
+ /**
+ * Retrieves the DN of the configuration entry with which this
+ * component is associated.
+ *
+ * @return The DN of the configuration entry with which this
+ * component is associated.
+ */
+ public DN getConfigurableComponentEntryDN()
+ {
+ assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
+
+ return configEntryDN;
+ }
+
+
+
+ /**
+ * Retrieves the set of configuration attributes that are associated
+ * with this configurable component.
+ *
+ * @return The set of configuration attributes that are associated
+ * with this configurable component.
+ */
+ public List<ConfigAttribute> getConfigurationAttributes()
+ {
+ assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
+
+ LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
+
+ LinkedList<String> mapValues = new LinkedList<String>();
+ for (String certAttrName : attributeMap.keySet())
+ {
+ AttributeType certAttrType = attributeMap.get(certAttrName);
+ mapValues.add(certAttrName + ":" + certAttrType.getNameOrOID());
+ }
+
+ int msgID = MSGID_SATUACM_DESCRIPTION_ATTR_MAP;
+ attrList.add(new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR_MAP,
+ getMessage(msgID), true, false,
+ false, mapValues));
+
+ LinkedList<DN> dnList = new LinkedList<DN>();
+ if (baseDNs != null)
+ {
+ for (DN baseDN : baseDNs)
+ {
+ dnList.add(baseDN);
+ }
+ }
+
+ msgID = MSGID_SATUACM_DESCRIPTION_BASE_DN;
+ attrList.add(new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false,
+ dnList));
+
+ return attrList;
+ }
+
+
+
+ /**
+ * Indicates whether the provided configuration entry has an
+ * acceptable configuration for this component. If it does not,
+ * then detailed information about the problem(s) should be added to
+ * the provided list.
+ *
+ * @param configEntry The configuration entry for which to
+ * make the determination.
+ * @param unacceptableReasons A list that can be used to hold
+ * messages about why the provided
+ * entry does not have an acceptable
+ * configuration.
+ *
+ * @return <CODE>true</CODE> if the provided entry has an
+ * acceptable configuration for this component, or
+ * <CODE>false</CODE> if not.
+ */
+ public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
+ List<String> unacceptableReasons)
+ {
+ assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
+ String.valueOf(configEntry), "java.util.List<String>");
+
+ DN configEntryDN = configEntry.getDN();
+ boolean configAcceptable = true;
+
+
+ // Get the attribute that will be used to map subject attributes to user
+ // attributes.
+ LinkedHashMap<String,AttributeType> newAttributeMap =
+ new LinkedHashMap<String,AttributeType>();
+ int msgID = MSGID_SATUACM_DESCRIPTION_ATTR_MAP;
+ StringConfigAttribute mapStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR_MAP,
+ getMessage(msgID), true, true, false);
+ try
+ {
+ StringConfigAttribute mapAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(mapStub);
+ if (mapAttr == null)
+ {
+ msgID = MSGID_SATUACM_NO_MAP_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_SUBJECT_ATTR_MAP);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ else
+ {
+mapLoop:
+ for (String mapStr : mapAttr.pendingValues())
+ {
+ String lowerMap = toLowerCase(mapStr);
+ int colonPos = lowerMap.indexOf(':');
+ if (colonPos <= 0)
+ {
+ msgID = MSGID_SATUACM_INVALID_MAP_FORMAT;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ mapStr);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ continue;
+ }
+
+ String certAttrName = lowerMap.substring(0, colonPos).trim();
+ String userAttrName = lowerMap.substring(colonPos+1).trim();
+ if ((certAttrName.length() == 0) || (userAttrName.length() == 0))
+ {
+ msgID = MSGID_SATUACM_INVALID_MAP_FORMAT;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ mapStr);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ continue;
+ }
+
+ if (newAttributeMap.containsKey(certAttrName))
+ {
+ msgID = MSGID_SATUACM_DUPLICATE_CERT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ certAttrName);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ continue;
+ }
+
+ AttributeType userAttrType =
+ DirectoryServer.getAttributeType(userAttrName, false);
+ if (userAttrType == null)
+ {
+ msgID = MSGID_SATUACM_NO_SUCH_ATTR;
+ String message = getMessage(msgID, mapStr,
+ String.valueOf(configEntryDN),
+ userAttrName);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ continue;
+ }
+
+ for (AttributeType attrType : newAttributeMap.values())
+ {
+ if (attrType.equals(userAttrType))
+ {
+ msgID = MSGID_SATUACM_DUPLICATE_USER_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ attrType.getNameOrOID());
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ continue mapLoop;
+ }
+ }
+
+ newAttributeMap.put(certAttrName, userAttrType);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
+
+ msgID = MSGID_SATUACM_CANNOT_GET_ATTR_MAP;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ DN[] newBaseDNs = null;
+ msgID = MSGID_SATUACM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ newBaseDNs = new DN[dnList.size()];
+ dnList.toArray(newBaseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
+
+ msgID = MSGID_SATUACM_CANNOT_GET_BASE_DN;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ return configAcceptable;
+ }
+
+
+
+ /**
+ * Makes a best-effort attempt to apply the configuration contained
+ * in the provided entry. Information about the result of this
+ * processing should be added to the provided message list.
+ * Information should always be added to this list if a
+ * configuration change could not be applied. If detailed results
+ * are requested, then information about the changes applied
+ * successfully (and optionally about parameters that were not
+ * changed) should also be included.
+ *
+ * @param configEntry The entry containing the new
+ * configuration to apply for this
+ * component.
+ * @param detailedResults Indicates whether detailed information
+ * about the processing should be added to
+ * the list.
+ *
+ * @return Information about the result of the configuration
+ * update.
+ */
+ public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
+ boolean detailedResults)
+ {
+ assert debugEnter(CLASS_NAME, "applyNewConfiguration",
+ String.valueOf(configEntry),
+ String.valueOf(detailedResults));
+
+ DN configEntryDN = configEntry.getDN();
+ ResultCode resultCode = ResultCode.SUCCESS;
+ ArrayList<String> messages = new ArrayList<String>();
+ boolean adminActionRequired = false;
+
+
+ // Get the attribute that will be used to map subject attributes to user
+ // attributes.
+ LinkedHashMap<String,AttributeType> newAttributeMap =
+ new LinkedHashMap<String,AttributeType>();
+ int msgID = MSGID_SATUACM_DESCRIPTION_ATTR_MAP;
+ StringConfigAttribute mapStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR_MAP,
+ getMessage(msgID), true, true, false);
+ try
+ {
+ StringConfigAttribute mapAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(mapStub);
+ if (mapAttr == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.OBJECTCLASS_VIOLATION;
+ }
+
+ msgID = MSGID_SATUACM_NO_MAP_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_SUBJECT_ATTR_MAP));
+ }
+ else
+ {
+mapLoop:
+ for (String mapStr : mapAttr.pendingValues())
+ {
+ String lowerMap = toLowerCase(mapStr);
+ int colonPos = lowerMap.indexOf(':');
+ if (colonPos <= 0)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ msgID = MSGID_SATUACM_INVALID_MAP_FORMAT;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ mapStr));
+ break;
+ }
+
+ String certAttrName = lowerMap.substring(0, colonPos).trim();
+ String userAttrName = lowerMap.substring(colonPos+1).trim();
+ if ((certAttrName.length() == 0) || (userAttrName.length() == 0))
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ msgID = MSGID_SATUACM_INVALID_MAP_FORMAT;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ mapStr));
+ break;
+ }
+
+ if (newAttributeMap.containsKey(certAttrName))
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.CONSTRAINT_VIOLATION;
+ }
+
+ msgID = MSGID_SATUACM_DUPLICATE_CERT_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ certAttrName));
+ break;
+ }
+
+ AttributeType userAttrType =
+ DirectoryServer.getAttributeType(userAttrName, false);
+ if (userAttrType == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
+ }
+
+ msgID = MSGID_SATUACM_NO_SUCH_ATTR;
+ messages.add(getMessage(msgID, mapStr,
+ String.valueOf(configEntryDN),
+ userAttrName));
+ break;
+ }
+
+ for (AttributeType attrType : newAttributeMap.values())
+ {
+ if (attrType.equals(userAttrType))
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.CONSTRAINT_VIOLATION;
+ }
+
+ msgID = MSGID_SATUACM_DUPLICATE_USER_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ attrType.getNameOrOID()));
+ break mapLoop;
+ }
+ }
+
+ newAttributeMap.put(certAttrName, userAttrType);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyNewConfiguration", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ }
+
+ msgID = MSGID_SATUACM_CANNOT_GET_ATTR_MAP;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ DN[] newBaseDNs = null;
+ msgID = MSGID_SATUACM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ newBaseDNs = new DN[dnList.size()];
+ dnList.toArray(newBaseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyNewConfiguration", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ }
+
+ msgID = MSGID_SATUACM_CANNOT_GET_BASE_DN;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ attributeMap = newAttributeMap;
+ baseDNs = newBaseDNs;
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java
new file mode 100644
index 0000000..5e0f536
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java
@@ -0,0 +1,584 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import javax.security.auth.x500.X500Principal;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.server.api.CertificateMapper;
+import org.opends.server.api.ConfigurableComponent;
+import org.opends.server.config.ConfigAttribute;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.config.DNConfigAttribute;
+import org.opends.server.config.StringConfigAttribute;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class implements a very simple Directory Server certificate mapper that
+ * will map a certificate to a user only if that user's entry contains an
+ * attribute with the subject of the client certificate. There must be exactly
+ * one matching user entry for the mapping to be successful.
+ */
+public class SubjectDNToUserAttributeCertificateMapper
+ extends CertificateMapper
+ implements ConfigurableComponent
+{
+ /**
+ * The fully-qualified name of this class for debugging purposes.
+ */
+ private static final String CLASS_NAME =
+ "org.opends.server.extensions.SubjectDNToUserAttributeCertificateMapper";
+
+
+
+ // The attribute type that will be used to map the certificate's subject.
+ private AttributeType subjectAttributeType;
+
+ // The DN of the configuration entry for this certificate mapper.
+ private DN configEntryDN;
+
+ // The set of base DNs below which the search will be performed.
+ private DN[] baseDNs;
+
+
+
+ /**
+ * Creates a new instance of this certificate mapper. Note that all actual
+ * initialization should be done in the
+ * <CODE>initializeCertificateMapper</CODE> method.
+ */
+ public SubjectDNToUserAttributeCertificateMapper()
+ {
+ super();
+
+ assert debugConstructor(CLASS_NAME);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initializeCertificateMapper(ConfigEntry configEntry)
+ throws ConfigException, InitializationException
+ {
+ assert debugEnter(CLASS_NAME, "initializeCertificateMapper",
+ String.valueOf(configEntry));
+
+ this.configEntryDN = configEntry.getDN();
+
+ // Get the attribute type that will be used to hold the certificate subject.
+ int msgID = MSGID_SDTUACM_DESCRIPTION_SUBJECT_ATTR;
+ StringConfigAttribute attrStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR,
+ getMessage(msgID), true, false, false);
+ try
+ {
+ StringConfigAttribute attrAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(attrStub);
+ if (attrAttr == null)
+ {
+ msgID = MSGID_SDTUACM_NO_SUBJECT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_SUBJECT_ATTR);
+ throw new ConfigException(msgID, message);
+ }
+ else
+ {
+ String attrName = attrAttr.pendingValue();
+ String lowerName = toLowerCase(attrName);
+ subjectAttributeType =
+ DirectoryServer.getAttributeType(lowerName, false);
+ if (subjectAttributeType == null)
+ {
+ msgID = MSGID_SDTUACM_NO_SUCH_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ attrName);
+ throw new ConfigException(msgID, message);
+ }
+ }
+ }
+ catch (ConfigException ce)
+ {
+ throw ce;
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_SDTUACM_CANNOT_GET_SUBJECT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ baseDNs = null;
+ msgID = MSGID_SDTUACM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ baseDNs = new DN[dnList.size()];
+ dnList.toArray(baseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeCertificateMapper", e);
+
+ msgID = MSGID_SDTUACM_CANNOT_GET_BASE_DN;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+ DirectoryServer.registerConfigurableComponent(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void finalizeCertificateMapper()
+ {
+ assert debugEnter(CLASS_NAME, "finalizeCertificateMapper");
+
+ DirectoryServer.deregisterConfigurableComponent(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Entry mapCertificateToUser(Certificate[] certificateChain)
+ throws DirectoryException
+ {
+ assert debugEnter(CLASS_NAME, "mapCertificateToUser",
+ String.valueOf(certificateChain));
+
+
+ // Make sure that a peer certificate was provided.
+ if ((certificateChain == null) || (certificateChain.length == 0))
+ {
+ int msgID = MSGID_SDTUACM_NO_PEER_CERTIFICATE;
+ String message = getMessage(msgID);
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Get the first certificate in the chain. It must be an X.509 certificate.
+ X509Certificate peerCertificate;
+ try
+ {
+ peerCertificate = (X509Certificate) certificateChain[0];
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "mapCertificateToUser", e);
+
+ int msgID = MSGID_SDTUACM_PEER_CERT_NOT_X509;
+ String message =
+ getMessage(msgID, String.valueOf(certificateChain[0].getType()));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+
+
+ // Get the subject from the peer certificate and use it to create a search
+ // filter.
+ X500Principal peerPrincipal = peerCertificate.getSubjectX500Principal();
+ String peerName = peerPrincipal.getName(X500Principal.RFC2253);
+ AttributeValue value = new AttributeValue(subjectAttributeType, peerName);
+ SearchFilter filter =
+ SearchFilter.createEqualityFilter(subjectAttributeType, value);
+
+
+ // If we have an explicit set of base DNs, then use it. Otherwise, use the
+ // set of public naming contexts in the server.
+ DN[] bases = baseDNs;
+ if (bases == null)
+ {
+ bases = new DN[0];
+ bases = DirectoryServer.getPublicNamingContexts().keySet().toArray(bases);
+ }
+
+
+ // For each base DN, issue an internal search in an attempt to map the
+ // certificate.
+ Entry userEntry = null;
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ for (DN baseDN : bases)
+ {
+ InternalSearchOperation searchOperation =
+ conn.processSearch(baseDN, SearchScope.WHOLE_SUBTREE, filter);
+ for (SearchResultEntry entry : searchOperation.getSearchEntries())
+ {
+ if (userEntry == null)
+ {
+ userEntry = entry;
+ }
+ else
+ {
+ int msgID = MSGID_SDTUACM_MULTIPLE_MATCHING_ENTRIES;
+ String message = getMessage(msgID, peerName,
+ String.valueOf(userEntry.getDN()),
+ String.valueOf(entry.getDN()));
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
+ msgID);
+ }
+ }
+ }
+
+
+ // If we've gotten here, then we either found exactly one user entry or we
+ // didn't find any. Either way, return the entry or null to the caller.
+ return userEntry;
+ }
+
+
+
+ /**
+ * Retrieves the DN of the configuration entry with which this
+ * component is associated.
+ *
+ * @return The DN of the configuration entry with which this
+ * component is associated.
+ */
+ public DN getConfigurableComponentEntryDN()
+ {
+ assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
+
+ return configEntryDN;
+ }
+
+
+
+ /**
+ * Retrieves the set of configuration attributes that are associated
+ * with this configurable component.
+ *
+ * @return The set of configuration attributes that are associated
+ * with this configurable component.
+ */
+ public List<ConfigAttribute> getConfigurationAttributes()
+ {
+ assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
+
+ LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
+
+ int msgID = MSGID_SDTUACM_DESCRIPTION_SUBJECT_ATTR;
+ attrList.add(new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR,
+ getMessage(msgID), true, false, false,
+ subjectAttributeType.getNameOrOID()));
+
+ LinkedList<DN> dnList = new LinkedList<DN>();
+ if (baseDNs != null)
+ {
+ for (DN baseDN : baseDNs)
+ {
+ dnList.add(baseDN);
+ }
+ }
+
+ msgID = MSGID_SDTUACM_DESCRIPTION_BASE_DN;
+ attrList.add(new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false,
+ dnList));
+
+ return attrList;
+ }
+
+
+
+ /**
+ * Indicates whether the provided configuration entry has an
+ * acceptable configuration for this component. If it does not,
+ * then detailed information about the problem(s) should be added to
+ * the provided list.
+ *
+ * @param configEntry The configuration entry for which to
+ * make the determination.
+ * @param unacceptableReasons A list that can be used to hold
+ * messages about why the provided
+ * entry does not have an acceptable
+ * configuration.
+ *
+ * @return <CODE>true</CODE> if the provided entry has an
+ * acceptable configuration for this component, or
+ * <CODE>false</CODE> if not.
+ */
+ public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
+ List<String> unacceptableReasons)
+ {
+ assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
+ String.valueOf(configEntry), "java.util.List<String>");
+
+ DN configEntryDN = configEntry.getDN();
+ boolean configAcceptable = true;
+
+
+ // Get the attribute type that will be used to hold the certificate subject.
+ int msgID = MSGID_SDTUACM_DESCRIPTION_SUBJECT_ATTR;
+ StringConfigAttribute attrStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR,
+ getMessage(msgID), true, false, false);
+ try
+ {
+ StringConfigAttribute attrAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(attrStub);
+ if (attrAttr == null)
+ {
+ msgID = MSGID_SDTUACM_NO_SUBJECT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_SUBJECT_ATTR);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ else
+ {
+ String attrName = attrAttr.pendingValue();
+ String lowerName = toLowerCase(attrName);
+ AttributeType attrType =
+ DirectoryServer.getAttributeType(lowerName, false);
+ if (attrType == null)
+ {
+ msgID = MSGID_SDTUACM_NO_SUCH_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ attrName);
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
+
+ msgID = MSGID_SDTUACM_CANNOT_GET_SUBJECT_ATTR;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ msgID = MSGID_SDTUACM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
+
+ msgID = MSGID_SDTUACM_CANNOT_GET_BASE_DN;
+ String message = getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+
+
+ return configAcceptable;
+ }
+
+
+
+ /**
+ * Makes a best-effort attempt to apply the configuration contained
+ * in the provided entry. Information about the result of this
+ * processing should be added to the provided message list.
+ * Information should always be added to this list if a
+ * configuration change could not be applied. If detailed results
+ * are requested, then information about the changes applied
+ * successfully (and optionally about parameters that were not
+ * changed) should also be included.
+ *
+ * @param configEntry The entry containing the new
+ * configuration to apply for this
+ * component.
+ * @param detailedResults Indicates whether detailed information
+ * about the processing should be added to
+ * the list.
+ *
+ * @return Information about the result of the configuration
+ * update.
+ */
+ public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
+ boolean detailedResults)
+ {
+ assert debugEnter(CLASS_NAME, "applyNewConfiguration",
+ String.valueOf(configEntry),
+ String.valueOf(detailedResults));
+
+ DN configEntryDN = configEntry.getDN();
+ ResultCode resultCode = ResultCode.SUCCESS;
+ ArrayList<String> messages = new ArrayList<String>();
+ boolean adminActionRequired = false;
+
+
+ // Get the attribute type that will be used to hold the certificate subject.
+ AttributeType newAttributeType = null;
+ int msgID = MSGID_SDTUACM_DESCRIPTION_SUBJECT_ATTR;
+ StringConfigAttribute attrStub =
+ new StringConfigAttribute(ATTR_CERTIFICATE_SUBJECT_ATTR,
+ getMessage(msgID), true, false, false);
+ try
+ {
+ StringConfigAttribute attrAttr =
+ (StringConfigAttribute) configEntry.getConfigAttribute(attrStub);
+ if (attrAttr == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.OBJECTCLASS_VIOLATION;
+ }
+
+ msgID = MSGID_SDTUACM_NO_SUBJECT_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ ATTR_CERTIFICATE_SUBJECT_ATTR));
+ }
+ else
+ {
+ String attrName = attrAttr.pendingValue();
+ String lowerName = toLowerCase(attrName);
+ newAttributeType = DirectoryServer.getAttributeType(lowerName, false);
+ if (subjectAttributeType == null)
+ {
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
+ }
+
+ msgID = MSGID_SDTUACM_NO_SUCH_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ attrName));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyNewConfiguration", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.OBJECTCLASS_VIOLATION;
+ }
+
+ msgID = MSGID_SDTUACM_CANNOT_GET_SUBJECT_ATTR;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ // Get the set of base DNs below which to perform the searches.
+ DN[] newBaseDNs = null;
+ msgID = MSGID_SDTUACM_DESCRIPTION_BASE_DN;
+ DNConfigAttribute baseStub =
+ new DNConfigAttribute(ATTR_CERTIFICATE_SUBJECT_BASEDN,
+ getMessage(msgID), false, true, false);
+ try
+ {
+ DNConfigAttribute baseAttr =
+ (DNConfigAttribute) configEntry.getConfigAttribute(baseStub);
+ if (baseAttr != null)
+ {
+ List<DN> dnList = baseAttr.activeValues();
+ newBaseDNs = new DN[dnList.size()];
+ dnList.toArray(newBaseDNs);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyNewConfiguration", e);
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.OBJECTCLASS_VIOLATION;
+ }
+
+ msgID = MSGID_SDTUACM_CANNOT_GET_BASE_DN;
+ messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+ stackTraceToSingleLineString(e)));
+ }
+
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ subjectAttributeType = newAttributeType;
+ baseDNs = newBaseDNs;
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index 909ce32..48e3ffc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4329,6 +4329,381 @@
/**
+ * The message ID for the message that will be used as the description for the
+ * subject attribute type attribute. It does not take any arguments.
+ */
+ public static final int MSGID_SDTUACM_DESCRIPTION_SUBJECT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 411;
+
+
+
+ /**
+ * The message ID for the message that will be used if the configuration entry
+ * does not specify which attribute type should be used to hold certificate
+ * subjects. This takes two arguments, which are the DN of the configuration
+ * entry and the attribute type that should be used to specify the subject
+ * attribute.
+ */
+ public static final int MSGID_SDTUACM_NO_SUBJECT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 412;
+
+
+
+ /**
+ * The message ID for the message that will be used if subject attribute type
+ * does not exist in the server schema. This takes two arguments, which are
+ * the DN of the configuration entry and the name of the specified attribute
+ * type.
+ */
+ public static final int MSGID_SDTUACM_NO_SUCH_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 413;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to determine the subject attribute type. This takes two arguments,
+ * which are the DN of the configuration entry and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_SDTUACM_CANNOT_GET_SUBJECT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 414;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * search base DN attribute. It does not take any arguments.
+ */
+ public static final int MSGID_SDTUACM_DESCRIPTION_BASE_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 415;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to determine the search base DN. This takes two arguments,
+ * which are the DN of the configuration entry and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_SDTUACM_CANNOT_GET_BASE_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 416;
+
+
+
+ /**
+ * The message ID for the message that will be used if the client did not
+ * present any certificate to the server. This does not take any arguments.
+ */
+ public static final int MSGID_SDTUACM_NO_PEER_CERTIFICATE =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 417;
+
+
+
+ /**
+ * The message ID for the message that will be used if the client certificate
+ * was not an X.509 certificate. This takes a single argument, which is the
+ * name of the certificate format.
+ */
+ public static final int MSGID_SDTUACM_PEER_CERT_NOT_X509 =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 418;
+
+
+
+ /**
+ * The message ID for the message that will be used if multiple user entries
+ * matched the specified certificate subject. This takes three arguments,
+ * which are the certificate subject and the DNs of the first two users found
+ * to match that subject.
+ */
+ public static final int MSGID_SDTUACM_MULTIPLE_MATCHING_ENTRIES =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 419;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * attribute map attribute. It does not take any arguments.
+ */
+ public static final int MSGID_SATUACM_DESCRIPTION_ATTR_MAP =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 420;
+
+
+
+ /**
+ * The message ID for the message that will be used if the configuration entry
+ * does not specify which attribute type should be used to map certificate
+ * attributes to user attributes. This takes two arguments, which are the DN
+ * of the configuration entry and the attribute type that should be used to
+ * specify the mapping.
+ */
+ public static final int MSGID_SATUACM_NO_MAP_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 421;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attribute map value
+ * has an invalid format. This takes two arguments, which are the DN of the
+ * configuration entry and the invalid map value.
+ */
+ public static final int MSGID_SATUACM_INVALID_MAP_FORMAT =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 422;
+
+
+
+ /**
+ * The message ID for the message that will be used if there are multiple
+ * mappings that target the same certificate attribute. This takes two
+ * arguments, which are the DN of the configuration entry and the name of the
+ * certificate attribute.
+ */
+ public static final int MSGID_SATUACM_DUPLICATE_CERT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 423;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attribute mapping
+ * references a user attribute that is not defined in the server schema.
+ * This takes two argumetns, which are the DN of the configuration entry and
+ * the name of the undefined user attribute.
+ */
+ public static final int MSGID_SATUACM_NO_SUCH_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 424;
+
+
+
+ /**
+ * The message ID for the message that will be used if there are multiple
+ * mappings that target the same user attribute. This takes two arguments,
+ * which are the DN of the configuration entry and the name of the user
+ * attribute.
+ */
+ public static final int MSGID_SATUACM_DUPLICATE_USER_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 425;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to process the attribute mapping. This takes two arguments,
+ * which are the DN of the configuration entry and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_SATUACM_CANNOT_GET_ATTR_MAP =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 426;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * search base DN attribute. It does not take any arguments.
+ */
+ public static final int MSGID_SATUACM_DESCRIPTION_BASE_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 427;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to process the set of base DNs. This takes two arguments,
+ * which are the DN of the configuration entry and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_SATUACM_CANNOT_GET_BASE_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 428;
+
+
+
+ /**
+ * The message ID for the message that will be used if the client did not
+ * present any certificate to the server. This does not take any arguments.
+ */
+ public static final int MSGID_SATUACM_NO_PEER_CERTIFICATE =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 429;
+
+
+
+ /**
+ * The message ID for the message that will be used if the client certificate
+ * was not an X.509 certificate. This takes a single argument, which is the
+ * name of the certificate format.
+ */
+ public static final int MSGID_SATUACM_PEER_CERT_NOT_X509 =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 430;
+
+
+
+ /**
+ * The message ID for the message that will be used if the peer certificate
+ * subject cannot be decoded as a DN. This takes two arguments, which are
+ * the peer certificate subject and a message explaining the problem that
+ * occurred.
+ */
+ public static final int MSGID_SATUACM_CANNOT_DECODE_SUBJECT_AS_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 431;
+
+
+
+ /**
+ * The message ID for the message that will be used if a peer certificate
+ * subject does not contain any mappable attributes. This takes a single
+ * argument, which is the peer certificate subject.
+ */
+ public static final int MSGID_SATUACM_NO_MAPPABLE_ATTRIBUTES =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 432;
+
+
+
+ /**
+ * The message ID for the message that will be used if multiple user entries
+ * matched the specified certificate subject. This takes three arguments,
+ * which are the certificate subject and the DNs of the first two users found
+ * to match that subject.
+ */
+ public static final int MSGID_SATUACM_MULTIPLE_MATCHING_ENTRIES =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 433;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * fingerprint attribute type attribute. It does not take any arguments.
+ */
+ public static final int MSGID_FCM_DESCRIPTION_FINGERPRINT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 434;
+
+
+
+ /**
+ * The message ID for the message that will be used if the configuration entry
+ * does not specify which attribute type should be used to hold certificate
+ * fingerprints. This takes two arguments, which are the DN of the
+ * configuration entry and the attribute type that should be used to specify
+ * the fingerprint attribute.
+ */
+ public static final int MSGID_FCM_NO_FINGERPRINT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 435;
+
+
+
+ /**
+ * The message ID for the message that will be used if the fingerprint
+ * attribute type does not exist in the server schema. This takes two
+ * arguments, which are the DN of the configuration entry and the name of the
+ * specified attribute type.
+ */
+ public static final int MSGID_FCM_NO_SUCH_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 436;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to determine the fingerprint attribute type. This takes two
+ * arguments, which are the DN of the configuration entry and a string
+ * representation of the exception that was caught.
+ */
+ public static final int MSGID_FCM_CANNOT_GET_FINGERPRINT_ATTR =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 437;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * fingerprint algorithm attribute. It does not take any arguments.
+ */
+ public static final int MSGID_FCM_DESCRIPTION_FINGERPRINT_ALGORITHM =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 438;
+
+
+
+ /**
+ * The message ID for the message that will be used if the configuration entry
+ * does not specify which digest algorithm should be used to compute
+ * fingerprints. This takes two arguments, which are the DN of the
+ * configuration entry and the attribute type that should be used to specify
+ * the fingerprint algorithm.
+ */
+ public static final int MSGID_FCM_NO_FINGERPRINT_ALGORITHM =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 439;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to determine the fingerprint algorithm. This takes two arguments,
+ * which are the DN of the configuration entry and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_FCM_CANNOT_GET_FINGERPRINT_ALGORITHM =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 440;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * search base DN attribute. It does not take any arguments.
+ */
+ public static final int MSGID_FCM_DESCRIPTION_BASE_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 441;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to determine the search base DN. This takes two arguments,
+ * which are the DN of the configuration entry and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_FCM_CANNOT_GET_BASE_DN =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 442;
+
+
+
+ /**
+ * The message ID for the message that will be used if the client did not
+ * present any certificate to the server. This does not take any arguments.
+ */
+ public static final int MSGID_FCM_NO_PEER_CERTIFICATE =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 443;
+
+
+
+ /**
+ * The message ID for the message that will be used if the client certificate
+ * was not an X.509 certificate. This takes a single argument, which is the
+ * name of the certificate format.
+ */
+ public static final int MSGID_FCM_PEER_CERT_NOT_X509 =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 444;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to compute the fingerprint for a certificate. This takes two
+ * arguments, which are the certificate subject and a string representation of
+ * the exception that was caught.
+ */
+ public static final int MSGID_FCM_CANNOT_CALCULATE_FINGERPRINT =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 445;
+
+
+
+ /**
+ * The message ID for the message that will be used if multiple user entries
+ * matched the specified certificate fingerprint. This takes three arguments,
+ * which are the certificate fingerprint and the DNs of the first two users
+ * found to match that fingerprint.
+ */
+ public static final int MSGID_FCM_MULTIPLE_MATCHING_ENTRIES =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 446;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -6266,6 +6641,156 @@
"Cannot remove user %s as a member of static group %s " +
"because an error occurred while attempting to perform " +
"an internal modification to update the group: %s.");
+
+
+ registerMessage(MSGID_SDTUACM_DESCRIPTION_SUBJECT_ATTR,
+ "Specifies the name of the attribute type in user " +
+ "entries that contains the subjects of the certificates " +
+ "held by that user. Changes to this configuration " +
+ "attribute will take effect immediately.");
+ registerMessage(MSGID_SDTUACM_NO_SUBJECT_ATTR,
+ "Configuration entry %s does not contain required " +
+ "attribute %s, which is used to specify which attribute " +
+ "should contain the subjects of the certificates held " +
+ "by users.");
+ registerMessage(MSGID_SDTUACM_NO_SUCH_ATTR,
+ "Configuration entry %s indicates that certificate " +
+ "subjects should be held in attribute %s, but this " +
+ "attribute is not defined in the server schema.");
+ registerMessage(MSGID_SDTUACM_CANNOT_GET_SUBJECT_ATTR,
+ "An error occurred while attempting to determine which " +
+ "attribute type should be used to hold certificate " +
+ "subjects from configuration entry %s: %s.");
+ registerMessage(MSGID_SDTUACM_DESCRIPTION_BASE_DN,
+ "Specifies the base DNs below which the searches to " +
+ "find matching user entries will be performed. If no " +
+ "base DN(s) are provided, then the server will search " +
+ "below all public naming contexts. Changes to this " +
+ "configuration attribute will take effect immediately.");
+ registerMessage(MSGID_SDTUACM_CANNOT_GET_BASE_DN,
+ "An error occurred while attempting to determine the " +
+ "search base DN(s) from configuration entry %s: %s.");
+ registerMessage(MSGID_SDTUACM_NO_PEER_CERTIFICATE,
+ "Could not map the provided certificate chain to a user " +
+ "entry because no peer certificate was available.");
+ registerMessage(MSGID_SDTUACM_PEER_CERT_NOT_X509,
+ "Could not map the provided certificate chain to a user " +
+ "because the peer certificate was not an X.509 " +
+ "certificate (peer certificate format was %s).");
+ registerMessage(MSGID_SDTUACM_MULTIPLE_MATCHING_ENTRIES,
+ "The certificate with subject %s could not be mapped to " +
+ "exactly one user. It maps to both %s and %s.");
+
+
+ registerMessage(MSGID_SATUACM_DESCRIPTION_ATTR_MAP,
+ "Specifies the name of the attribute type in user " +
+ "entries that defines the mapping between attributes " +
+ "in certificate subjects and attributes in user " +
+ "entries. Values should be in the form " +
+ "'certattr:userattr'. Changes to this configuration " +
+ "attribute will take effect immediately.");
+ registerMessage(MSGID_SATUACM_NO_MAP_ATTR,
+ "Configuration entry %s does not contain required " +
+ "attribute %s, which is used to specify the mappings " +
+ "between attributes in certificate subjects and " +
+ "attributes in user entries.");
+ registerMessage(MSGID_SATUACM_INVALID_MAP_FORMAT,
+ "Configuration entry %s has value '%s' which violates " +
+ "the format required for attribute mappings. The " +
+ "expected format is 'certattr:userattr'.");
+ registerMessage(MSGID_SATUACM_DUPLICATE_CERT_ATTR,
+ "Configuration entry %s contains multiple mappings " +
+ "for certificate attribute %s.");
+ registerMessage(MSGID_SATUACM_NO_SUCH_ATTR,
+ "Mapping %s in configuration entry %s references " +
+ "attribute %s which is not defined in the server schema.");
+ registerMessage(MSGID_SATUACM_DUPLICATE_USER_ATTR,
+ "Configuration entry %s contains multiple mappings " +
+ "for user attribute %s.");
+ registerMessage(MSGID_SATUACM_CANNOT_GET_ATTR_MAP,
+ "An error occurred while attempting to determine the set " +
+ "of attribute mappings from configuration entry %s: %s.");
+ registerMessage(MSGID_SATUACM_DESCRIPTION_BASE_DN,
+ "Specifies the base DNs below which the searches to " +
+ "find matching user entries will be performed. If no " +
+ "base DN(s) are provided, then the server will search " +
+ "below all public naming contexts. Changes to this " +
+ "configuration attribute will take effect immediately.");
+ registerMessage(MSGID_SATUACM_CANNOT_GET_BASE_DN,
+ "An error occurred while attempting to determine the " +
+ "search base DN(s) from configuration entry %s: %s.");
+ registerMessage(MSGID_SATUACM_NO_PEER_CERTIFICATE,
+ "Could not map the provided certificate chain to a user " +
+ "entry because no peer certificate was available.");
+ registerMessage(MSGID_SATUACM_PEER_CERT_NOT_X509,
+ "Could not map the provided certificate chain to a user " +
+ "because the peer certificate was not an X.509 " +
+ "certificate (peer certificate format was %s).");
+ registerMessage(MSGID_SATUACM_CANNOT_DECODE_SUBJECT_AS_DN,
+ "Unable to decode peer certificate subject %s as a DN: " +
+ "%s.");
+ registerMessage(MSGID_SATUACM_NO_MAPPABLE_ATTRIBUTES,
+ "Peer certificate subject %s does not contain any " +
+ "attributes for which a mapping has been established.");
+ registerMessage(MSGID_SATUACM_MULTIPLE_MATCHING_ENTRIES,
+ "The certificate with subject %s could not be mapped to " +
+ "exactly one user. It maps to both %s and %s.");
+
+
+ registerMessage(MSGID_FCM_DESCRIPTION_FINGERPRINT_ATTR,
+ "Specifies the name of the attribute type in user " +
+ "entries that contains the fingerprints of the " +
+ "certificates held by that user. Changes to this " +
+ "configuration attribute will take effect immediately.");
+ registerMessage(MSGID_FCM_NO_FINGERPRINT_ATTR,
+ "Configuration entry %s does not contain required " +
+ "attribute %s, which is used to specify which attribute " +
+ "should contain the fingerprints of the certificates " +
+ "held by users.");
+ registerMessage(MSGID_FCM_NO_SUCH_ATTR,
+ "Configuration entry %s indicates that certificate " +
+ "fingerprints should be held in attribute %s, but this " +
+ "attribute is not defined in the server schema.");
+ registerMessage(MSGID_FCM_CANNOT_GET_FINGERPRINT_ATTR,
+ "An error occurred while attempting to determine which " +
+ "attribute type should be used to hold certificate " +
+ "fingerprints from configuration entry %s: %s.");
+ registerMessage(MSGID_FCM_DESCRIPTION_FINGERPRINT_ALGORITHM,
+ "Specifies the name of the digest algorithm used for " +
+ "the certificate fingerprints. The value should be " +
+ "either 'MD5' or 'SHA1'. Changes to this configuration " +
+ "attribute will take effect immediately.");
+ registerMessage(MSGID_FCM_NO_FINGERPRINT_ALGORITHM,
+ "Configuration entry %s does not contain required " +
+ "attribute %s, which is used to specify which digest " +
+ "algorithm should be used to compute certificate " +
+ "fingerprints.");
+ registerMessage(MSGID_FCM_CANNOT_GET_FINGERPRINT_ALGORITHM,
+ "An error occurred while attempting to determine the " +
+ "digest algorithm from configuration entry %s: %s.");
+ registerMessage(MSGID_FCM_DESCRIPTION_BASE_DN,
+ "Specifies the base DNs below which the searches to " +
+ "find matching user entries will be performed. If no " +
+ "base DN(s) are provided, then the server will search " +
+ "below all public naming contexts. Changes to this " +
+ "configuration attribute will take effect immediately.");
+ registerMessage(MSGID_FCM_CANNOT_GET_BASE_DN,
+ "An error occurred while attempting to determine the " +
+ "search base DN(s) from configuration entry %s: %s.");
+ registerMessage(MSGID_FCM_NO_PEER_CERTIFICATE,
+ "Could not map the provided certificate chain to a user " +
+ "entry because no peer certificate was available.");
+ registerMessage(MSGID_FCM_PEER_CERT_NOT_X509,
+ "Could not map the provided certificate chain to a user " +
+ "because the peer certificate was not an X.509 " +
+ "certificate (peer certificate format was %s).");
+ registerMessage(MSGID_FCM_CANNOT_CALCULATE_FINGERPRINT,
+ "An error occurred while attempting to calculate the " +
+ "fingerprint for the peer certificate with subject %s: " +
+ "%s.");
+ registerMessage(MSGID_FCM_MULTIPLE_MATCHING_ENTRIES,
+ "The certificate with fingerprint %s could not be mapped " +
+ "to exactly one user. It maps to both %s and %s.");
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java b/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
index a0b29d2..ace21e6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
@@ -759,6 +759,37 @@
/**
* Retrieves a string representation of the contents of the provided byte
+ * array using hexadecimal characters and a colon between each byte.
+ *
+ * @param b The byte array containing the data.
+ *
+ * @return A string representation of the contents of the provided byte
+ * array using hexadecimal characters.
+ */
+ public static String bytesToColonDelimitedHex(byte[] b)
+ {
+ if ((b == null) || (b.length == 0))
+ {
+ return "";
+ }
+
+ int arrayLength = b.length;
+ StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
+ buffer.append(byteToHex(b[0]));
+
+ for (int i=1; i < arrayLength; i++)
+ {
+ buffer.append(":");
+ buffer.append(byteToHex(b[i]));
+ }
+
+ return buffer.toString();
+ }
+
+
+
+ /**
+ * Retrieves a string representation of the contents of the provided byte
* buffer using hexadecimal characters and a space between each byte.
*
* @param b The byte buffer containing the data.
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FingerprintCertificateMapperTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FingerprintCertificateMapperTestCase.java
new file mode 100644
index 0000000..9f512fe
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/FingerprintCertificateMapperTestCase.java
@@ -0,0 +1,681 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.tools.LDAPSearch;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the fingerprint certificate mapper.
+ */
+public class FingerprintCertificateMapperTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid configurations that cannot be used to
+ * initialize the certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidConfigs")
+ public Object[][] getInvalidConfigurations()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ "dn: cn=No Fingerprint Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-fingerprint-certificate-mapper",
+ "cn: No Fingerprint Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "FingerprintCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-fingerprint-algorithm: MD5",
+ "",
+ "dn: cn=Undefined Fingerprint Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-fingerprint-certificate-mapper",
+ "cn: Undefined Fingerprint Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "FingerprintCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-fingerprint-attribute-type: undefined",
+ "ds-cfg-certificate-fingerprint-algorithm: MD5",
+ "",
+ "dn: cn=No Fingerprint Algorithm,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-fingerprint-certificate-mapper",
+ "cn: No Fingerprint Algorithm",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "FingerprintCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-fingerprint-attribute-type: " +
+ "ds-certificate-fingerprint",
+ "",
+ "dn: cn=Invalid Fingerprint Algorithm,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-fingerprint-certificate-mapper",
+ "cn: Invalid Fingerprint Algorithm",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "FingerprintCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-fingerprint-attribute-type: " +
+ "ds-certificate-fingerprint",
+ "ds-cfg-certificate-fingerprint-algorithm: invalid",
+ "",
+ "dn: cn=Invalid Base DN,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-fingerprint-certificate-mapper",
+ "cn: Invalid Base DN",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "FingerprintCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-fingerprint-attribute-type: " +
+ "ds-certificate-fingerprint",
+ "ds-cfg-certificate-fingerprint-algorithm: MD5",
+ "ds-cfg-certificate-user-base-dn: invalid");
+
+
+ Object[][] configEntries = new Object[entries.size()][1];
+ for (int i=0; i < configEntries.length; i++)
+ {
+ configEntries[i] = new Object[] { entries.get(i) };
+ }
+
+ return configEntries;
+ }
+
+
+
+ /**
+ * Tests initialization with an invalid configuration.
+ *
+ * @param e The configuration entry to use to initialize the certificate
+ * mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidConfigs",
+ expectedExceptions = { ConfigException.class,
+ InitializationException.class })
+ public void testInvalidConfigs(Entry e)
+ throws Exception
+ {
+ DN parentDN = DN.decode("cn=Certificate Mappers,cn=config");
+ ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
+ ConfigEntry configEntry = new ConfigEntry(e, parentEntry);
+
+ FingerprintCertificateMapper mapper = new FingerprintCertificateMapper();
+ mapper.initializeCertificateMapper(configEntry);
+ }
+
+
+
+ /**
+ * Tests a successful mapping using the default configuration.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingDefaultConfig()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "ds-certificate-fingerprint: " +
+ "07:5A:AB:4B:E1:DD:E3:05:83:C0:FE:5F:A3:E8:1E:EB");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a successful mapping using the SHA-1 digest algorithm..
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingSHA1()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setFingerprintAlgorithm("SHA1");
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "ds-certificate-fingerprint: " +
+ "CB:A4:C7:A0:46:1F:44:88:12:23:56:49:F9:54:F4:37:E1:9F:9F:A4");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ setFingerprintAlgorithm("MD5");
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping due to no matching entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingNoMatchingEntries()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping due to multiple matching entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingMultipleMatchingEntries()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user1,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user1",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User 1",
+ "ds-certificate-fingerprint: " +
+ "07:5A:AB:4B:E1:DD:E3:05:83:C0:FE:5F:A3:E8:1E:EB",
+ "",
+ "dn: uid=test.user2,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user2",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User 2",
+ "ds-certificate-fingerprint: " +
+ "07:5A:AB:4B:E1:DD:E3:05:83:C0:FE:5F:A3:E8:1E:EB");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to remove the fingerprint attribute will
+ * fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRemoveFingerprintAttribute()
+ throws Exception
+ {
+ String mapperDN = "cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config";
+
+ Attribute a =
+ new Attribute(DirectoryServer.getAttributeType(
+ "ds-cfg-certificate-fingerprint-attribute-type"));
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.DELETE, a));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertFalse(modifyOperation.getResultCode() == ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to remove the fingerprint algorithm will
+ * fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRemoveFingerprintAlgorithm()
+ throws Exception
+ {
+ String mapperDN = "cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config";
+
+ Attribute a =
+ new Attribute(DirectoryServer.getAttributeType(
+ "ds-cfg-certificate-fingerprint-algorithm"));
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.DELETE, a));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertFalse(modifyOperation.getResultCode() == ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an undefined fingerprint attribute
+ * will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetUndefinedFingerprintAttribute()
+ throws Exception
+ {
+ setFingerprintAttribute("undefined");
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an undefined fingerprint algorithm
+ * will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetUndefinedFingerprintAlgorithm()
+ throws Exception
+ {
+ setFingerprintAlgorithm("undefined");
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an invalid base DN will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetInvalidBaseDN()
+ throws Exception
+ {
+ setBaseDNs(new String[] { "invalid" });
+ }
+
+
+
+ /**
+ * Alters the configuration of the SASL EXTERNAL mechanism handler so that it
+ * uses the Subject DN to User Attribute certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void enableMapper()
+ throws Exception
+ {
+ String externalDN = "cn=EXTERNAL,cn=SASL Mechanisms,cn=config";
+ String mapperDN = "cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-mapper-dn",
+ mapperDN)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(externalDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the SASL EXTERNAL mechanism handler so that it
+ * uses the Subject Equals DN certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void disableMapper()
+ throws Exception
+ {
+ String externalDN = "cn=EXTERNAL,cn=SASL Mechanisms,cn=config";
+ String mapperDN = "cn=Subject Equals DN,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-mapper-dn",
+ mapperDN)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(externalDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the fingerprint certificate mapper so that it
+ * will look for the fingerprint in the specified attribute.
+ *
+ * @param attrName The name of the attribute in which to look for the
+ * certificate subject.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setFingerprintAttribute(String attrName)
+ throws Exception
+ {
+ String mapperDN = "cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-fingerprint-attribute-type",
+ attrName)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the fingerprint certificate mapper so that it
+ * will use the specified fingerprint algorithm.
+ *
+ * @param algorithm The name of the fingerprint algorithm to use.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setFingerprintAlgorithm(String algorithm)
+ throws Exception
+ {
+ String mapperDN = "cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-fingerprint-algorithm",
+ algorithm)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the Subject DN to User Attribute certificate
+ * mapper so that it will look for the subject DN below the specified set of
+ * base DNs.
+ *
+ * @param baseDNs The set of base DNs to use when mapping certificates to
+ * users.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setBaseDNs(String[] baseDNs)
+ throws Exception
+ {
+ String mapperDN = "cn=Fingerprint Mapper,cn=Certificate Mappers,cn=config";
+
+ AttributeType attrType =
+ DirectoryServer.getAttributeType("ds-cfg-certificate-user-base-dn");
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ if (baseDNs != null)
+ {
+ for (String baseDN : baseDNs)
+ {
+ values.add(new AttributeValue(attrType, baseDN));
+ }
+ }
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute(attrType, attrType.getNameOrOID(),
+ values)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapperTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapperTestCase.java
new file mode 100644
index 0000000..5e28262
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapperTestCase.java
@@ -0,0 +1,851 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.tools.LDAPSearch;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the Subject Attribute to User Attribute certificate
+ * mapper.
+ */
+public class SubjectAttributeToUserAttributeCertificateMapperTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid configurations that cannot be used to
+ * initialize the certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidConfigs")
+ public Object[][] getInvalidConfigurations()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ "dn: cn=No Map Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: No Map Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "",
+ "dn: cn=No Map Colon,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: No Map Colon",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: nomapcolon",
+ "",
+ "dn: cn=No Map Cert Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: No Map Cert Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: :cn",
+ "",
+ "dn: cn=No Map User Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: No Map User Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: cn:",
+ "",
+ "dn: cn=Undefined User Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: Undefined User Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: cn:undefined",
+ "",
+ "dn: cn=Duplicate Cert Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: Duplicate Cert Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: cn:cn",
+ "ds-cfg-certificate-subject-attribute-mapping: cn:sn",
+ "",
+ "dn: cn=Duplicate User Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: Duplicate User Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: cn:cn",
+ "ds-cfg-certificate-subject-attribute-mapping: e:cn",
+ "",
+ "dn: cn=Invalid Base DN,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: " +
+ "ds-cfg-subject-attribute-to-user-attribute-certificate-mapper",
+ "cn: Invalid Base DN",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectAttributeToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-mapping: cn:cn",
+ "ds-cfg-certificate-user-base-dn: invalid");
+
+
+ Object[][] configEntries = new Object[entries.size()][1];
+ for (int i=0; i < configEntries.length; i++)
+ {
+ configEntries[i] = new Object[] { entries.get(i) };
+ }
+
+ return configEntries;
+ }
+
+
+
+ /**
+ * Tests initialization with an invalid configuration.
+ *
+ * @param e The configuration entry to use to initialize the certificate
+ * mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidConfigs",
+ expectedExceptions = { ConfigException.class,
+ InitializationException.class })
+ public void testInvalidConfigs(Entry e)
+ throws Exception
+ {
+ DN parentDN = DN.decode("cn=Certificate Mappers,cn=config");
+ ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
+ ConfigEntry configEntry = new ConfigEntry(e, parentEntry);
+
+ SubjectAttributeToUserAttributeCertificateMapper mapper =
+ new SubjectAttributeToUserAttributeCertificateMapper();
+ mapper.initializeCertificateMapper(configEntry);
+ }
+
+
+
+ /**
+ * Tests a successful mapping using the default configuration.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingDefaultConfig()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a successful mapping with multiple attributes.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingMultipleAttributes()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setAttributeMappings(new String[] { "cn:cn", "o:o" });
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "o: test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ setAttributeMappings(new String[] { "cn:cn", "e:mail" });
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping due to no mappable attributes in the certificate.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedNoMappableAttributes()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setAttributeMappings(new String[] { "e:mail" });
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "o: test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, System.err) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ setAttributeMappings(new String[] { "cn:cn", "e:mail" });
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping due to no matching users.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingNoMatchingUsers()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Not Test User");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping due to multiple matching users.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingMultipleMatchingUsers()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user1,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user1",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "",
+ "dn: uid=test.user2,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user2",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping when there are no users below the configured base
+ * DNs that match the criteria.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingNoUserBelowBaseDNs()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setBaseDNs(new String[] { "dc=example,dc=com" });
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ setBaseDNs(null);
+ }
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to remove the subject attribute will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRemoveMapAttribute()
+ throws Exception
+ {
+ String mapperDN = "cn=Subject Attribute to User Attribute," +
+ "cn=Certificate Mappers,cn=config";
+
+ Attribute a =
+ new Attribute(DirectoryServer.getAttributeType(
+ "ds-cfg-certificate-subject-attribute-mapping"));
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.DELETE, a));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertFalse(modifyOperation.getResultCode() == ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an attribute mapping with no colon
+ * will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetMappingNoColon()
+ throws Exception
+ {
+ setAttributeMappings(new String[] { "nocolon" });
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an attribute mapping with no cert
+ * attribute will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetMappingNoCertAttribute()
+ throws Exception
+ {
+ setAttributeMappings(new String[] { ":cn" });
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an attribute mapping with no user
+ * attribute will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetMappingNoUserAttribute()
+ throws Exception
+ {
+ setAttributeMappings(new String[] { "cn:" });
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an attribute mapping with an
+ * undefined user attribute will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetMappingUndefinedUserAttribute()
+ throws Exception
+ {
+ setAttributeMappings(new String[] { "cn:undefined" });
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an attribute mapping with a
+ * duplicate cert attribute mapping will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetMappingDuplicateCertAttribute()
+ throws Exception
+ {
+ setAttributeMappings(new String[] { "cn:cn", "cn:sn" });
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an attribute mapping with a
+ * duplicate user attribute mapping will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetMappingDuplicateUserAttribute()
+ throws Exception
+ {
+ setAttributeMappings(new String[] { "cn:cn", "e:cn" });
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an invalid base DN will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetInvalidBaseDN()
+ throws Exception
+ {
+ setBaseDNs(new String[] { "invalid" });
+ }
+
+
+
+ /**
+ * Alters the configuration of the SASL EXTERNAL mechanism handler so that it
+ * uses the Subject Attribute to User Attribute certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void enableMapper()
+ throws Exception
+ {
+ String externalDN = "cn=EXTERNAL,cn=SASL Mechanisms,cn=config";
+ String mapperDN = "cn=Subject Attribute to User Attribute," +
+ "cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-mapper-dn",
+ mapperDN)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(externalDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the SASL EXTERNAL mechanism handler so that it
+ * uses the Subject Equals DN certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void disableMapper()
+ throws Exception
+ {
+ String externalDN = "cn=EXTERNAL,cn=SASL Mechanisms,cn=config";
+ String mapperDN = "cn=Subject Equals DN,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-mapper-dn",
+ mapperDN)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(externalDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the Subject Attribute to User Attribute
+ * certificate mapper so that it will use the specified set of mappings.
+ *
+ * @param mappings The specified set of mappings to use.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setAttributeMappings(String[] mappings)
+ throws Exception
+ {
+ String mapperDN = "cn=Subject Attribute to User Attribute," +
+ "cn=Certificate Mappers,cn=config";
+
+ AttributeType attrType =
+ DirectoryServer.getAttributeType(
+ "ds-cfg-certificate-subject-attribute-mapping");
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ if (mappings != null)
+ {
+ for (String mapping : mappings)
+ {
+ values.add(new AttributeValue(attrType, mapping));
+ }
+ }
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute(attrType, attrType.getNameOrOID(),
+ values)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the Subject Attribute to User Attribute
+ * certificate mapper so that it will look for matches below the specified set
+ * of base DNs.
+ *
+ * @param baseDNs The set of base DNs to use when mapping certificates to
+ * users.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setBaseDNs(String[] baseDNs)
+ throws Exception
+ {
+ String mapperDN = "cn=Subject Attribute to User Attribute," +
+ "cn=Certificate Mappers,cn=config";
+
+ AttributeType attrType =
+ DirectoryServer.getAttributeType("ds-cfg-certificate-user-base-dn");
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ if (baseDNs != null)
+ {
+ for (String baseDN : baseDNs)
+ {
+ values.add(new AttributeValue(attrType, baseDN));
+ }
+ }
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute(attrType, attrType.getNameOrOID(),
+ values)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapperTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapperTestCase.java
new file mode 100644
index 0000000..f682cc3
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapperTestCase.java
@@ -0,0 +1,710 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.tools.LDAPSearch;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the Subject DN to User Attribute certificate mapper.
+ */
+public class SubjectDNToUserAttributeCertificateMapperTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid configurations that cannot be used to
+ * initialize the certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidConfigs")
+ public Object[][] getInvalidConfigurations()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ "dn: cn=No Subject Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-subject-dn-to-user-attribute-certificate-mapper",
+ "cn: No Subject Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectDNToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "",
+ "dn: cn=Undefined Subject Attr,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-subject-dn-to-user-attribute-certificate-mapper",
+ "cn: Undefined Subject Attr",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectDNToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-type: undefined",
+ "",
+ "dn: cn=Invalid Base DN,cn=Certificate Mappers,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-certificate-mapper",
+ "objectClass: ds-cfg-subject-dn-to-user-attribute-certificate-mapper",
+ "cn: Invalid Base DN",
+ "ds-cfg-certificate-mapper-class: org.opends.server.extensions." +
+ "SubjectDNToUserAttributeCertificateMapper",
+ "ds-cfg-certificate-mapper-enabled: true",
+ "ds-cfg-certificate-subject-attribute-type: ds-certificate-subject-dn",
+ "ds-cfg-certificate-user-base-dn: invalid");
+
+
+ Object[][] configEntries = new Object[entries.size()][1];
+ for (int i=0; i < configEntries.length; i++)
+ {
+ configEntries[i] = new Object[] { entries.get(i) };
+ }
+
+ return configEntries;
+ }
+
+
+
+ /**
+ * Tests initialization with an invalid configuration.
+ *
+ * @param e The configuration entry to use to initialize the certificate
+ * mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidConfigs",
+ expectedExceptions = { ConfigException.class,
+ InitializationException.class })
+ public void testInvalidConfigs(Entry e)
+ throws Exception
+ {
+ DN parentDN = DN.decode("cn=Certificate Mappers,cn=config");
+ ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
+ ConfigEntry configEntry = new ConfigEntry(e, parentEntry);
+
+ SubjectDNToUserAttributeCertificateMapper mapper =
+ new SubjectDNToUserAttributeCertificateMapper();
+ mapper.initializeCertificateMapper(configEntry);
+ }
+
+
+
+ /**
+ * Tests a successful mapping using the default configuration.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingDefaultConfig()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "ds-certificate-subject-dn: CN=Test User, O=Test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a successful mapping using a configuration with a different subject
+ * attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingAlternateSubjectAttribute()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setSubjectAttribute("manager");
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "manager: CN=Test User, O=Test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ setSubjectAttribute("ds-certificate-subject-dn");
+ }
+ }
+
+
+
+ /**
+ * Tests a successful mapping using a configuration with a different set of
+ * base DNs.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSuccessfulMappingAlternateBaseDNs()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setBaseDNs(new String[] { "o=test" });
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "ds-certificate-subject-dn: CN=Test User, O=Test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+ }
+ finally
+ {
+ disableMapper();
+ setSubjectAttribute("ds-certificate-subject-dn");
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping when there are no users that should match.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingNoUsers()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntry(
+ "dn: cn=Test User,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping when there are multiple users that match the
+ * critieria.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingMultipleUsers()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user1,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user1",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User 1",
+ "ds-certificate-subject-dn: CN=Test User, O=Test",
+ "",
+ "dn: uid=test.user2,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user2",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User 2",
+ "ds-certificate-subject-dn: CN=Test User, O=Test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ }
+ }
+
+
+
+ /**
+ * Tests a failed mapping when there are no users below the configured base
+ * DNs that match the criteria.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testFailedMappingNoUserBelowBaseDNs()
+ throws Exception
+ {
+ enableMapper();
+
+ try
+ {
+ setBaseDNs(new String[] { "dc=example,dc=com" });
+
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "objectClass: ds-certificate-user",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "ds-certificate-subject-dn: CN=Test User, O=Test");
+
+
+
+ String keyStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.keystore";
+ String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+ "config" + File.separator + "client.truststore";
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+ "-Z",
+ "-K", keyStorePath,
+ "-W", "password",
+ "-P", trustStorePath,
+ "-r",
+ "-b", "",
+ "-s", "base",
+ "(objectClass=*)"
+ };
+
+ assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+ }
+ finally
+ {
+ disableMapper();
+ setBaseDNs(null);
+ }
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to remove the subject attribute will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRemoveSubjectAttribute()
+ throws Exception
+ {
+ String mapperDN =
+ "cn=Subject DN to User Attribute,cn=Certificate Mappers,cn=config";
+
+ Attribute a =
+ new Attribute(DirectoryServer.getAttributeType(
+ "ds-cfg-certificate-subject-attribute-type"));
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.DELETE, a));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertFalse(modifyOperation.getResultCode() == ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an undefined subject attribute will
+ * fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetUndefinedSubjectAttribute()
+ throws Exception
+ {
+ setSubjectAttribute("undefined");
+ }
+
+
+
+ /**
+ * Tests to ensure that an attmept to set an invalid base DN will fail.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { AssertionError.class })
+ public void testSetInvalidBaseDN()
+ throws Exception
+ {
+ setBaseDNs(new String[] { "invalid" });
+ }
+
+
+
+ /**
+ * Alters the configuration of the SASL EXTERNAL mechanism handler so that it
+ * uses the Subject DN to User Attribute certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void enableMapper()
+ throws Exception
+ {
+ String externalDN = "cn=EXTERNAL,cn=SASL Mechanisms,cn=config";
+ String mapperDN =
+ "cn=Subject DN to User Attribute,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-mapper-dn",
+ mapperDN)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(externalDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the SASL EXTERNAL mechanism handler so that it
+ * uses the Subject Equals DN certificate mapper.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void disableMapper()
+ throws Exception
+ {
+ String externalDN = "cn=EXTERNAL,cn=SASL Mechanisms,cn=config";
+ String mapperDN = "cn=Subject Equals DN,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-mapper-dn",
+ mapperDN)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(externalDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the Subject DN to User Attribute certificate
+ * mapper so that it will look for the subject DN in the specified attribute.
+ *
+ * @param attrName The name of the attribute in which to look for the
+ * certificate subject.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setSubjectAttribute(String attrName)
+ throws Exception
+ {
+ String mapperDN =
+ "cn=Subject DN to User Attribute,cn=Certificate Mappers,cn=config";
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-certificate-subject-attribute-type",
+ attrName)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Alters the configuration of the Subject DN to User Attribute certificate
+ * mapper so that it will look for the subject DN below the specified set of
+ * base DNs.
+ *
+ * @param baseDNs The set of base DNs to use when mapping certificates to
+ * users.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void setBaseDNs(String[] baseDNs)
+ throws Exception
+ {
+ String mapperDN =
+ "cn=Subject DN to User Attribute,cn=Certificate Mappers,cn=config";
+
+ AttributeType attrType =
+ DirectoryServer.getAttributeType("ds-cfg-certificate-user-base-dn");
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ if (baseDNs != null)
+ {
+ for (String baseDN : baseDNs)
+ {
+ values.add(new AttributeValue(attrType, baseDN));
+ }
+ }
+
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute(attrType, attrType.getNameOrOID(),
+ values)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(DN.decode(mapperDN), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+}
+
--
Gitblit v1.10.0