001/*
002 * Copyright 2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.extensions;
022
023
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.List;
029
030import com.unboundid.asn1.ASN1Boolean;
031import com.unboundid.asn1.ASN1Element;
032import com.unboundid.asn1.ASN1OctetString;
033import com.unboundid.asn1.ASN1Sequence;
034import com.unboundid.ldap.sdk.LDAPException;
035import com.unboundid.ldap.sdk.ResultCode;
036import com.unboundid.util.Debug;
037import com.unboundid.util.NotMutable;
038import com.unboundid.util.StaticUtils;
039import com.unboundid.util.ThreadSafety;
040import com.unboundid.util.ThreadSafetyLevel;
041import com.unboundid.util.Validator;
042
043import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
044
045
046
047/**
048 * This class defines a data structure that holds information about a password
049 * generated by the server and returned to the client in a
050 * {@link GeneratePasswordExtendedResult}.
051 * <BR>
052 * <BLOCKQUOTE>
053 *   <B>NOTE:</B>  This class, and other classes within the
054 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
055 *   supported for use against Ping Identity, UnboundID, and
056 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
057 *   for proprietary functionality or for external specifications that are not
058 *   considered stable or mature enough to be guaranteed to work in an
059 *   interoperable way with other types of LDAP servers.
060 * </BLOCKQUOTE>
061 */
062@NotMutable()
063@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064public final class GeneratedPassword
065       implements Serializable
066{
067  /**
068   * The BER type for the element that provides a list of validation errors for
069   * the generated password.
070   */
071  private static final byte TYPE_VALIDATION_ERRORS = (byte) 0xA0;
072
073
074
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = -240847847799966594L;
079
080
081
082  // The password that was generated.
083  private final ASN1OctetString password;
084
085  // Indicates whether the server attempted to perform any validation on the
086  // provided password.
087  private final boolean validationAttempted;
088
089  // A list of messages with information about any problems identified while the
090  // server was validating the quality of the generated password.
091  private final List<String> validationErrors;
092
093
094
095  /**
096   * Creates a generated password object with the provided information.
097   *
098   * @param  password             The password that was generated.  It must not
099   *                              be @code null} or empty.
100   * @param  validationAttempted  Indicates whether the server attempted to
101   *                              validate the quality of the generated
102   *                              password.
103   * @param  validationErrors     An optional list of messages with information
104   *                              about any problems identified while the
105   *                              server was validating the quality of the
106   *                              generated password.
107   */
108  public GeneratedPassword(final String password,
109                           final boolean validationAttempted,
110                           final List<String> validationErrors)
111  {
112    this(new ASN1OctetString(password), validationAttempted, validationErrors);
113  }
114
115
116
117  /**
118   * Creates a generated password object with the provided information.
119   *
120   * @param  password             The password that was generated.  It must not
121   *                              be @code null} or empty.
122   * @param  validationAttempted  Indicates whether the server attempted to
123   *                              validate the quality of the generated
124   *                              password.
125   * @param  validationErrors     An optional list of messages with information
126   *                              about any problems identified while the
127   *                              server was validating the quality of the
128   *                              generated password.
129   */
130  public GeneratedPassword(final byte[] password,
131                           final boolean validationAttempted,
132                           final List<String> validationErrors)
133  {
134    this(new ASN1OctetString(password), validationAttempted, validationErrors);
135  }
136
137
138
139  /**
140   * Creates a generated password object with the provided information.
141   *
142   * @param  password             The password that was generated.  It must not
143   *                              be @code null} or empty.
144   * @param  validationAttempted  Indicates whether the server attempted to
145   *                              validate the quality of the generated
146   *                              password.
147   * @param  validationErrors     An optional list of messages with information
148   *                              about any problems identified while the
149   *                              server was validating the quality of the
150   *                              generated password.
151   */
152  private GeneratedPassword(final ASN1OctetString password,
153                            final boolean validationAttempted,
154                            final List<String> validationErrors)
155  {
156    Validator.ensureTrue(
157         ((password != null) && (password.getValueLength() > 0)),
158         "GeneratedPassword.password must not be null or empty.");
159
160    this.password = password;
161    this.validationAttempted = validationAttempted;
162
163    if (validationErrors == null)
164    {
165      this.validationErrors = Collections.emptyList();
166    }
167    else
168    {
169      this.validationErrors = Collections.unmodifiableList(
170           new ArrayList<>(validationErrors));
171    }
172  }
173
174
175
176  /**
177   * Retrieves a string representation of the server-generated password.
178   *
179   * @return  A string representation of the server-generated password.
180   */
181  public String getPasswordString()
182  {
183    return password.stringValue();
184  }
185
186
187
188  /**
189   * Retrieves the bytes that comprise the server-generated password.
190   *
191   * @return  The bytes that comprise the server-generated password.
192   */
193  public byte[] getPasswordBytes()
194  {
195    return password.getValue();
196  }
197
198
199
200  /**
201   * Indicates whether the server attempted to validate the quality of the
202   * generated password.
203   *
204   * @return  {@code true} if the server attempted to validate the quality of
205   *          the generated password, or {@code false} if not.
206   */
207  public boolean validationAttempted()
208  {
209    return validationAttempted;
210  }
211
212
213
214  /**
215   * Retrieves a list of problems identified while the server was validating the
216   * quality of the generated password.
217   *
218   * @return  A list of problems identified while the server was validating the
219   *          quality of the generated password, or an empty list if no
220   *          validation was attempted or if the generated password satisfied
221   *          all of the requirements for all of the appropriate password
222   *          validators.
223   */
224  public List<String> getValidationErrors()
225  {
226    return validationErrors;
227  }
228
229
230
231  /**
232   * Encodes this generated password to a sequence suitable for inclusion in the
233   * value of a {@link GeneratePasswordExtendedResult}.
234   *
235   * @return  An ASN.1 sequence containing an encoded representation of this
236   *          generated password object.
237   */
238  public ASN1Sequence encode()
239  {
240    final List<ASN1Element> elements = new ArrayList<>(3);
241    elements.add(password);
242    elements.add(new ASN1Boolean(validationAttempted));
243
244    if (! validationErrors.isEmpty())
245    {
246      final List<ASN1Element> validationErrorElements =
247           new ArrayList<>(validationErrors.size());
248      for (final String error : validationErrors)
249      {
250        validationErrorElements.add(new ASN1OctetString(error));
251      }
252
253      elements.add(new ASN1Sequence(TYPE_VALIDATION_ERRORS,
254           validationErrorElements));
255    }
256
257    return new ASN1Sequence(elements);
258  }
259
260
261
262  /**
263   * Decodes the provided ASN.1 element as a generated password object.
264   *
265   * @param  element  The ASN.1 element to be decoded.  It must not be
266   *                  {@code null}.
267   *
268   * @return  The generated password object that was decoded.
269   *
270   * @throws  LDAPException  If a problem is encountered while decoding the
271   *                         provided element as a generated password.
272   */
273  public static GeneratedPassword decode(final ASN1Element element)
274         throws LDAPException
275  {
276    try
277    {
278      final ASN1Element[] elements =
279           ASN1Sequence.decodeAsSequence(element).elements();
280      final ASN1OctetString password = elements[0].decodeAsOctetString();
281      final boolean validationAttempted =
282           elements[1].decodeAsBoolean().booleanValue();
283
284      final List<String> validationErrors = new ArrayList<>(5);
285      for (int i=2; i < elements.length; i++)
286      {
287        if (elements[i].getType() == TYPE_VALIDATION_ERRORS)
288        {
289          for (final ASN1Element errorElement :
290               elements[i].decodeAsSequence().elements())
291          {
292            validationErrors.add(
293                 errorElement.decodeAsOctetString().stringValue());
294          }
295        }
296      }
297
298      return new GeneratedPassword(password, validationAttempted,
299           validationErrors);
300    }
301    catch (final Exception e)
302    {
303      Debug.debugException(e);
304      throw new LDAPException(ResultCode.DECODING_ERROR,
305           ERR_GENERATED_PASSWORD_DECODING_ERROR.get(
306                StaticUtils.getExceptionMessage(e)),
307           e);
308    }
309  }
310
311
312
313  /**
314   * Retrieves a string representation of this generated password object.
315   *
316   * @return  A string representation of this generated password object.
317   */
318  @Override()
319  public String toString()
320  {
321    final StringBuilder buffer = new StringBuilder();
322    toString(buffer);
323    return buffer.toString();
324  }
325
326
327
328  /**
329   * Appends a string representation of this generated password object to the
330   * provided buffer.
331   *
332   * @param  buffer  The buffer to which the information should be appended.
333   */
334  public void toString(final StringBuilder buffer)
335  {
336    buffer.append("GeneratedPassword(passwordLength=");
337    buffer.append(password.getValueLength());
338    buffer.append(", validationAttempted=");
339    buffer.append(validationAttempted);
340
341    if (! validationErrors.isEmpty())
342    {
343      buffer.append(", validationErrors={");
344      buffer.append('}');
345    }
346
347    buffer.append(')');
348  }
349}