From 858ec6c82055b255338871aaa40c69c26913657c Mon Sep 17 00:00:00 2001 From: Neil Wilson Date: Sun, 28 Apr 2024 14:20:54 -0500 Subject: [PATCH] Add a verify password extended request Added client-side support for a new verify password extended request that may be used in the Ping Identity Directory Server to determine whether a provided password is correct for a given user without performing any other password policy processing. This extended operation may only be used under a limited set of circumstances. --- docs/ldap-oid-reference.html | 5 + docs/release-notes.html | 9 + ...boundid-ldapsdk-unboundid-extop.properties | 13 + resource/oid-registry.json | 1 + .../ldap/listener/InMemoryRequestHandler.java | 2 + .../VerifyPasswordExtendedRequest.java | 354 ++++++++++++++++++ ...VerifyPasswordExtendedRequestTestCase.java | 314 ++++++++++++++++ 7 files changed, 698 insertions(+) create mode 100644 src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequest.java create mode 100644 tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequestTestCase.java diff --git a/docs/ldap-oid-reference.html b/docs/ldap-oid-reference.html index a5e38e585..e41c3fc91 100644 --- a/docs/ldap-oid-reference.html +++ b/docs/ldap-oid-reference.html @@ -3540,6 +3540,11 @@

LDAP OID Reference

Purge Retired Inter-Server Certificates Extended Operation Ping Identity Directory Server + + 1.3.6.1.4.1.30221.2.6.72 + Verify Password Extended Request + Ping Identity Directory Server + 1.3.6.1.4.1.30221.2.10.3.1 access-control-disabled Administrative Alert diff --git a/docs/release-notes.html b/docs/release-notes.html index 0b20d8899..03c619e40 100644 --- a/docs/release-notes.html +++ b/docs/release-notes.html @@ -69,6 +69,15 @@

Version 7.0.1



