001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2007-2015 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     */
021    package com.unboundid.ldap.sdk.experimental;
022    
023    
024    
025    import java.util.ArrayList;
026    
027    import com.unboundid.asn1.ASN1Element;
028    import com.unboundid.asn1.ASN1Enumerated;
029    import com.unboundid.asn1.ASN1Exception;
030    import com.unboundid.asn1.ASN1Integer;
031    import com.unboundid.asn1.ASN1OctetString;
032    import com.unboundid.asn1.ASN1Sequence;
033    import com.unboundid.ldap.sdk.Control;
034    import com.unboundid.ldap.sdk.DecodeableControl;
035    import com.unboundid.ldap.sdk.LDAPException;
036    import com.unboundid.ldap.sdk.LDAPResult;
037    import com.unboundid.ldap.sdk.ResultCode;
038    import com.unboundid.util.NotMutable;
039    import com.unboundid.util.ThreadSafety;
040    import com.unboundid.util.ThreadSafetyLevel;
041    
042    import static com.unboundid.ldap.sdk.experimental.ExperimentalMessages.*;
043    import static com.unboundid.util.Debug.*;
044    import 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)
063    public 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    }