001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2007-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.extensions;
037
038
039import com.unboundid.asn1.ASN1Element;
040import com.unboundid.asn1.ASN1OctetString;
041import com.unboundid.asn1.ASN1Sequence;
042import com.unboundid.ldap.sdk.Control;
043import com.unboundid.ldap.sdk.ExtendedResult;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.util.Debug;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.NotNull;
049import com.unboundid.util.Nullable;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052
053import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
054
055
056
057/**
058 * This class implements a data structure for storing the information from an
059 * extended result for the password modify extended request as defined in
060 * <A HREF="http://www.ietf.org/rfc/rfc3062.txt">RFC 3062</A>.  It is identical
061 * to the standard {@link ExtendedResult} object except that it is also able to
062 * extract the generated password if one was included.  See the documentation
063 * for the {@link PasswordModifyExtendedRequest} class for an example of this.
064 */
065@NotMutable()
066@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
067public final class PasswordModifyExtendedResult
068       extends ExtendedResult
069{
070  /**
071   * The serial version UID for this serializable class.
072   */
073  private static final long serialVersionUID = -160274020063799410L;
074
075
076
077  // The generated password from the response, if applicable.
078  @Nullable private final ASN1OctetString generatedPassword;
079
080
081
082  /**
083   * Creates a new password modify extended result from the provided extended
084   * result.
085   *
086   * @param  extendedResult  The extended result to be decoded as a password
087   *                         modify extended result.  It must not be
088   *                         {@code null}.
089   *
090   * @throws  LDAPException  If the provided extended result cannot be decoded
091   *                         as a password modify extended result.
092   */
093  public PasswordModifyExtendedResult(
094              @NotNull final ExtendedResult extendedResult)
095         throws LDAPException
096  {
097    super(extendedResult);
098
099    final ASN1OctetString value = extendedResult.getValue();
100    if (value == null)
101    {
102      generatedPassword = null;
103      return;
104    }
105
106    final ASN1Element[] elements;
107    try
108    {
109      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
110      elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
111    }
112    catch (final Exception e)
113    {
114      Debug.debugException(e);
115      throw new LDAPException(ResultCode.DECODING_ERROR,
116                              ERR_PW_MODIFY_RESPONSE_VALUE_NOT_SEQUENCE.get(e),
117                              e);
118    }
119
120    if (elements.length == 0)
121    {
122      generatedPassword = null;
123      return;
124    }
125    else if (elements.length != 1)
126    {
127      throw new LDAPException(ResultCode.DECODING_ERROR,
128                              ERR_PW_MODIFY_RESPONSE_MULTIPLE_ELEMENTS.get());
129    }
130
131    generatedPassword = ASN1OctetString.decodeAsOctetString(elements[0]);
132  }
133
134
135
136  /**
137   * Creates a new password modify extended result with the provided
138   * information.
139   *
140   * @param  messageID          The message ID for the LDAP message that is
141   *                            associated with this LDAP result.
142   * @param  resultCode         The result code from the response.
143   * @param  diagnosticMessage  The diagnostic message from the response, if
144   *                            available.
145   * @param  matchedDN          The matched DN from the response, if available.
146   * @param  referralURLs       The set of referral URLs from the response, if
147   *                            available.
148   * @param  generatedPassword  The generated password for this response, if
149   *                            available.
150   * @param  responseControls   The set of controls from the response, if
151   *                            available.
152   */
153  public PasswordModifyExtendedResult(final int messageID,
154              @NotNull final ResultCode resultCode,
155              @Nullable final String diagnosticMessage,
156              @Nullable final String matchedDN,
157              @Nullable final String[] referralURLs,
158              @Nullable final ASN1OctetString generatedPassword,
159              @Nullable final Control[] responseControls)
160  {
161    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
162          null, encodeValue(generatedPassword), responseControls);
163
164    this.generatedPassword = generatedPassword;
165  }
166
167
168
169  /**
170   * Encodes the value for this extended result using the provided information.
171   *
172   * @param  generatedPassword  The generated password for this response, if
173   *                            available.
174   *
175   * @return  An ASN.1 octet string containing the encoded value, or
176   *          {@code null} if there should not be an encoded value.
177   */
178  @Nullable()
179  private static ASN1OctetString encodeValue(
180                      @Nullable final ASN1OctetString generatedPassword)
181  {
182    if (generatedPassword == null)
183    {
184      return null;
185    }
186
187    final ASN1Element[] elements =
188    {
189      new ASN1OctetString((byte) 0x80, generatedPassword.getValue())
190    };
191
192    return new ASN1OctetString(new ASN1Sequence(elements).encode());
193  }
194
195
196
197  /**
198   * Retrieves the string representation of the generated password contained in
199   * this extended result, if available.
200   *
201   * @return  The string representation of the generated password contained in
202   *          this extended result, or {@code null} if no generated password was
203   *          included in the extended result.
204   */
205  @Nullable()
206  public String getGeneratedPassword()
207  {
208    if (generatedPassword == null)
209    {
210      return null;
211    }
212    else
213    {
214      return generatedPassword.stringValue();
215    }
216  }
217
218
219
220  /**
221   * Retrieves the binary representation of the generated password contained in
222   * this extended result, if available.
223   *
224   * @return  The binary representation of the generated password contained in
225   *          this extended result, or {@code null} if no generated password was
226   *          included in the extended result.
227   */
228  @Nullable()
229  public byte[] getGeneratedPasswordBytes()
230  {
231    if (generatedPassword == null)
232    {
233      return null;
234    }
235    else
236    {
237      return generatedPassword.getValue();
238    }
239  }
240
241
242
243  /**
244   * Retrieves the raw generated password contained in this extended result, if
245   * available.
246   *
247   * @return  The raw generated password contained in this extended result, or
248   *          {@code null} if no generated password was included in the extended
249   *          result.
250   */
251  @Nullable()
252  public ASN1OctetString getRawGeneratedPassword()
253  {
254    return generatedPassword;
255  }
256
257
258
259  /**
260   * {@inheritDoc}
261   */
262  @Override()
263  @NotNull()
264  public String getExtendedResultName()
265  {
266    return INFO_EXTENDED_RESULT_NAME_PASSWORD_MODIFY.get();
267  }
268
269
270
271  /**
272   * Appends a string representation of this extended result to the provided
273   * buffer.
274   *
275   * @param  buffer  The buffer to which a string representation of this
276   *                 extended result will be appended.
277   */
278  @Override()
279  public void toString(@NotNull final StringBuilder buffer)
280  {
281    buffer.append("PasswordModifyExtendedResult(resultCode=");
282    buffer.append(getResultCode());
283
284    final int messageID = getMessageID();
285    if (messageID >= 0)
286    {
287      buffer.append(", messageID=");
288      buffer.append(messageID);
289    }
290
291    if (generatedPassword != null)
292    {
293      buffer.append(", generatedPassword='*****REDACTED*****'");
294    }
295
296    final String diagnosticMessage = getDiagnosticMessage();
297    if (diagnosticMessage != null)
298    {
299      buffer.append(", diagnosticMessage='");
300      buffer.append(diagnosticMessage);
301      buffer.append('\'');
302    }
303
304    final String matchedDN = getMatchedDN();
305    if (matchedDN != null)
306    {
307      buffer.append(", matchedDN='");
308      buffer.append(matchedDN);
309      buffer.append('\'');
310    }
311
312    final String[] referralURLs = getReferralURLs();
313    if (referralURLs.length > 0)
314    {
315      buffer.append(", referralURLs={");
316      for (int i=0; i < referralURLs.length; i++)
317      {
318        if (i > 0)
319        {
320          buffer.append(", ");
321        }
322
323        buffer.append('\'');
324        buffer.append(referralURLs[i]);
325        buffer.append('\'');
326      }
327      buffer.append('}');
328    }
329
330    final Control[] responseControls = getResponseControls();
331    if (responseControls.length > 0)
332    {
333      buffer.append(", responseControls={");
334      for (int i=0; i < responseControls.length; i++)
335      {
336        if (i > 0)
337        {
338          buffer.append(", ");
339        }
340
341        buffer.append(responseControls[i]);
342      }
343      buffer.append('}');
344    }
345
346    buffer.append(')');
347  }
348}