001/*
002 * Copyright 2017-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-2019 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.controls;
022
023
024
025import java.util.ArrayList;
026
027import com.unboundid.asn1.ASN1Boolean;
028import com.unboundid.asn1.ASN1Element;
029import com.unboundid.asn1.ASN1OctetString;
030import com.unboundid.asn1.ASN1Sequence;
031import com.unboundid.ldap.sdk.Control;
032import com.unboundid.ldap.sdk.LDAPException;
033import com.unboundid.ldap.sdk.ResultCode;
034import com.unboundid.util.Debug;
035import com.unboundid.util.NotMutable;
036import com.unboundid.util.StaticUtils;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
041
042
043
044/**
045 * This class provides an implementation of a request control that can be
046 * included in an add request, modify request, or password modify extended
047 * request to control the way the server should behave when performing a
048 * password change.  The requester must have the password-reset privilege.
049 * <BR>
050 * <BLOCKQUOTE>
051 *   <B>NOTE:</B>  This class, and other classes within the
052 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
053 *   supported for use against Ping Identity, UnboundID, and
054 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
055 *   for proprietary functionality or for external specifications that are not
056 *   considered stable or mature enough to be guaranteed to work in an
057 *   interoperable way with other types of LDAP servers.
058 * </BLOCKQUOTE>
059 * <BR>
060 * This request control has an OID of 1.3.6.1.4.1.30221.2.5.51.  The criticality
061 * may be either true or false.  It must have a value, and the value should have
062 * the following encoding:
063 * <PRE>
064 *   PasswordUpdateBehaviorRequest ::= SEQUENCE {
065 *        isSelfChange                        [0] BOOLEAN OPTIONAL,
066 *        allowPreEncodedPassword             [1] BOOLEAN OPTIONAL,
067 *        skipPasswordValidation              [2] BOOLEAN OPTIONAL,
068 *        ignorePasswordHistory               [3] BOOLEAN OPTIONAL,
069 *        ignoreMinimumPasswordAge            [4] BOOLEAN OPTIONAL,
070 *        passwordStorageScheme               [5] OCTET STRING OPTIONAL,
071 *        mustChangePassword                  [6] BOOLEAN OPTIONAL,
072 *        ... }
073 * </PRE>
074 *
075 * @see  PasswordUpdateBehaviorRequestControlProperties
076 */
077@NotMutable()
078@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
079public final class PasswordUpdateBehaviorRequestControl
080       extends Control
081{
082  /**
083   * The OID (1.3.6.1.4.1.30221.2.5.51) for the password update behavior request
084   * control.
085   */
086  public static final String PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID =
087       "1.3.6.1.4.1.30221.2.5.51";
088
089
090
091  /**
092   * The BER type to use for the {@code isSelfChange} element in the encoded
093   * request.
094   */
095  private static final byte TYPE_IS_SELF_CHANGE = (byte) 0x80;
096
097
098
099  /**
100   * The BER type to use for the {@code allowPreEncodedPassword} element in the
101   * encoded request.
102   */
103  private static final byte TYPE_ALLOW_PRE_ENCODED_PASSWORD = (byte) 0x81;
104
105
106
107  /**
108   * The BER type to use for the {@code skipPasswordValidation} element in the
109   * encoded request.
110   */
111  private static final byte TYPE_SKIP_PASSWORD_VALIDATION = (byte) 0x82;
112
113
114
115  /**
116   * The BER type to use for the {@code ignorePasswordHistory} element in the
117   * encoded request.
118   */
119  private static final byte TYPE_IGNORE_PASSWORD_HISTORY = (byte) 0x83;
120
121
122
123  /**
124   * The BER type to use for the {@code ignoreMinimumPasswordAge} element in the
125   * encoded request.
126   */
127  private static final byte TYPE_IGNORE_MINIMUM_PASSWORD_AGE = (byte) 0x84;
128
129
130
131  /**
132   * The BER type to use for the {@code passwordStorageScheme} element in the
133   * encoded request.
134   */
135  private static final byte TYPE_PASSWORD_STORAGE_SCHEME = (byte) 0x85;
136
137
138
139  /**
140   * The BER type to use for the {@code mustChangePassword} element in the
141   * encoded request.
142   */
143  private static final byte TYPE_MUST_CHANGE_PASSWORD = (byte) 0x86;
144
145
146
147  /**
148   * The serial version UID for this serializable class.
149   */
150  private static final long serialVersionUID = -1915608505128236450L;
151
152
153
154  // Indicates whether the requester should be allowed to provide a pre-encoded
155  // password.
156  private final Boolean allowPreEncodedPassword;
157
158  // Indicates whether to ignore any minimum password age configured in the
159  // password policy.
160  private final Boolean ignoreMinimumPasswordAge;
161
162  // Indicates whether to skip the process of checking whether the provided
163  // password matches the new current password or is in the password history.
164  private final Boolean ignorePasswordHistory;
165
166  // Indicates whether to treat the password change as a self change.
167  private final Boolean isSelfChange;
168
169  // Indicates whether to update the user's account to indicate that they must
170  // change their password the next time they authenticate.
171  private final Boolean mustChangePassword;
172
173  // Indicates whether to skip password validation for the new password.
174  private final Boolean skipPasswordValidation;
175
176  // Specifies the password storage scheme to use for the new password.
177  private final String passwordStorageScheme;
178
179
180
181  /**
182   * Creates a new password update behavior request control with the provided
183   * information.
184   *
185   * @param  properties  The set of properties to use for the request control.
186   *                     It must not be {@code null}.
187   * @param  isCritical  Indicates whether the control should be considered
188   *                     critical.
189   */
190  public PasswordUpdateBehaviorRequestControl(
191              final PasswordUpdateBehaviorRequestControlProperties properties,
192              final boolean isCritical)
193  {
194    super(PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID, isCritical,
195         encodeValue(properties));
196
197    isSelfChange = properties.getIsSelfChange();
198    allowPreEncodedPassword = properties.getAllowPreEncodedPassword();
199    skipPasswordValidation = properties.getSkipPasswordValidation();
200    ignorePasswordHistory = properties.getIgnorePasswordHistory();
201    ignoreMinimumPasswordAge = properties.getIgnoreMinimumPasswordAge();
202    passwordStorageScheme = properties.getPasswordStorageScheme();
203    mustChangePassword = properties.getMustChangePassword();
204  }
205
206
207
208  /**
209   * Creates a new password update behavior request control that is decoded from
210   * the provided generic control.
211   *
212   * @param  control  The control to be decoded as a password update behavior
213   *                  request control.  It must not be {@code null}.
214   *
215   * @throws  LDAPException  If the provided control cannot be parsed as a
216   *                         password update behavior request control.
217   */
218  public PasswordUpdateBehaviorRequestControl(final Control control)
219         throws LDAPException
220  {
221    super(control);
222
223    final ASN1OctetString value = control.getValue();
224    if (value == null)
225    {
226      throw new LDAPException(ResultCode.DECODING_ERROR,
227           ERR_PW_UPDATE_BEHAVIOR_REQ_DECODE_NO_VALUE.get());
228    }
229
230    try
231    {
232      Boolean allowPreEncoded = null;
233      Boolean ignoreAge = null;
234      Boolean ignoreHistory = null;
235      Boolean mustChange = null;
236      Boolean selfChange = null;
237      Boolean skipValidation = null;
238      String scheme = null;
239      for (final ASN1Element e :
240           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
241      {
242        switch (e.getType())
243        {
244          case TYPE_IS_SELF_CHANGE:
245            selfChange = ASN1Boolean.decodeAsBoolean(e).booleanValue();
246            break;
247          case TYPE_ALLOW_PRE_ENCODED_PASSWORD:
248            allowPreEncoded = ASN1Boolean.decodeAsBoolean(e).booleanValue();
249            break;
250          case TYPE_SKIP_PASSWORD_VALIDATION:
251            skipValidation = ASN1Boolean.decodeAsBoolean(e).booleanValue();
252            break;
253          case TYPE_IGNORE_PASSWORD_HISTORY:
254            ignoreHistory = ASN1Boolean.decodeAsBoolean(e).booleanValue();
255            break;
256          case TYPE_IGNORE_MINIMUM_PASSWORD_AGE:
257            ignoreAge = ASN1Boolean.decodeAsBoolean(e).booleanValue();
258            break;
259          case TYPE_PASSWORD_STORAGE_SCHEME:
260            scheme = ASN1OctetString.decodeAsOctetString(e).stringValue();
261            break;
262          case TYPE_MUST_CHANGE_PASSWORD:
263            mustChange = ASN1Boolean.decodeAsBoolean(e).booleanValue();
264            break;
265          default:
266            throw new LDAPException(ResultCode.DECODING_ERROR,
267                 ERR_PW_UPDATE_BEHAVIOR_REQ_DECODE_UNRECOGNIZED_ELEMENT_TYPE.
268                      get(StaticUtils.toHex(e.getType())));
269        }
270      }
271
272      isSelfChange = selfChange;
273      allowPreEncodedPassword = allowPreEncoded;
274      skipPasswordValidation = skipValidation;
275      ignorePasswordHistory = ignoreHistory;
276      ignoreMinimumPasswordAge = ignoreAge;
277      passwordStorageScheme = scheme;
278      mustChangePassword = mustChange;
279    }
280    catch (final Exception e)
281    {
282      Debug.debugException(e);
283      throw new LDAPException(ResultCode.DECODING_ERROR,
284           ERR_PW_UPDATE_BEHAVIOR_REQ_DECODE_ERROR.get(
285                StaticUtils.getExceptionMessage(e)),
286           e);
287    }
288  }
289
290
291
292  /**
293   * Encodes the provided properties into a form that can be used as the value
294   * for this control.
295   *
296   * @param  properties  The properties to be encoded.
297   *
298   * @return  An ASN.1 octet string that can be used as the request control
299   *          value.
300   */
301  private static ASN1OctetString encodeValue(
302               final PasswordUpdateBehaviorRequestControlProperties properties)
303  {
304    final ArrayList<ASN1Element> elements = new ArrayList<>(6);
305
306    if (properties.getIsSelfChange() != null)
307    {
308      elements.add(new ASN1Boolean(TYPE_IS_SELF_CHANGE,
309           properties.getIsSelfChange()));
310    }
311
312    if (properties.getAllowPreEncodedPassword() != null)
313    {
314      elements.add(new ASN1Boolean(TYPE_ALLOW_PRE_ENCODED_PASSWORD,
315           properties.getAllowPreEncodedPassword()));
316    }
317
318    if (properties.getSkipPasswordValidation() != null)
319    {
320      elements.add(new ASN1Boolean(TYPE_SKIP_PASSWORD_VALIDATION,
321           properties.getSkipPasswordValidation()));
322    }
323
324    if (properties.getIgnorePasswordHistory() != null)
325    {
326      elements.add(new ASN1Boolean(TYPE_IGNORE_PASSWORD_HISTORY,
327           properties.getIgnorePasswordHistory()));
328    }
329
330    if (properties.getIgnoreMinimumPasswordAge() != null)
331    {
332      elements.add(new ASN1Boolean(TYPE_IGNORE_MINIMUM_PASSWORD_AGE,
333           properties.getIgnoreMinimumPasswordAge()));
334    }
335
336    if (properties.getPasswordStorageScheme() != null)
337    {
338      elements.add(new ASN1OctetString(TYPE_PASSWORD_STORAGE_SCHEME,
339           properties.getPasswordStorageScheme()));
340    }
341
342    if (properties.getMustChangePassword() != null)
343    {
344      elements.add(new ASN1Boolean(TYPE_MUST_CHANGE_PASSWORD,
345           properties.getMustChangePassword()));
346    }
347
348    return new ASN1OctetString(new ASN1Sequence(elements).encode());
349  }
350
351
352
353  /**
354   * Indicates whether this control should override the server's automatic
355   * classification of the password update as a self change or an administrative
356   * reset, and if so, what the overridden value should be.
357   *
358   * @return  {@code Boolean.TRUE} if the server should treat the password
359   *          update as a self change, {@code Boolean.FALSE} if the server
360   *          should treat the password update as an administrative reset, or
361   *          {@code null} if the server should automatically determine whether
362   *          the password update is a self change or an administrative reset.
363   */
364  public Boolean getIsSelfChange()
365  {
366    return isSelfChange;
367  }
368
369
370
371  /**
372   * Indicates whether this control should override the value of the
373   * {@code allow-pre-encoded-passwords} configuration property for the target
374   * user's password policy, and if so, what the overridden value should be.
375   *
376   * @return  {@code Boolean.TRUE} if the server should accept a pre-encoded
377   *          password in the password update even if the server's password
378   *          policy configuration would normally not permit this,
379   *          {@code Boolean.FALSE} if the server should reject a pre-encoded
380   *          password in the password update even if the server's password
381   *          policy configuration would normally accept it, or {@code null} if
382   *          the password policy configuration should be used to determine
383   *          whether to accept pre-encoded passwords.
384   */
385  public Boolean getAllowPreEncodedPassword()
386  {
387    return allowPreEncodedPassword;
388  }
389
390
391
392  /**
393   * Indicates whether this control should override the server's normal behavior
394   * with regard to invoking password validators for any new passwords included
395   * in the password update, and if so, what the overridden behavior should be.
396   *
397   * @return  {@code Boolean.TRUE} if the server should skip invoking the
398   *          password validators configured in the target user's password
399   *          policy validators for any new passwords included in the password
400   *          update even if the server would normally perform password
401   *          validation, {@code Boolean.FALSE} if the server should invoke the
402   *          password validators even if it would normally skip them, or
403   *          {@code null} if the password policy configuration should be used
404   *          to determine whether to skip password validation.
405   */
406  public Boolean getSkipPasswordValidation()
407  {
408    return skipPasswordValidation;
409  }
410
411
412
413  /**
414   * Indicates whether this control should override the server's normal behavior
415   * with regard to checking the password history for any new passwords included
416   * in the password update, and if so, what the overridden behavior should be.
417   *
418   * @return  {@code Boolean.TRUE} if the server should not check to see whether
419   *          any new password matches the current password or is in the user's
420   *          password history even if it would normally perform that check,
421   *          {@code Boolean.FALSE} if the server should check to see whether
422   *          any new password matches the current or previous password even if
423   *          it would normally not perform such a check, or {@code null} if the
424   *          password policy configuration should be used to determine whether
425   *          to ignore the password history.
426   */
427  public Boolean getIgnorePasswordHistory()
428  {
429    return ignorePasswordHistory;
430  }
431
432
433
434  /**
435   * Indicates whether this control should override the server's normal behavior
436   * with regard to checking the minimum password age, and if so, what the
437   * overridden behavior should be.
438   *
439   * @return  {@code Boolean.TRUE} if the server should accept the password
440   *          change even if it has been less than the configured minimum
441   *          password age since the password was last changed,
442   *          {@code Boolean.FALSE} if the server should reject the password
443   *          change if it has been less than teh configured minimum password
444   *          age, or {@code null} if the password policy configuration should
445   *          be used to determine the appropriate behavior.
446   */
447  public Boolean getIgnoreMinimumPasswordAge()
448  {
449    return ignoreMinimumPasswordAge;
450  }
451
452
453
454  /**
455   * Indicates whether this control should override the server's normal behavior
456   * with regard to selecting the password storage scheme to use to encode new
457   * password values, and if so, which password storage scheme should be used.
458   *
459   * @return  The name of the password storage scheme that should be used to
460   *          encode any new password values, or {@code null} if the target
461   *          user's password policy configuration should determine the
462   *          appropriate schemes for encoding new passwords.
463   */
464  public String getPasswordStorageScheme()
465  {
466    return passwordStorageScheme;
467  }
468
469
470
471  /**
472   * Indicates whether this control should override the server's normal behavior
473   * with regard to requiring a password change, and if so, what that behavior
474   * should be.
475   *
476   * @return  {@code Boolean.TRUE} if the user will be required to change their
477   *          password before being allowed to perform any other operation,
478   *          {@code Boolean.FALSE} if the user will not be required to change
479   *          their password before being allowed to perform any other
480   *          operation, or {@code null} if the password policy configuration
481   *          should be used to control this behavior.
482   */
483  public Boolean getMustChangePassword()
484  {
485    return mustChangePassword;
486  }
487
488
489
490  /**
491   * {@inheritDoc}
492   */
493  @Override()
494  public String getControlName()
495  {
496    return INFO_PW_UPDATE_BEHAVIOR_REQ_CONTROL_NAME.get();
497  }
498
499
500
501  /**
502   * {@inheritDoc}
503   */
504  @Override()
505  public void toString(final StringBuilder buffer)
506  {
507    buffer.append("PasswordUpdateBehaviorRequestControl(oid='");
508    buffer.append(PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID);
509    buffer.append("', isCritical=");
510    buffer.append(isCritical());
511    buffer.append(", properties=");
512    new PasswordUpdateBehaviorRequestControlProperties(this).toString(buffer);
513    buffer.append(')');
514  }
515}