001/*
002 * Copyright 2008-2017 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2017 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.text.ParseException;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.Date;
029import java.util.Iterator;
030import java.util.LinkedHashMap;
031import java.util.Map;
032import java.util.NoSuchElementException;
033
034import com.unboundid.asn1.ASN1Element;
035import com.unboundid.asn1.ASN1OctetString;
036import com.unboundid.asn1.ASN1Sequence;
037import com.unboundid.ldap.sdk.Control;
038import com.unboundid.ldap.sdk.ExtendedResult;
039import com.unboundid.ldap.sdk.LDAPException;
040import com.unboundid.ldap.sdk.ResultCode;
041import com.unboundid.util.NotMutable;
042import com.unboundid.util.ThreadSafety;
043import com.unboundid.util.ThreadSafetyLevel;
044
045import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
046import static com.unboundid.util.Debug.*;
047
048
049
050/**
051 * This class implements a data structure for storing the information from an
052 * extended result for the password policy state extended request as used in the
053 * Ping Identity, UnboundID, or Alcatel-Lucent 8661 Directory Server.  It is
054 * able to decode a generic extended result to obtain the user DN and
055 * operations.  See the documentation in the
056 * {@link PasswordPolicyStateExtendedRequest} class for an example that
057 * demonstrates the use of the password policy state extended operation.
058 * <BR>
059 * <BLOCKQUOTE>
060 *   <B>NOTE:</B>  This class, and other classes within the
061 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
062 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
063 *   server products.  These classes provide support for proprietary
064 *   functionality or for external specifications that are not considered stable
065 *   or mature enough to be guaranteed to work in an interoperable way with
066 *   other types of LDAP servers.
067 * </BLOCKQUOTE>
068 */
069@NotMutable()
070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
071public final class PasswordPolicyStateExtendedResult
072       extends ExtendedResult
073{
074  /**
075   * The serial version UID for this serializable class.
076   */
077  private static final long serialVersionUID = 7140468768443263344L;
078
079
080
081  // A map containing all of the response operations, indexed by operation type.
082  private final Map<Integer,PasswordPolicyStateOperation> operations;
083
084  // The user DN from the response.
085  private final String userDN;
086
087
088
089  /**
090   * Creates a new password policy state extended result from the provided
091   * extended result.
092   *
093   * @param  extendedResult  The extended result to be decoded as a password
094   *                         policy state extended result.  It must not be
095   *                         {@code null}.
096   *
097   * @throws  LDAPException  If the provided extended result cannot be decoded
098   *                         as a password policy state extended result.
099   */
100  public PasswordPolicyStateExtendedResult(final ExtendedResult extendedResult)
101         throws LDAPException
102  {
103    super(extendedResult);
104
105    final ASN1OctetString value = extendedResult.getValue();
106    if (value == null)
107    {
108      userDN = null;
109      operations = Collections.emptyMap();
110      return;
111    }
112
113    final ASN1Element[] elements;
114    try
115    {
116      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
117      elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
118    }
119    catch (final Exception e)
120    {
121      debugException(e);
122      throw new LDAPException(ResultCode.DECODING_ERROR,
123                              ERR_PWP_STATE_RESPONSE_VALUE_NOT_SEQUENCE.get(e),
124                              e);
125    }
126
127    if ((elements.length < 1) || (elements.length > 2))
128    {
129      throw new LDAPException(ResultCode.DECODING_ERROR,
130                              ERR_PWP_STATE_RESPONSE_INVALID_ELEMENT_COUNT.get(
131                                   elements.length));
132    }
133
134    userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
135
136    final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops =
137         new LinkedHashMap<Integer,PasswordPolicyStateOperation>();
138    if (elements.length == 2)
139    {
140      try
141      {
142        final ASN1Element[] opElements =
143             ASN1Sequence.decodeAsSequence(elements[1]).elements();
144        for (final ASN1Element e : opElements)
145        {
146          final PasswordPolicyStateOperation op =
147               PasswordPolicyStateOperation.decode(e);
148          ops.put(op.getOperationType(), op);
149        }
150      }
151      catch (final Exception e)
152      {
153        debugException(e);
154        throw new LDAPException(ResultCode.DECODING_ERROR,
155                                ERR_PWP_STATE_RESPONSE_CANNOT_DECODE_OPS.get(e),
156                                e);
157      }
158    }
159
160    operations = Collections.unmodifiableMap(ops);
161  }
162
163
164
165  /**
166   * Creates a new password policy state extended result with the provided
167   * information.
168   * @param  messageID          The message ID for the LDAP message that is
169   *                            associated with this LDAP result.
170   * @param  resultCode         The result code from the response.
171   * @param  diagnosticMessage  The diagnostic message from the response, if
172   *                            available.
173   * @param  matchedDN          The matched DN from the response, if available.
174   * @param  referralURLs       The set of referral URLs from the response, if
175   *                            available.
176   * @param  userDN             The user DN from the response.
177   * @param  operations         The set of operations from the response, mapped
178   *                            from operation type to the corresponding
179   *                            operation data.
180   * @param  responseControls   The set of controls from the response, if
181   *                            available.
182   */
183  public PasswordPolicyStateExtendedResult(final int messageID,
184              final ResultCode resultCode, final String diagnosticMessage,
185              final String matchedDN, final String[] referralURLs,
186              final String userDN,
187              final PasswordPolicyStateOperation[] operations,
188              final Control[] responseControls)
189  {
190    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
191          null, encodeValue(userDN, operations), responseControls);
192
193    this.userDN = userDN;
194
195    if ((operations == null) || (operations.length == 0))
196    {
197      this.operations = Collections.emptyMap();
198    }
199    else
200    {
201      final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops =
202           new LinkedHashMap<Integer,PasswordPolicyStateOperation>(
203                    operations.length);
204      for (final PasswordPolicyStateOperation o : operations)
205      {
206        ops.put(o.getOperationType(), o);
207      }
208      this.operations = Collections.unmodifiableMap(ops);
209    }
210  }
211
212
213
214  /**
215   * Encodes the provided information into a suitable value for this control.
216   *
217   * @param  userDN             The user DN from the response.
218   * @param  operations         The set of operations from the response, mapped
219   *                            from operation type to the corresponding
220   *                            operation data.
221   *
222   * @return  An ASN.1 octet string containing the appropriately-encoded value
223   *          for this control, or {@code null} if there should not be a value.
224   */
225  private static ASN1OctetString encodeValue(final String userDN,
226       final PasswordPolicyStateOperation[] operations)
227  {
228    if ((userDN == null) && ((operations == null) || (operations.length == 0)))
229    {
230      return null;
231    }
232
233    final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
234    elements.add(new ASN1OctetString(userDN));
235
236    if ((operations != null) && (operations.length > 0))
237    {
238      final ASN1Element[] opElements = new ASN1Element[operations.length];
239      for (int i=0; i < operations.length; i++)
240      {
241        opElements[i] = operations[i].encode();
242      }
243
244      elements.add(new ASN1Sequence(opElements));
245    }
246
247    return new ASN1OctetString(new ASN1Sequence(elements).encode());
248  }
249
250
251
252
253  /**
254   * Retrieves the user DN included in the response.
255   *
256   * @return  The user DN included in the response, or {@code null} if the user
257   *          DN is not available (e.g., if this is an error response).
258   */
259  public String getUserDN()
260  {
261    return userDN;
262  }
263
264
265
266  /**
267   * Retrieves the set of password policy operations included in the response.
268   *
269   * @return  The set of password policy operations included in the response.
270   */
271  public Iterable<PasswordPolicyStateOperation> getOperations()
272  {
273    return operations.values();
274  }
275
276
277
278  /**
279   * Retrieves the specified password policy state operation from the response.
280   *
281   * @param  opType  The operation type for the password policy state operation
282   *                 to retrieve.
283   *
284   * @return  The requested password policy state operation, or {@code null} if
285   *          no such operation was included in the response.
286   */
287  public PasswordPolicyStateOperation getOperation(final int opType)
288  {
289    return operations.get(opType);
290  }
291
292
293
294  /**
295   * Retrieves the value for the specified password policy state operation as a
296   * string.
297   *
298   * @param  opType  The operation type for the password policy state operation
299   *                 to retrieve.
300   *
301   * @return  The string value of the requested password policy state operation,
302   *          or {@code null} if the specified operation was not included in the
303   *          response or did not have any values.
304   */
305  public String getStringValue(final int opType)
306  {
307    final PasswordPolicyStateOperation op = operations.get(opType);
308    if (op == null)
309    {
310      return null;
311    }
312
313    return op.getStringValue();
314  }
315
316
317
318  /**
319   * Retrieves the set of string values for the specified password policy state
320   * operation.
321   *
322   * @param  opType  The operation type for the password policy state operation
323   *                 to retrieve.
324   *
325   * @return  The set of string values for the requested password policy state
326   *          operation, or {@code null} if the specified operation was not
327   *          included in the response.
328   */
329  public String[] getStringValues(final int opType)
330  {
331    final PasswordPolicyStateOperation op = operations.get(opType);
332    if (op == null)
333    {
334      return null;
335    }
336
337    return op.getStringValues();
338  }
339
340
341
342  /**
343   * Retrieves the value of the specified password policy state operation as a
344   * boolean.
345   *
346   * @param  opType  The operation type for the password policy state operation
347   *                 to retrieve.
348   *
349   * @return  The boolean value of the requested password policy state
350   *          operation.
351   *
352   * @throws  NoSuchElementException  If the specified operation was not
353   *                                  included in the response.
354   *
355   * @throws  IllegalStateException  If the specified password policy state
356   *                                 operation does not have exactly one value,
357   *                                 or if the value cannot be parsed as a
358   *                                 boolean value.
359   */
360  public boolean getBooleanValue(final int opType)
361         throws NoSuchElementException, IllegalStateException
362  {
363    final PasswordPolicyStateOperation op = operations.get(opType);
364    if (op == null)
365    {
366      throw new NoSuchElementException(
367                     ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get());
368    }
369
370    return op.getBooleanValue();
371  }
372
373
374
375  /**
376   * Retrieves the value of the specified password policy state operation as an
377   * integer.
378   *
379   * @param  opType  The operation type for the password policy state operation
380   *                 to retrieve.
381   *
382   * @return  The integer value of the requested password policy state
383   *          operation.
384   *
385   * @throws  NoSuchElementException  If the specified operation was not
386   *                                  included in the response.
387   *
388   * @throws  IllegalStateException  If the value of the specified password
389   *                                 policy state operation cannot be parsed as
390   *                                 an integer value.
391   */
392  public int getIntValue(final int opType)
393         throws NoSuchElementException, IllegalStateException
394  {
395    final PasswordPolicyStateOperation op = operations.get(opType);
396    if (op == null)
397    {
398      throw new NoSuchElementException(
399                     ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get());
400    }
401
402    return op.getIntValue();
403  }
404
405
406
407  /**
408   * Retrieves the value for the specified password policy state operation as a
409   * {@code Date} in generalized time format.
410   *
411   * @param  opType  The operation type for the password policy state operation
412   *                 to retrieve.
413   *
414   * @return  The value of the requested password policy state operation as a
415   *          {@code Date}, or {@code null} if the specified operation was not
416   *          included in the response or did not have any values.
417   *
418   * @throws  ParseException  If the value cannot be parsed as a date in
419   *                          generalized time format.
420   */
421  public Date getGeneralizedTimeValue(final int opType)
422         throws ParseException
423  {
424    final PasswordPolicyStateOperation op = operations.get(opType);
425    if (op == null)
426    {
427      return null;
428    }
429
430    return op.getGeneralizedTimeValue();
431  }
432
433
434
435  /**
436   * Retrieves the set of values for the specified password policy state
437   * operation as {@code Date}s in generalized time format.
438   *
439   * @param  opType  The operation type for the password policy state operation
440   *                 to retrieve.
441   *
442   * @return  The set of values of the requested password policy state operation
443   *          as {@code Date}s.
444   *
445   * @throws  ParseException  If any of the values cannot be parsed as a date in
446   *                          generalized time format.
447   */
448  public Date[] getGeneralizedTimeValues(final int opType)
449         throws ParseException
450  {
451    final PasswordPolicyStateOperation op = operations.get(opType);
452    if (op == null)
453    {
454      return null;
455    }
456
457    return op.getGeneralizedTimeValues();
458  }
459
460
461
462  /**
463   * {@inheritDoc}
464   */
465  @Override()
466  public String getExtendedResultName()
467  {
468    return INFO_EXTENDED_RESULT_NAME_PW_POLICY_STATE.get();
469  }
470
471
472
473  /**
474   * Appends a string representation of this extended result to the provided
475   * buffer.
476   *
477   * @param  buffer  The buffer to which a string representation of this
478   *                 extended result will be appended.
479   */
480  @Override()
481  public void toString(final StringBuilder buffer)
482  {
483    buffer.append("PasswordPolicyStateExtendedResult(resultCode=");
484    buffer.append(getResultCode());
485
486    final int messageID = getMessageID();
487    if (messageID >= 0)
488    {
489      buffer.append(", messageID=");
490      buffer.append(messageID);
491    }
492
493    buffer.append(", userDN='");
494    buffer.append(userDN);
495    buffer.append("', operations={");
496
497    final Iterator<PasswordPolicyStateOperation> iterator =
498         operations.values().iterator();
499    while (iterator.hasNext())
500    {
501      iterator.next().toString(buffer);
502      if (iterator.hasNext())
503      {
504        buffer.append(", ");
505      }
506    }
507    buffer.append('}');
508
509    final String diagnosticMessage = getDiagnosticMessage();
510    if (diagnosticMessage != null)
511    {
512      buffer.append(", diagnosticMessage='");
513      buffer.append(diagnosticMessage);
514      buffer.append('\'');
515    }
516
517    final String matchedDN = getMatchedDN();
518    if (matchedDN != null)
519    {
520      buffer.append(", matchedDN='");
521      buffer.append(matchedDN);
522      buffer.append('\'');
523    }
524
525    final String[] referralURLs = getReferralURLs();
526    if (referralURLs.length > 0)
527    {
528      buffer.append(", referralURLs={");
529      for (int i=0; i < referralURLs.length; i++)
530      {
531        if (i > 0)
532        {
533          buffer.append(", ");
534        }
535
536        buffer.append('\'');
537        buffer.append(referralURLs[i]);
538        buffer.append('\'');
539      }
540      buffer.append('}');
541    }
542
543    final Control[] responseControls = getResponseControls();
544    if (responseControls.length > 0)
545    {
546      buffer.append(", responseControls={");
547      for (int i=0; i < responseControls.length; i++)
548      {
549        if (i > 0)
550        {
551          buffer.append(", ");
552        }
553
554        buffer.append(responseControls[i]);
555      }
556      buffer.append('}');
557    }
558
559    buffer.append(')');
560  }
561}