+
  • + Added client-side support for a new verify password extended request that may be + used in the Ping Identity Directory Server to determine whether a provided + password is correct for a given user without performing any other password policy + processing. This extended operation may only be used under a limited set of + circumstances. +

    +
  • +
  • Updated the OID registry to include records for a number of collation matching rules. diff --git a/messages/unboundid-ldapsdk-unboundid-extop.properties b/messages/unboundid-ldapsdk-unboundid-extop.properties index a3e52c12e..68e322624 100644 --- a/messages/unboundid-ldapsdk-unboundid-extop.properties +++ b/messages/unboundid-ldapsdk-unboundid-extop.properties @@ -932,3 +932,16 @@ ERR_TB_DECODE_UNRECOGNIZED_TYPE=Unable to decode an encoded trust behavior \ ERR_REPLACE_CERT_RESULT_CANNOT_DECODE_VALUE=An error occurred while \ attempting to decode the value of the replace certificate extended result: \ {0} +ERR_VERIFY_PASSWORD_REQUEST_NO_VALUE=Unable to decode the provided extended \ + request as a verify password request because the provided request does not \ + have a value. +ERR_VERIFY_PASSWORD_REQUEST_CANNOT_DECODE_VALUE=Unable to decode the provided \ + extended request as a verify password request because an error occurred \ + while attempting to decode the value for the request. +ERR_VERIFY_PASSWORD_REQUEST_MISSING_FIELD=Unable to decode the provided \ + extended request as a verify password request because the request value \ + is missing the required ''{0}'' field. +ERR_VERIFY_PASSWORD_REQUEST_EMPTY_FIELD=Unable to decode the provided \ + extended request as a verify password request because the request value \ + has an empty ''{0}'' field. +INFO_EXTENDED_REQUEST_NAME_VERIFY_PASSWORD=Verify Password Extended Request diff --git a/resource/oid-registry.json b/resource/oid-registry.json index bd50b20eb..7e8652b7a 100644 --- a/resource/oid-registry.json +++ b/resource/oid-registry.json @@ -702,6 +702,7 @@ { "oid":"1.3.6.1.4.1.30221.2.6.69", "name":"Replace Inter-Server Certificate", "type":"Extended Operation", "origin":"Ping Identity Directory Server" } { "oid":"1.3.6.1.4.1.30221.2.6.70", "name":"Purge Retired Listener Certificates", "type":"Extended Operation", "origin":"Ping Identity Directory Server" } { "oid":"1.3.6.1.4.1.30221.2.6.71", "name":"Purge Retired Inter-Server Certificates", "type":"Extended Operation", "origin":"Ping Identity Directory Server" } +{ "oid":"1.3.6.1.4.1.30221.2.6.72", "name":"Verify Password", "type":"Extended Request", "origin":"Ping Identity Directory Server" } { "oid":"1.3.6.1.4.1.30221.2.10.3.1", "name":"access-control-disabled", "type":"Administrative Alert", "origin":"Ping Identity Directory Server" } { "oid":"1.3.6.1.4.1.30221.2.10.3.2", "name":"access-control-enabled", "type":"Administrative Alert", "origin":"Ping Identity Directory Server" } { "oid":"1.3.6.1.4.1.30221.2.10.3.3", "name":"access-control-parse-failure", "type":"Administrative Alert", "origin":"Ping Identity Directory Server" } diff --git a/src/com/unboundid/ldap/listener/InMemoryRequestHandler.java b/src/com/unboundid/ldap/listener/InMemoryRequestHandler.java index cd5401789..603ee4aca 100644 --- a/src/com/unboundid/ldap/listener/InMemoryRequestHandler.java +++ b/src/com/unboundid/ldap/listener/InMemoryRequestHandler.java @@ -2984,6 +2984,8 @@ public LDAPMessage processModifyDNRequest(final int messageID, try (WriteLock writeLock = readWriteLock.lockWrite()) { + writeLock.avoidCompilerWarning(); + // Process the provided request controls. final Map controlMap; try diff --git a/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequest.java b/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequest.java new file mode 100644 index 000000000..bc88ec82e --- /dev/null +++ b/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequest.java @@ -0,0 +1,354 @@ +/* + * Copyright 2024 Ping Identity Corporation + * All Rights Reserved. + */ +/* + * Copyright 2024 Ping Identity Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2024 Ping Identity Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPLv2 only) + * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +package com.unboundid.ldap.sdk.unboundidds.extensions; + + + +import com.unboundid.asn1.ASN1OctetString; +import com.unboundid.ldap.sdk.Control; +import com.unboundid.ldap.sdk.ExtendedRequest; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.ResultCode; +import com.unboundid.util.Debug; +import com.unboundid.util.NotMutable; +import com.unboundid.util.NotNull; +import com.unboundid.util.Nullable; +import com.unboundid.util.ThreadSafety; +import com.unboundid.util.ThreadSafetyLevel; +import com.unboundid.util.Validator; +import com.unboundid.util.json.JSONField; +import com.unboundid.util.json.JSONObject; + +import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; + + + +/** + * This class provides an implementation of an extended request that may be sent + * to the Ping Identity Directory Server to determine whether a provided + * password is correct for a user without performing any other password policy + * processing for that user. The server will not make any attempt to determine + * whether the target user's account is in a usable state, nor will it update + * the user's password policy state information in any way as a result of the + * verification attempt. + *
    + *
    + * NOTE: This class, and other classes within the + * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only + * supported for use against Ping Identity, UnboundID, and + * Nokia/Alcatel-Lucent 8661 server products. These classes provide support + * for proprietary functionality or for external specifications that are not + * considered stable or mature enough to be guaranteed to work in an + * interoperable way with other types of LDAP servers. + *
    + *
    + * The extended request has an OID of 1.3.6.1.4.1.30221.2.6.72. The request must + * have a value, which will be encoded as a JSON object with the following + * fields: + *
      + *
    • + * {@code dn} -- The DN of the user for whom to make the determination. + * This field is required to be present. + *
    • + *
    • + * {@code password} -- The password to verify for the user. This field is + * required to be present. + *
    + *
    + * For security purposes, the server will only allow this request to be issued + * by a client with the necessary access control permission to do so, and who + * also has the {@code permit-verify-password-operation} privilege. + */ +@NotMutable() +@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) +public final class VerifyPasswordExtendedRequest + extends ExtendedRequest +{ + /** + * The OID (1.3.6.1.4.1.30221.2.6.72) for the verify password extended + * request. + */ + @NotNull public static final String VERIFY_PASSWORD_REQUEST_OID = + "1.3.6.1.4.1.30221.2.6.72"; + + + + /** + * The name of the JSON field used to specify the DN of the user for whom + * to make the determination. + */ + @NotNull public static final String REQUEST_FIELD_DN = "dn"; + + + + /** + * The name of the JSON field used to specify the password for which to make + * the determination. + */ + @NotNull public static final String REQUEST_FIELD_PASSWORD = "password"; + + + + /** + * The serial version UID for this serializable class. + */ + private static final long serialVersionUID = 4632159563446607461L; + + + + // The DN of the user for whom to make the determination. + @NotNull private final String dn; + + // The password of the user for whom to make the determination. + @NotNull private final String password; + + + + /** + * Creates a new verify password extended request with the provided + * information. + * + * @param dn The DN of the user for whom to make the determination. + * It must not be {@code null} or empty. + * @param password The password for which to make the determination. It + * must not be {@code null} or empty. + * @param controls An optional set of controls to include in the extended + * request. It may be {@code null} or empty if no controls + * are needed. + */ + public VerifyPasswordExtendedRequest(@NotNull final String dn, + @NotNull final String password, + @Nullable final Control... controls) + { + super(VERIFY_PASSWORD_REQUEST_OID, encodeValue(dn, password), controls); + + this.dn = dn; + this.password = password; + } + + + + /** + * Encodes the provided information into a form sufficient for use as the + * value of this extended request. + * + * @param dn The DN of the user for whom to make the determination. + * It must not be {@code null} or empty. + * @param password The password for which to make the determination. It + * must not be {@code null} or empty. + * + * @return An ASN.1 octet string containing the encoded value. + */ + @NotNull() + private static ASN1OctetString encodeValue(@NotNull final String dn, + @NotNull final String password) + { + Validator.ensureNotNullOrEmpty(dn, + "VerifyPasswordExtendedRequest.dn must not be null or empty"); + Validator.ensureNotNullOrEmpty(password, + "VerifyPasswordExtendedRequest.password must not be null or empty"); + + final JSONObject requestObject = new JSONObject( + new JSONField(REQUEST_FIELD_DN, dn), + new JSONField(REQUEST_FIELD_PASSWORD, password)); + + return new ASN1OctetString(requestObject.toSingleLineString()); + } + + + + /** + * Attempts to decode the provided generic extended request as a verify + * password extended request. + * + * @param extendedRequest The generic extended request to decode as a verify + * password request. It must not be {@code null}. + * + * @throws LDAPException If the provided request cannot be decoded as a + * verify password request. + */ + public VerifyPasswordExtendedRequest( + @NotNull final ExtendedRequest extendedRequest) + throws LDAPException + { + super(extendedRequest); + + final ASN1OctetString value = extendedRequest.getValue(); + if (value == null) + { + throw new LDAPException(ResultCode.DECODING_ERROR, + ERR_VERIFY_PASSWORD_REQUEST_NO_VALUE.get()); + } + + final JSONObject requestObject; + try + { + requestObject = new JSONObject(value.stringValue()); + } + catch (final Exception e) + { + Debug.debugException(e); + + throw new LDAPException(ResultCode.DECODING_ERROR, + ERR_VERIFY_PASSWORD_REQUEST_CANNOT_DECODE_VALUE.get()); + } + + dn = requestObject.getFieldAsString(REQUEST_FIELD_DN); + if (dn == null) + { + throw new LDAPException(ResultCode.DECODING_ERROR, + ERR_VERIFY_PASSWORD_REQUEST_MISSING_FIELD.get(REQUEST_FIELD_DN)); + } + else if (dn.isEmpty()) + { + throw new LDAPException(ResultCode.DECODING_ERROR, + ERR_VERIFY_PASSWORD_REQUEST_EMPTY_FIELD.get(REQUEST_FIELD_DN)); + } + + password = requestObject.getFieldAsString(REQUEST_FIELD_PASSWORD); + if (password == null) + { + throw new LDAPException(ResultCode.DECODING_ERROR, + ERR_VERIFY_PASSWORD_REQUEST_MISSING_FIELD.get( + REQUEST_FIELD_PASSWORD)); + } + else if (password.isEmpty()) + { + throw new LDAPException(ResultCode.DECODING_ERROR, + ERR_VERIFY_PASSWORD_REQUEST_EMPTY_FIELD.get(REQUEST_FIELD_PASSWORD)); + } + } + + + + /** + * Retrieves the DN of the user for whom to verify the password. + * + * @return The DN of the user for whom to verify the password. + */ + @NotNull() + public String getDN() + { + return dn; + } + + + + /** + * Retrieves the password to attempt to verify for the user. + * + * @return The password to attempt to verify for the user. + */ + @NotNull() + public String getPassword() + { + return password; + } + + + + /** + * {@inheritDoc} + */ + @Override() + @NotNull() + public VerifyPasswordExtendedRequest duplicate() + { + return duplicate(getControls()); + } + + + + /** + * {@inheritDoc} + */ + @Override() + @NotNull() + public VerifyPasswordExtendedRequest duplicate( + @Nullable final Control[] controls) + { + final VerifyPasswordExtendedRequest r = + new VerifyPasswordExtendedRequest(dn, password, controls); + r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); + r.setIntermediateResponseListener(getIntermediateResponseListener()); + r.setReferralDepth(getReferralDepth()); + r.setReferralConnector(getReferralConnectorInternal()); + return r; + } + + + + /** + * {@inheritDoc} + */ + @Override() + @NotNull() + public String getExtendedRequestName() + { + return INFO_EXTENDED_REQUEST_NAME_VERIFY_PASSWORD.get(); + } + + + + /** + * {@inheritDoc} + */ + @Override() + public void toString(@NotNull final StringBuilder buffer) + { + buffer.append("VerifyPasswordExtendedRequest(dn='"); + buffer.append(dn); + buffer.append('\''); + + final Control[] controls = getControls(); + if (controls.length > 0) + { + buffer.append(", controls={"); + for (int i=0; i < controls.length; i++) + { + if (i > 0) + { + buffer.append(", "); + } + + buffer.append(controls[i]); + } + buffer.append('}'); + } + + buffer.append(')'); + } +} diff --git a/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequestTestCase.java b/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequestTestCase.java new file mode 100644 index 000000000..3bc6cb70a --- /dev/null +++ b/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/extensions/VerifyPasswordExtendedRequestTestCase.java @@ -0,0 +1,314 @@ +/* + * Copyright 2024 Ping Identity Corporation + * All Rights Reserved. + */ +/* + * Copyright 2024 Ping Identity Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2024 Ping Identity Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPLv2 only) + * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +package com.unboundid.ldap.sdk.unboundidds.extensions; + + + +import java.util.UUID; + +import org.testng.annotations.Test; + +import com.unboundid.asn1.ASN1OctetString; +import com.unboundid.ldap.sdk.Control; +import com.unboundid.ldap.sdk.ExtendedRequest; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.LDAPSDKTestCase; +import com.unboundid.ldap.sdk.ResultCode; +import com.unboundid.util.json.JSONField; +import com.unboundid.util.json.JSONObject; + + + +/** + * This class provides a set of unit tests for the verify password extended + * request. + */ +public final class VerifyPasswordExtendedRequestTestCase + extends LDAPSDKTestCase +{ + /** + * Tests a valid instance of the request without any controls. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testValidRequestWithoutControls() + throws Exception + { + final String uid = UUID.randomUUID().toString(); + final String dn = "uid=" + uid + ",ou=People,dc=example,dc=com"; + + final String password = UUID.randomUUID().toString(); + + VerifyPasswordExtendedRequest r = + new VerifyPasswordExtendedRequest(dn, password); + r = new VerifyPasswordExtendedRequest(r); + r = r.duplicate(); + + assertNotNull(r.getOID()); + assertEquals(r.getOID(), "1.3.6.1.4.1.30221.2.6.72"); + + assertNotNull(r.getValue()); + + final JSONObject valueObject = new JSONObject(r.getValue().stringValue()); + assertNotNull(valueObject); + assertEquals(valueObject, + new JSONObject( + new JSONField("dn", dn), + new JSONField("password", password))); + + assertNotNull(r.getControls()); + assertEquals(r.getControls().length, 0); + + assertNotNull(r.getDN()); + assertEquals(r.getDN(), dn); + + assertNotNull(r.getPassword()); + assertEquals(r.getPassword(), password); + + assertNotNull(r.getExtendedRequestName()); + + assertNotNull(r.toString()); + } + + + + /** + * Tests a valid instance of the request that includes controls. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testValidRequestWithControls() + throws Exception + { + final String uid = UUID.randomUUID().toString(); + final String dn = "uid=" + uid + ",ou=People,dc=example,dc=com"; + + final String password = UUID.randomUUID().toString(); + + VerifyPasswordExtendedRequest r = + new VerifyPasswordExtendedRequest(dn, password, + new Control("1.2.3.4"), + new Control("1.2.3.5", true, new ASN1OctetString("foo"))); + r = new VerifyPasswordExtendedRequest(r); + r = r.duplicate(); + + assertNotNull(r.getOID()); + assertEquals(r.getOID(), "1.3.6.1.4.1.30221.2.6.72"); + + assertNotNull(r.getValue()); + + final JSONObject valueObject = new JSONObject(r.getValue().stringValue()); + assertNotNull(valueObject); + assertEquals(valueObject, + new JSONObject( + new JSONField("dn", dn), + new JSONField("password", password))); + + assertNotNull(r.getControls()); + assertEquals(r.getControls().length, 2); + assertNotNull(r.getControl("1.2.3.4")); + assertNotNull(r.getControl("1.2.3.5")); + + assertNotNull(r.getDN()); + assertEquals(r.getDN(), dn); + + assertNotNull(r.getPassword()); + assertEquals(r.getPassword(), password); + + assertNotNull(r.getExtendedRequestName()); + + assertNotNull(r.toString()); + } + + + + /** + * Tests the behavior when attempting to decode a generic extended request + * that does not have a value. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testDecodeRequestWithoutValue() + throws Exception + { + try + { + new VerifyPasswordExtendedRequest( + new ExtendedRequest("1.3.6.1.4.1.30221.2.6.72")); + } + catch (final LDAPException e) + { + assertEquals(e.getResultCode(), ResultCode.DECODING_ERROR); + } + } + + + + /** + * Tests the behavior when attempting to decode a generic extended request + * that does not have a value. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testDecodeRequestValueNotJSON() + throws Exception + { + try + { + new VerifyPasswordExtendedRequest( + new ExtendedRequest("1.3.6.1.4.1.30221.2.6.72", + new ASN1OctetString("this-is-not-a-json-object"))); + } + catch (final LDAPException e) + { + assertEquals(e.getResultCode(), ResultCode.DECODING_ERROR); + } + } + + + + /** + * Tests the behavior when attempting to decode a generic extended request + * whose value is a JSON object that is missing the required "dn" field. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testDecodeRequestValueMissingDN() + throws Exception + { + try + { + new VerifyPasswordExtendedRequest( + new ExtendedRequest("1.3.6.1.4.1.30221.2.6.72", + new ASN1OctetString( + new JSONObject( + new JSONField("password", + "the-password")).toSingleLineString()))); + } + catch (final LDAPException e) + { + assertEquals(e.getResultCode(), ResultCode.DECODING_ERROR); + } + } + + + + /** + * Tests the behavior when attempting to decode a generic extended request + * whose value is a JSON object that is has an empty "dn" field. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testDecodeRequestValueEmptyDN() + throws Exception + { + try + { + new VerifyPasswordExtendedRequest( + new ExtendedRequest("1.3.6.1.4.1.30221.2.6.72", + new ASN1OctetString( + new JSONObject( + new JSONField("dn", ""), + new JSONField("password", + "the-password")).toSingleLineString()))); + } + catch (final LDAPException e) + { + assertEquals(e.getResultCode(), ResultCode.DECODING_ERROR); + } + } + + + + /** + * Tests the behavior when attempting to decode a generic extended request + * whose value is a JSON object that is missing the required "password" field. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testDecodeRequestValueMissingPassword() + throws Exception + { + try + { + new VerifyPasswordExtendedRequest( + new ExtendedRequest("1.3.6.1.4.1.30221.2.6.72", + new ASN1OctetString( + new JSONObject( + new JSONField("dn", + "uid=x")).toSingleLineString()))); + } + catch (final LDAPException e) + { + assertEquals(e.getResultCode(), ResultCode.DECODING_ERROR); + } + } + + + + /** + * Tests the behavior when attempting to decode a generic extended request + * whose value is a JSON object that has an empty "password" field. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testDecodeRequestValueEmptyPassword() + throws Exception + { + try + { + new VerifyPasswordExtendedRequest( + new ExtendedRequest("1.3.6.1.4.1.30221.2.6.72", + new ASN1OctetString( + new JSONObject( + new JSONField("dn", "uid=x"), + new JSONField("password", + "")).toSingleLineString()))); + } + catch (final LDAPException e) + { + assertEquals(e.getResultCode(), ResultCode.DECODING_ERROR); + } + } +}