All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.extensions.PasswordQualityRequirement Maven / Gradle / Ivy

/*
 * Copyright 2015-2019 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright (C) 2015-2019 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.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import com.unboundid.asn1.ASN1Element;
import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.asn1.ASN1Sequence;
import com.unboundid.asn1.ASN1Set;
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.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.Validator;

import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;



/**
 * This class provides a data structure that describes a requirement that
 * passwords must satisfy in order to be accepted by the server.
 * 
*
* 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. *
*
* A password quality requirement will always include a description, which * should be a string that provides a user-friendly description of the * constraints that a proposed password must satisfy in order to meet this * requirement and be accepted by the server. It may optionally include * additional information that could allow an application to attempt some kind * of pre-validation in order to determine whether a proposed password might * fall outside the constraints associated with this requirement and would * therefore be rejected by the server. This could allow a client to provide * better performance (by not having to submit a password to the server and wait * for the response in order to detect certain kinds of problems) and a better * user experience (for example, by interactively indicating whether the value * is acceptable as the user is entering it). *

* If a password quality requirement object does provide client-side validation * data, then it will include at least a validation type (which indicates the * nature of the validation that will be performed), and an optional set of * properties that provide additional information about the specific nature of * the validation. For example, if the server is configured with a length-based * password validator that requires passwords to be between eight and 20 * characters, then the requirement may have a validation type of "length" and * two validation properties: "minimum-length" with a value of "8" and * "maximum-length" with a value of "20". An application that supports this * type of client-side validation could prevent a user from supplying a password * that is too short or too long without the need to communicate with the * server. *

* Note that not all types of password requirements will support client-side * validation. For example, the server may be configured to use a dictionary * with some of the most commonly-used passwords in an attempt to prevent * users from selecting passwords that may be easily guessed, or the server * may be configured with a password history to prevent users from selecting a * password that they had already used. In these kinds of cases, the * application will not have access to the information necessary to make the * determination using client-side logic. The server is the ultimate authority * as to whether a proposed password will be accepted, and even applications * should be prepared to handle the case in which a password is rejected by the * server even if client-side validation does not indicate that there are any * problems with the password. There may also be cases in which the reason that * an attempt to set a password fails for a reason that is not related to the * quality of the provided password. *

* However, even in cases where an application may not be able to perform any * client-side validation, the server may still offer a client-side validation * type and validation properties. This is not intended to help the client * determine whether a proposed password is acceptable, but could allow the * client to convey information about the requirement to the user in a more * flexible manner than simply providing the requirement description (e.g., it * could allow the client to provide information about the requirement to the * user in a different language than the server-provided description, or it * could allow information about one requirement to be split into multiple * elements, or multiple requirements combined into a single element. *

* If it appears in an LDAP protocol element (e.g., a get password quality * requirements extended response, or a password validation details response * control), it should have the following ASN.1 encoding: *
 *   PasswordQualityRequirement ::= SEQUENCE {
 *        description                  OCTET STRING,
 *        clientSideValidationInfo     [0] SEQUENCE {
 *             validationType     OCTET STRING,
 *             properties         [0] SET OF SEQUENCE {
 *                  name      OCTET STRING,
 *                  value     OCTET STRING } OPTIONAL } OPTIONAL }
 * 
*/ @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class PasswordQualityRequirement implements Serializable { /** * The BER type that will be used for the optional client-side validation info * element of an encoded password quality requirement. */ private static final byte TYPE_CLIENT_SIDE_VALIDATION_INFO = (byte) 0xA1; /** * The BER type that will be used for the optional validation properties * element of an encoded client-side validation info element. */ private static final byte TYPE_CLIENT_SIDE_VALIDATION_PROPERTIES = (byte) 0xA1; /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = 2956655422853571644L; // A set of properties that may be used to indicate constraints that the // server will impose when validating the password in accordance with this // requirement. private final Map clientSideValidationProperties; // The name of the client-side validation type for this requirement, if any. private final String clientSideValidationType; // A user-friendly description of the constraints that proposed passwords must // satisfy in order to be accepted by the server. private final String description; /** * Creates a new password quality requirement object without any support for * client-side validation. * * @param description A user-friendly description of the constraints that a * proposed password must satisfy in order to meet this * requirement and be accepted by the server. This must * not be {@code null}. */ public PasswordQualityRequirement(final String description) { this(description, null, null); } /** * Creates a new password quality requirement object with optional support for * client-side validation. * * @param description A user-friendly description of the * constraints that a proposed * password must satisfy in order to * meet this requirement and be * accepted by the server. This must * not be {@code null}. * @param clientSideValidationType An optional string that identifies * the type of validation associated * with this requirement. * Applications that support * client-side validation and * recognize this validation type can * attempt to use their own logic in * attempt to determine whether a * proposed password may be rejected * by the server because it does not * satisfy this requirement. This may * be {@code null} if no client-side * validation is available for this * requirement. * @param clientSideValidationProperties An optional map of property names * and values that may provide * additional information that can be * used for client-side validation. * The properties that may be included * depend on the validation type. * This must be empty or {@code null} * if the provided validation type is * {@code null}. It may also be empty * or {@code null} if no additional * properties are required for the * associated type of client-side * validation. */ public PasswordQualityRequirement(final String description, final String clientSideValidationType, final Map clientSideValidationProperties) { Validator.ensureNotNull(description); if (clientSideValidationType == null) { Validator.ensureTrue((clientSideValidationProperties == null) || clientSideValidationProperties.isEmpty()); } this.description = description; this.clientSideValidationType = clientSideValidationType; if (clientSideValidationProperties == null) { this.clientSideValidationProperties = Collections.emptyMap(); } else { this.clientSideValidationProperties = Collections.unmodifiableMap( new LinkedHashMap<>(clientSideValidationProperties)); } } /** * Retrieves a user-friendly description of the constraints that a proposed * password must satisfy in order to meet this requirement and be accepted * by the server. * * @return A user-friendly description for this password quality requirement. */ public String getDescription() { return description; } /** * Retrieves a string that identifies the type of client-side validation that * may be performed by applications in order to identify potential problems * with a proposed password before sending it to the server. Client-side * validation may not be available for all types of password quality * requirements. * * @return The client side validation type for this password quality * requirement, or {@code null} if client-side validation is not * supported for this password quality requirement. */ public String getClientSideValidationType() { return clientSideValidationType; } /** * Retrieves a set of properties that may be used in the course of performing * client-side validation for a proposed password. The types of properties * that may be included depend on the client-side validation type. * * @return A map of properties that may be used in the course of performing * client-side validation, or an empty map if client-side validation * is not available for this password quality requirement, or if no * additional properties required for the associated type of * client-side validation. */ public Map getClientSideValidationProperties() { return clientSideValidationProperties; } /** * Encodes this password quality requirement to an ASN.1 element that may be * included in LDAP protocol elements that may need to include it (e.g., a * get password quality requirements extended response or a password * validation details response control). * * @return An ASN.1-encoded representation of this password quality * requirement. */ public ASN1Element encode() { final ArrayList requirementElements = new ArrayList<>(2); requirementElements.add(new ASN1OctetString(description)); if (clientSideValidationType != null) { final ArrayList clientSideElements = new ArrayList<>(2); clientSideElements.add(new ASN1OctetString(clientSideValidationType)); if (! clientSideValidationProperties.isEmpty()) { final ArrayList propertyElements = new ArrayList<>(clientSideValidationProperties.size()); for (final Map.Entry e : clientSideValidationProperties.entrySet()) { propertyElements.add(new ASN1Sequence( new ASN1OctetString(e.getKey()), new ASN1OctetString(e.getValue()))); } clientSideElements.add(new ASN1Set( TYPE_CLIENT_SIDE_VALIDATION_PROPERTIES, propertyElements)); } requirementElements.add(new ASN1Sequence(TYPE_CLIENT_SIDE_VALIDATION_INFO, clientSideElements)); } return new ASN1Sequence(requirementElements); } /** * Decodes the provided ASN.1 element as a password quality requirement. * * @param element The ASN.1 element to decode as a password quality * requirement. It must not be {@code null}. * * @return The decoded password quality requirement. * * @throws LDAPException If a problem was encountered while attempting to * decode the provided ASN.1 element as a password * quality requirement. */ public static PasswordQualityRequirement decode(final ASN1Element element) throws LDAPException { try { final ASN1Element[] requirementElements = ASN1Sequence.decodeAsSequence(element).elements(); final String description = ASN1OctetString.decodeAsOctetString( requirementElements[0]).stringValue(); String clientSideValidationType = null; Map clientSideValidationProperties = null; for (int i=1; i < requirementElements.length; i++) { final ASN1Element requirementElement = requirementElements[i]; switch (requirementElement.getType()) { case TYPE_CLIENT_SIDE_VALIDATION_INFO: final ASN1Element[] csvInfoElements = ASN1Sequence.decodeAsSequence(requirementElement).elements(); clientSideValidationType = ASN1OctetString.decodeAsOctetString( csvInfoElements[0]).stringValue(); for (int j=1; j < csvInfoElements.length; j++) { final ASN1Element csvInfoElement = csvInfoElements[j]; switch (csvInfoElement.getType()) { case TYPE_CLIENT_SIDE_VALIDATION_PROPERTIES: final ASN1Element[] csvPropElements = ASN1Sequence.decodeAsSequence(csvInfoElement).elements(); clientSideValidationProperties = new LinkedHashMap<>( StaticUtils.computeMapCapacity(csvPropElements.length)); for (final ASN1Element csvPropElement : csvPropElements) { final ASN1Element[] propElements = ASN1Sequence.decodeAsSequence( csvPropElement).elements(); final String name = ASN1OctetString.decodeAsOctetString( propElements[0]).stringValue(); final String value = ASN1OctetString.decodeAsOctetString( propElements[1]).stringValue(); clientSideValidationProperties.put(name, value); } break; default: throw new LDAPException(ResultCode.DECODING_ERROR, ERR_PW_QUALITY_REQ_INVALID_CSV_ELEMENT_TYPE.get( StaticUtils.toHex(csvInfoElement.getType()))); } } break; default: throw new LDAPException(ResultCode.DECODING_ERROR, ERR_PW_QUALITY_REQ_INVALID_REQ_ELEMENT_TYPE.get( StaticUtils.toHex(requirementElement.getType()))); } } return new PasswordQualityRequirement(description, clientSideValidationType, clientSideValidationProperties); } catch (final LDAPException le) { Debug.debugException(le); throw le; } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_PW_QUALITY_REQ_DECODE_ERROR.get( StaticUtils.getExceptionMessage(e)), e); } } /** * Retrieves a string representation of this password quality requirement. * * @return A string representation of this password quality requirement. */ @Override() public String toString() { final StringBuilder buffer = new StringBuilder(); toString(buffer); return buffer.toString(); } /** * Appends a string representation of this password quality requirement to the * provided buffer. * * @param buffer The buffer to which the information should be appended. */ public void toString(final StringBuilder buffer) { buffer.append("PasswordQualityRequirement(description='"); buffer.append(description); buffer.append('\''); if (clientSideValidationType != null) { buffer.append(", clientSideValidationType='"); buffer.append(clientSideValidationType); buffer.append('\''); if (! clientSideValidationProperties.isEmpty()) { buffer.append(", clientSideValidationProperties={"); final Iterator> iterator = clientSideValidationProperties.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); buffer.append('\''); buffer.append(e.getKey()); buffer.append("'='"); buffer.append(e.getValue()); buffer.append('\''); if (iterator.hasNext()) { buffer.append(','); } } buffer.append('}'); } } buffer.append(')'); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy