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