001/*
002 * Copyright 2007-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2007-2018 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.experimental;
022
023
024
025import java.util.ArrayList;
026
027import com.unboundid.asn1.ASN1Element;
028import com.unboundid.asn1.ASN1Enumerated;
029import com.unboundid.asn1.ASN1Exception;
030import com.unboundid.asn1.ASN1Integer;
031import com.unboundid.asn1.ASN1OctetString;
032import com.unboundid.asn1.ASN1Sequence;
033import com.unboundid.ldap.sdk.Control;
034import com.unboundid.ldap.sdk.DecodeableControl;
035import com.unboundid.ldap.sdk.LDAPException;
036import com.unboundid.ldap.sdk.LDAPResult;
037import com.unboundid.ldap.sdk.ResultCode;
038import com.unboundid.util.NotMutable;
039import com.unboundid.util.ThreadSafety;
040import com.unboundid.util.ThreadSafetyLevel;
041
042import static com.unboundid.ldap.sdk.experimental.ExperimentalMessages.*;
043import static com.unboundid.util.Debug.*;
044import static com.unboundid.util.StaticUtils.*;
045
046
047
048/**
049 * This class provides an implementation of the password policy response control
050 * as described in draft-behera-ldap-password-policy-10.  It may be used to
051 * provide information related to a user's password policy.  It may include at
052 * most one warning from the set of
053 * {@link DraftBeheraLDAPPasswordPolicy10WarningType} values and at most one
054 * error from the set of {@link DraftBeheraLDAPPasswordPolicy10ErrorType}
055 * values.  See the documentation for those classes for more information on the
056 * information that may be included.  See the
057 * {@link DraftBeheraLDAPPasswordPolicy10RequestControl} documentation for an
058 * example that demonstrates the use of the password policy request and response
059 * controls.
060 */
061@NotMutable()
062@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
063public final class DraftBeheraLDAPPasswordPolicy10ResponseControl
064       extends Control
065       implements DecodeableControl
066{
067  /**
068   * The OID (1.3.6.1.4.1.42.2.27.8.5.1) for the password policy response
069   * control.
070   */
071  public static final String PASSWORD_POLICY_RESPONSE_OID =
072       "1.3.6.1.4.1.42.2.27.8.5.1";
073
074
075
076  /**
077   * The BER type for the password policy warning element.
078   */
079  private static final byte TYPE_WARNING = (byte) 0xA0;
080
081
082
083  /**
084   * The BER type for the password policy error element.
085   */
086  private static final byte TYPE_ERROR = (byte) 0x81;
087
088
089
090  /**
091   * The BER type for the "time before expiration" warning element.
092   */
093  private static final byte TYPE_TIME_BEFORE_EXPIRATION = (byte) 0x80;
094
095
096
097  /**
098   * The BER type for the "grace logins remaining" warning element.
099   */
100  private static final byte TYPE_GRACE_LOGINS_REMAINING = (byte) 0x81;
101
102
103
104  /**
105   * The serial version UID for this serializable class.
106   */
107  private static final long serialVersionUID = 1835830253434331833L;
108
109
110
111  // The password policy warning value, if applicable.
112  private final int warningValue;
113
114  // The password policy error type, if applicable.
115  private final DraftBeheraLDAPPasswordPolicy10ErrorType errorType;
116
117  // The password policy warning type, if applicable.
118  private final DraftBeheraLDAPPasswordPolicy10WarningType warningType;
119
120
121
122  /**
123   * Creates a new empty control instance that is intended to be used only for
124   * decoding controls via the {@code DecodeableControl} interface.
125   */
126  DraftBeheraLDAPPasswordPolicy10ResponseControl()
127  {
128    warningType  = null;
129    errorType    = null;
130    warningValue = -1;
131  }
132
133
134
135  /**
136   * Creates a new password policy response control with the provided
137   * information.  It will not be critical.
138   *
139   * @param  warningType   The password policy warning type for this response
140   *                       control, or {@code null} if there should be no
141   *                       warning type.
142   * @param  warningValue  The value for the password policy warning type, or -1
143   *                       if there is no warning type.
144   * @param  errorType     The password policy error type for this response
145   *                       control, or {@code null} if there should be no error
146   *                       type.
147   */
148  public DraftBeheraLDAPPasswordPolicy10ResponseControl(
149              final DraftBeheraLDAPPasswordPolicy10WarningType warningType,
150              final int warningValue,
151              final DraftBeheraLDAPPasswordPolicy10ErrorType errorType)
152  {
153    this(warningType, warningValue, errorType, false);
154  }
155
156
157
158  /**
159   * Creates a new password policy response control with the provided
160   * information.
161   *
162   * @param  warningType   The password policy warning type for this response
163   *                       control, or {@code null} if there should be no
164   *                       warning type.
165   * @param  warningValue  The value for the password policy warning type, or -1
166   *                       if there is no warning type.
167   * @param  errorType     The password policy error type for this response
168   *                       control, or {@code null} if there should be no error
169   *                       type.
170   * @param  isCritical    Indicates whether this control should be marked
171   *                       critical.
172   */
173  public DraftBeheraLDAPPasswordPolicy10ResponseControl(
174              final DraftBeheraLDAPPasswordPolicy10WarningType warningType,
175              final int warningValue,
176              final DraftBeheraLDAPPasswordPolicy10ErrorType errorType,
177              final boolean isCritical)
178  {
179    super(PASSWORD_POLICY_RESPONSE_OID, isCritical,
180          encodeValue(warningType, warningValue, errorType));
181
182    this.warningType = warningType;
183    this.errorType   = errorType;
184
185    if (warningType == null)
186    {
187      this.warningValue = -1;
188    }
189    else
190    {
191      this.warningValue = warningValue;
192    }
193  }
194
195
196
197  /**
198   * Creates a new password policy response control with the provided
199   * information.
200   *
201   * @param  oid         The OID for the control.
202   * @param  isCritical  Indicates whether the control should be marked
203   *                     critical.
204   * @param  value       The encoded value for the control.  This may be
205   *                     {@code null} if no value was provided.
206   *
207   * @throws  LDAPException  If the provided control cannot be decoded as a
208   *                         password policy response control.
209   */
210  public DraftBeheraLDAPPasswordPolicy10ResponseControl(final String oid,
211              final boolean isCritical, final ASN1OctetString value)
212         throws LDAPException
213  {
214    super(oid, isCritical, value);
215
216    if (value == null)
217    {
218      throw new LDAPException(ResultCode.DECODING_ERROR,
219                              ERR_PWP_RESPONSE_NO_VALUE.get());
220    }
221
222    final ASN1Sequence valueSequence;
223    try
224    {
225      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
226      valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
227    }
228    catch (final ASN1Exception ae)
229    {
230      debugException(ae);
231      throw new LDAPException(ResultCode.DECODING_ERROR,
232                              ERR_PWP_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
233    }
234
235    final ASN1Element[] valueElements = valueSequence.elements();
236    if (valueElements.length > 2)
237    {
238      throw new LDAPException(ResultCode.DECODING_ERROR,
239                              ERR_PWP_RESPONSE_INVALID_ELEMENT_COUNT.get(
240                                   valueElements.length));
241    }
242
243    int                                        wv = -1;
244    DraftBeheraLDAPPasswordPolicy10ErrorType   et = null;
245    DraftBeheraLDAPPasswordPolicy10WarningType wt = null;
246    for (final ASN1Element e : valueElements)
247    {
248      switch (e.getType())
249      {
250        case TYPE_WARNING:
251          if (wt == null)
252          {
253            try
254            {
255              final ASN1Element warningElement =
256                   ASN1Element.decode(e.getValue());
257              wv = ASN1Integer.decodeAsInteger(warningElement).intValue();
258              switch (warningElement.getType())
259              {
260                case TYPE_TIME_BEFORE_EXPIRATION:
261                  wt = DraftBeheraLDAPPasswordPolicy10WarningType.
262                       TIME_BEFORE_EXPIRATION;
263                  break;
264
265                case TYPE_GRACE_LOGINS_REMAINING:
266                  wt = DraftBeheraLDAPPasswordPolicy10WarningType.
267                       GRACE_LOGINS_REMAINING;
268                  break;
269
270                default:
271                  throw new LDAPException(ResultCode.DECODING_ERROR,
272                                 ERR_PWP_RESPONSE_INVALID_WARNING_TYPE.get(
273                                      toHex(warningElement.getType())));
274              }
275            }
276            catch (final ASN1Exception ae)
277            {
278              debugException(ae);
279              throw new LDAPException(ResultCode.DECODING_ERROR,
280                             ERR_PWP_RESPONSE_CANNOT_DECODE_WARNING.get(ae),
281                             ae);
282            }
283          }
284          else
285          {
286            throw new LDAPException(ResultCode.DECODING_ERROR,
287                                    ERR_PWP_RESPONSE_MULTIPLE_WARNING.get());
288          }
289          break;
290
291        case TYPE_ERROR:
292          if (et == null)
293          {
294            try
295            {
296              final ASN1Enumerated errorElement =
297                   ASN1Enumerated.decodeAsEnumerated(e);
298              et = DraftBeheraLDAPPasswordPolicy10ErrorType.valueOf(
299                   errorElement.intValue());
300              if (et == null)
301              {
302                  throw new LDAPException(ResultCode.DECODING_ERROR,
303                                 ERR_PWP_RESPONSE_INVALID_ERROR_TYPE.get(
304                                      errorElement.intValue()));
305              }
306            }
307            catch (final ASN1Exception ae)
308            {
309              debugException(ae);
310              throw new LDAPException(ResultCode.DECODING_ERROR,
311                             ERR_PWP_RESPONSE_CANNOT_DECODE_ERROR.get(ae), ae);
312            }
313          }
314          else
315          {
316            throw new LDAPException(ResultCode.DECODING_ERROR,
317                                    ERR_PWP_RESPONSE_MULTIPLE_ERROR.get());
318          }
319          break;
320
321        default:
322          throw new LDAPException(ResultCode.DECODING_ERROR,
323                                  ERR_PWP_RESPONSE_INVALID_TYPE.get(
324                                       toHex(e.getType())));
325      }
326    }
327
328    warningType  = wt;
329    warningValue = wv;
330    errorType    = et;
331  }
332
333
334
335  /**
336   * {@inheritDoc}
337   */
338  @Override()
339  public DraftBeheraLDAPPasswordPolicy10ResponseControl
340              decodeControl(final String oid, final boolean isCritical,
341                            final ASN1OctetString value)
342         throws LDAPException
343  {
344    return new DraftBeheraLDAPPasswordPolicy10ResponseControl(oid, isCritical,
345         value);
346  }
347
348
349
350  /**
351   * Extracts a password policy response control from the provided result.
352   *
353   * @param  result  The result from which to retrieve the password policy
354   *                 response control.
355   *
356   * @return  The password policy response control contained in the provided
357   *          result, or {@code null} if the result did not contain a password
358   *          policy response control.
359   *
360   * @throws  LDAPException  If a problem is encountered while attempting to
361   *                         decode the password policy response control
362   *                         contained in the provided result.
363   */
364  public static DraftBeheraLDAPPasswordPolicy10ResponseControl get(
365                     final LDAPResult result)
366         throws LDAPException
367  {
368    final Control c = result.getResponseControl(PASSWORD_POLICY_RESPONSE_OID);
369    if (c == null)
370    {
371      return null;
372    }
373
374    if (c instanceof DraftBeheraLDAPPasswordPolicy10ResponseControl)
375    {
376      return (DraftBeheraLDAPPasswordPolicy10ResponseControl) c;
377    }
378    else
379    {
380      return new DraftBeheraLDAPPasswordPolicy10ResponseControl(c.getOID(),
381           c.isCritical(), c.getValue());
382    }
383  }
384
385
386
387  /**
388   * Encodes the provided information as appropriate for use as the value of a
389   * password policy response control.
390   *
391   * @param  warningType   The warning type to use for the warning element, or
392   *                       {@code null} if there is not to be a warning element.
393   * @param  warningValue  The value to use for the warning element.
394   * @param  errorType     The error type to use for the error element, or
395   *                       {@code null} if there is not to be an error element.
396   *
397   * @return  The ASN.1 octet string containing the encoded control value.
398   */
399  private static ASN1OctetString encodeValue(
400       final DraftBeheraLDAPPasswordPolicy10WarningType warningType,
401       final int warningValue,
402       final DraftBeheraLDAPPasswordPolicy10ErrorType errorType)
403  {
404    final ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(2);
405
406    if (warningType != null)
407    {
408      switch (warningType)
409      {
410        case TIME_BEFORE_EXPIRATION:
411          valueElements.add(new ASN1Element(TYPE_WARNING,
412               new ASN1Integer(TYPE_TIME_BEFORE_EXPIRATION,
413                               warningValue).encode()));
414          break;
415
416        case GRACE_LOGINS_REMAINING:
417          valueElements.add(new ASN1Element(TYPE_WARNING,
418               new ASN1Integer(TYPE_GRACE_LOGINS_REMAINING,
419                               warningValue).encode()));
420          break;
421      }
422    }
423
424    if (errorType != null)
425    {
426      valueElements.add(new ASN1Enumerated(TYPE_ERROR, errorType.intValue()));
427    }
428
429    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
430  }
431
432
433
434  /**
435   * Retrieves the warning type for this password policy response control, if
436   * available.
437   *
438   * @return  The warning type for this password policy response control, or
439   *          {@code null} if there is no warning type.
440   */
441  public DraftBeheraLDAPPasswordPolicy10WarningType getWarningType()
442  {
443    return warningType;
444  }
445
446
447
448  /**
449   * Retrieves the warning value for this password policy response control, if
450   * available.
451   *
452   * @return  The warning value for this password policy response control, or -1
453   *          if there is no warning type.
454   */
455  public int getWarningValue()
456  {
457    return warningValue;
458  }
459
460
461
462  /**
463   * Retrieves the error type for this password policy response control, if
464   * available.
465   *
466   * @return  The error type for this password policy response control, or
467   *          {@code null} if there is no error type.
468   */
469  public DraftBeheraLDAPPasswordPolicy10ErrorType getErrorType()
470  {
471    return errorType;
472  }
473
474
475
476  /**
477   * {@inheritDoc}
478   */
479  @Override()
480  public String getControlName()
481  {
482    return INFO_CONTROL_NAME_PW_POLICY_RESPONSE.get();
483  }
484
485
486
487  /**
488   * {@inheritDoc}
489   */
490  @Override()
491  public void toString(final StringBuilder buffer)
492  {
493    boolean elementAdded = false;
494
495    buffer.append("PasswordPolicyResponseControl(");
496
497    if (warningType != null)
498    {
499      buffer.append("warningType='");
500      buffer.append(warningType.getName());
501      buffer.append("', warningValue=");
502      buffer.append(warningValue);
503      elementAdded = true;
504    }
505
506    if (errorType != null)
507    {
508      if (elementAdded)
509      {
510        buffer.append(", ");
511      }
512
513      buffer.append("errorType='");
514      buffer.append(errorType.getName());
515      buffer.append('\'');
516      elementAdded = true;
517    }
518
519    if (elementAdded)
520    {
521      buffer.append(", ");
522    }
523
524    buffer.append("isCritical=");
525    buffer.append(isCritical());
526    buffer.append(')');
527  }
528}