001/*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2007-2014 UnboundID Corp.
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 (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 (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 (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  public DraftBeheraLDAPPasswordPolicy10ResponseControl
339              decodeControl(final String oid, final boolean isCritical,
340                            final ASN1OctetString value)
341         throws LDAPException
342  {
343    return new DraftBeheraLDAPPasswordPolicy10ResponseControl(oid, isCritical,
344         value);
345  }
346
347
348
349  /**
350   * Extracts a password policy response control from the provided result.
351   *
352   * @param  result  The result from which to retrieve the password policy
353   *                 response control.
354   *
355   * @return  The password policy response control contained in the provided
356   *          result, or {@code null} if the result did not contain a password
357   *          policy response control.
358   *
359   * @throws  LDAPException  If a problem is encountered while attempting to
360   *                         decode the password policy response control
361   *                         contained in the provided result.
362   */
363  public static DraftBeheraLDAPPasswordPolicy10ResponseControl get(
364                     final LDAPResult result)
365         throws LDAPException
366  {
367    final Control c = result.getResponseControl(PASSWORD_POLICY_RESPONSE_OID);
368    if (c == null)
369    {
370      return null;
371    }
372
373    if (c instanceof DraftBeheraLDAPPasswordPolicy10ResponseControl)
374    {
375      return (DraftBeheraLDAPPasswordPolicy10ResponseControl) c;
376    }
377    else
378    {
379      return new DraftBeheraLDAPPasswordPolicy10ResponseControl(c.getOID(),
380           c.isCritical(), c.getValue());
381    }
382  }
383
384
385
386  /**
387   * Encodes the provided information as appropriate for use as the value of a
388   * password policy response control.
389   *
390   * @param  warningType   The warning type to use for the warning element, or
391   *                       {@code null} if there is not to be a warning element.
392   * @param  warningValue  The value to use for the warning element.
393   * @param  errorType     The error type to use for the error element, or
394   *                       {@code null} if there is not to be an error element.
395   *
396   * @return  The ASN.1 octet string containing the encoded control value.
397   */
398  private static ASN1OctetString encodeValue(
399       final DraftBeheraLDAPPasswordPolicy10WarningType warningType,
400       final int warningValue,
401       final DraftBeheraLDAPPasswordPolicy10ErrorType errorType)
402  {
403    final ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(2);
404
405    if (warningType != null)
406    {
407      switch (warningType)
408      {
409        case TIME_BEFORE_EXPIRATION:
410          valueElements.add(new ASN1Element(TYPE_WARNING,
411               new ASN1Integer(TYPE_TIME_BEFORE_EXPIRATION,
412                               warningValue).encode()));
413          break;
414
415        case GRACE_LOGINS_REMAINING:
416          valueElements.add(new ASN1Element(TYPE_WARNING,
417               new ASN1Integer(TYPE_GRACE_LOGINS_REMAINING,
418                               warningValue).encode()));
419          break;
420      }
421    }
422
423    if (errorType != null)
424    {
425      valueElements.add(new ASN1Enumerated(TYPE_ERROR, errorType.intValue()));
426    }
427
428    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
429  }
430
431
432
433  /**
434   * Retrieves the warning type for this password policy response control, if
435   * available.
436   *
437   * @return  The warning type for this password policy response control, or
438   *          {@code null} if there is no warning type.
439   */
440  public DraftBeheraLDAPPasswordPolicy10WarningType getWarningType()
441  {
442    return warningType;
443  }
444
445
446
447  /**
448   * Retrieves the warning value for this password policy response control, if
449   * available.
450   *
451   * @return  The warning value for this password policy response control, or -1
452   *          if there is no warning type.
453   */
454  public int getWarningValue()
455  {
456    return warningValue;
457  }
458
459
460
461  /**
462   * Retrieves the error type for this password policy response control, if
463   * available.
464   *
465   * @return  The error type for this password policy response control, or
466   *          {@code null} if there is no error type.
467   */
468  public DraftBeheraLDAPPasswordPolicy10ErrorType getErrorType()
469  {
470    return errorType;
471  }
472
473
474
475  /**
476   * {@inheritDoc}
477   */
478  @Override()
479  public String getControlName()
480  {
481    return INFO_CONTROL_NAME_PW_POLICY_RESPONSE.get();
482  }
483
484
485
486  /**
487   * {@inheritDoc}
488   */
489  @Override()
490  public void toString(final StringBuilder buffer)
491  {
492    boolean elementAdded = false;
493
494    buffer.append("PasswordPolicyResponseControl(");
495
496    if (warningType != null)
497    {
498      buffer.append("warningType='");
499      buffer.append(warningType.getName());
500      buffer.append("', warningValue=");
501      buffer.append(warningValue);
502      elementAdded = true;
503    }
504
505    if (errorType != null)
506    {
507      if (elementAdded)
508      {
509        buffer.append(", ");
510      }
511
512      buffer.append("errorType='");
513      buffer.append(errorType.getName());
514      buffer.append('\'');
515      elementAdded = true;
516    }
517
518    if (elementAdded)
519    {
520      buffer.append(", ");
521    }
522
523    buffer.append("isCritical=");
524    buffer.append(isCritical());
525    buffer.append(')');
526  }
527}