001/*
002 * Copyright 2017-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2017-2024 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-2024 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;
041import java.util.LinkedHashMap;
042import java.util.List;
043import java.util.Map;
044
045import com.unboundid.asn1.ASN1Boolean;
046import com.unboundid.asn1.ASN1Element;
047import com.unboundid.asn1.ASN1OctetString;
048import com.unboundid.asn1.ASN1Sequence;
049import com.unboundid.ldap.sdk.Control;
050import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
051import com.unboundid.ldap.sdk.LDAPException;
052import com.unboundid.ldap.sdk.ResultCode;
053import com.unboundid.util.Debug;
054import com.unboundid.util.NotMutable;
055import com.unboundid.util.NotNull;
056import com.unboundid.util.Nullable;
057import com.unboundid.util.StaticUtils;
058import com.unboundid.util.ThreadSafety;
059import com.unboundid.util.ThreadSafetyLevel;
060import com.unboundid.util.json.JSONBoolean;
061import com.unboundid.util.json.JSONField;
062import com.unboundid.util.json.JSONObject;
063import com.unboundid.util.json.JSONString;
064import com.unboundid.util.json.JSONValue;
065
066import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
067
068
069
070/**
071 * This class provides an implementation of a request control that can be
072 * included in an add request, modify request, or password modify extended
073 * request to control the way the server should behave when performing a
074 * password change.  The requester must have the password-reset privilege.
075 * <BR>
076 * <BLOCKQUOTE>
077 *   <B>NOTE:</B>  This class, and other classes within the
078 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
079 *   supported for use against Ping Identity, UnboundID, and
080 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
081 *   for proprietary functionality or for external specifications that are not
082 *   considered stable or mature enough to be guaranteed to work in an
083 *   interoperable way with other types of LDAP servers.
084 * </BLOCKQUOTE>
085 * <BR>
086 * This request control has an OID of 1.3.6.1.4.1.30221.2.5.51.  The criticality
087 * may be either true or false.  It must have a value, and the value should have
088 * the following encoding:
089 * <PRE>
090 *   PasswordUpdateBehaviorRequest ::= SEQUENCE {
091 *        isSelfChange                        [0] BOOLEAN OPTIONAL,
092 *        allowPreEncodedPassword             [1] BOOLEAN OPTIONAL,
093 *        skipPasswordValidation              [2] BOOLEAN OPTIONAL,
094 *        ignorePasswordHistory               [3] BOOLEAN OPTIONAL,
095 *        ignoreMinimumPasswordAge            [4] BOOLEAN OPTIONAL,
096 *        passwordStorageScheme               [5] OCTET STRING OPTIONAL,
097 *        mustChangePassword                  [6] BOOLEAN OPTIONAL,
098 *        ... }
099 * </PRE>
100 *
101 * @see  PasswordUpdateBehaviorRequestControlProperties
102 */
103@NotMutable()
104@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
105public final class PasswordUpdateBehaviorRequestControl
106       extends Control
107{
108  /**
109   * The OID (1.3.6.1.4.1.30221.2.5.51) for the password update behavior request
110   * control.
111   */
112  @NotNull public static final String PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID =
113       "1.3.6.1.4.1.30221.2.5.51";
114
115
116
117  /**
118   * The BER type to use for the {@code isSelfChange} element in the encoded
119   * request.
120   */
121  private static final byte TYPE_IS_SELF_CHANGE = (byte) 0x80;
122
123
124
125  /**
126   * The BER type to use for the {@code allowPreEncodedPassword} element in the
127   * encoded request.
128   */
129  private static final byte TYPE_ALLOW_PRE_ENCODED_PASSWORD = (byte) 0x81;
130
131
132
133  /**
134   * The BER type to use for the {@code skipPasswordValidation} element in the
135   * encoded request.
136   */
137  private static final byte TYPE_SKIP_PASSWORD_VALIDATION = (byte) 0x82;
138
139
140
141  /**
142   * The BER type to use for the {@code ignorePasswordHistory} element in the
143   * encoded request.
144   */
145  private static final byte TYPE_IGNORE_PASSWORD_HISTORY = (byte) 0x83;
146
147
148
149  /**
150   * The BER type to use for the {@code ignoreMinimumPasswordAge} element in the
151   * encoded request.
152   */
153  private static final byte TYPE_IGNORE_MINIMUM_PASSWORD_AGE = (byte) 0x84;
154
155
156
157  /**
158   * The BER type to use for the {@code passwordStorageScheme} element in the
159   * encoded request.
160   */
161  private static final byte TYPE_PASSWORD_STORAGE_SCHEME = (byte) 0x85;
162
163
164
165  /**
166   * The BER type to use for the {@code mustChangePassword} element in the
167   * encoded request.
168   */
169  private static final byte TYPE_MUST_CHANGE_PASSWORD = (byte) 0x86;
170
171
172
173  /**
174   * The name of the field used to hold the allow-pre-encoded-password value in
175   * the JSON representation of this control.
176   */
177  @NotNull private static final String JSON_FIELD_ALLOW_PRE_ENCODED_PASSWORD =
178       "allow-pre-encoded-password";
179
180
181
182  /**
183   * The name of the field used to hold the ignore-minimum-password-age value in
184   * the JSON representation of this control.
185   */
186  @NotNull private static final String JSON_FIELD_IGNORE_MINIMUM_PASSWORD_AGE =
187       "ignore-minimum-password-age";
188
189
190
191  /**
192   * The name of the field used to hold the ignore-password-history value in the
193   * JSON representation of this control.
194   */
195  @NotNull private static final String JSON_FIELD_IGNORE_PASSWORD_HISTORY =
196       "ignore-password-history";
197
198
199
200  /**
201   * The name of the field used to hold the is-self-change value in the JSON
202   * representation of this control.
203   */
204  @NotNull private static final String JSON_FIELD_IS_SELF_CHANGE =
205       "is-self-change";
206
207
208
209  /**
210   * The name of the field used to hold the must-change-password value in the
211   * JSON representation of this control.
212   */
213  @NotNull private static final String JSON_FIELD_MUST_CHANGE_PASSWORD =
214       "must-change-password";
215
216
217
218  /**
219   * The name of the field used to hold the password-storage-scheme value in the
220   * JSON representation of this control.
221   */
222  @NotNull private static final String JSON_FIELD_PASSWORD_STORAGE_SCHEME =
223       "password-storage-scheme";
224
225
226
227  /**
228   * The name of the field used to hold the skip-password-validation value in
229   * the JSON representation of this control.
230   */
231  @NotNull private static final String JSON_FIELD_SKIP_PASSWORD_VALIDATION =
232       "skip-password-validation";
233
234
235
236  /**
237   * The serial version UID for this serializable class.
238   */
239  private static final long serialVersionUID = -1915608505128236450L;
240
241
242
243  // Indicates whether the requester should be allowed to provide a pre-encoded
244  // password.
245  @Nullable private final Boolean allowPreEncodedPassword;
246
247  // Indicates whether to ignore any minimum password age configured in the
248  // password policy.
249  @Nullable private final Boolean ignoreMinimumPasswordAge;
250
251  // Indicates whether to skip the process of checking whether the provided
252  // password matches the new current password or is in the password history.
253  @Nullable private final Boolean ignorePasswordHistory;
254
255  // Indicates whether to treat the password change as a self change.
256  @Nullable private final Boolean isSelfChange;
257
258  // Indicates whether to update the user's account to indicate that they must
259  // change their password the next time they authenticate.
260  @Nullable private final Boolean mustChangePassword;
261
262  // Indicates whether to skip password validation for the new password.
263  @Nullable private final Boolean skipPasswordValidation;
264
265  // Specifies the password storage scheme to use for the new password.
266  @Nullable private final String passwordStorageScheme;
267
268
269
270  /**
271   * Creates a new password update behavior request control with the provided
272   * information.
273   *
274   * @param  properties  The set of properties to use for the request control.
275   *                     It must not be {@code null}.
276   * @param  isCritical  Indicates whether the control should be considered
277   *                     critical.
278   */
279  public PasswordUpdateBehaviorRequestControl(
280       @NotNull final PasswordUpdateBehaviorRequestControlProperties properties,
281       final boolean isCritical)
282  {
283    super(PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID, isCritical,
284         encodeValue(properties));
285
286    isSelfChange = properties.getIsSelfChange();
287    allowPreEncodedPassword = properties.getAllowPreEncodedPassword();
288    skipPasswordValidation = properties.getSkipPasswordValidation();
289    ignorePasswordHistory = properties.getIgnorePasswordHistory();
290    ignoreMinimumPasswordAge = properties.getIgnoreMinimumPasswordAge();
291    passwordStorageScheme = properties.getPasswordStorageScheme();
292    mustChangePassword = properties.getMustChangePassword();
293  }
294
295
296
297  /**
298   * Creates a new password update behavior request control that is decoded from
299   * the provided generic control.
300   *
301   * @param  control  The control to be decoded as a password update behavior
302   *                  request control.  It must not be {@code null}.
303   *
304   * @throws  LDAPException  If the provided control cannot be parsed as a
305   *                         password update behavior request control.
306   */
307  public PasswordUpdateBehaviorRequestControl(@NotNull final Control control)
308         throws LDAPException
309  {
310    super(control);
311
312    final ASN1OctetString value = control.getValue();
313    if (value == null)
314    {
315      throw new LDAPException(ResultCode.DECODING_ERROR,
316           ERR_PW_UPDATE_BEHAVIOR_REQ_DECODE_NO_VALUE.get());
317    }
318
319    try
320    {
321      Boolean allowPreEncoded = null;
322      Boolean ignoreAge = null;
323      Boolean ignoreHistory = null;
324      Boolean mustChange = null;
325      Boolean selfChange = null;
326      Boolean skipValidation = null;
327      String scheme = null;
328      for (final ASN1Element e :
329           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
330      {
331        switch (e.getType())
332        {
333          case TYPE_IS_SELF_CHANGE:
334            selfChange = ASN1Boolean.decodeAsBoolean(e).booleanValue();
335            break;
336          case TYPE_ALLOW_PRE_ENCODED_PASSWORD:
337            allowPreEncoded = ASN1Boolean.decodeAsBoolean(e).booleanValue();
338            break;
339          case TYPE_SKIP_PASSWORD_VALIDATION:
340            skipValidation = ASN1Boolean.decodeAsBoolean(e).booleanValue();
341            break;
342          case TYPE_IGNORE_PASSWORD_HISTORY:
343            ignoreHistory = ASN1Boolean.decodeAsBoolean(e).booleanValue();
344            break;
345          case TYPE_IGNORE_MINIMUM_PASSWORD_AGE:
346            ignoreAge = ASN1Boolean.decodeAsBoolean(e).booleanValue();
347            break;
348          case TYPE_PASSWORD_STORAGE_SCHEME:
349            scheme = ASN1OctetString.decodeAsOctetString(e).stringValue();
350            break;
351          case TYPE_MUST_CHANGE_PASSWORD:
352            mustChange = ASN1Boolean.decodeAsBoolean(e).booleanValue();
353            break;
354          default:
355            throw new LDAPException(ResultCode.DECODING_ERROR,
356                 ERR_PW_UPDATE_BEHAVIOR_REQ_DECODE_UNRECOGNIZED_ELEMENT_TYPE.
357                      get(StaticUtils.toHex(e.getType())));
358        }
359      }
360
361      isSelfChange = selfChange;
362      allowPreEncodedPassword = allowPreEncoded;
363      skipPasswordValidation = skipValidation;
364      ignorePasswordHistory = ignoreHistory;
365      ignoreMinimumPasswordAge = ignoreAge;
366      passwordStorageScheme = scheme;
367      mustChangePassword = mustChange;
368    }
369    catch (final Exception e)
370    {
371      Debug.debugException(e);
372      throw new LDAPException(ResultCode.DECODING_ERROR,
373           ERR_PW_UPDATE_BEHAVIOR_REQ_DECODE_ERROR.get(
374                StaticUtils.getExceptionMessage(e)),
375           e);
376    }
377  }
378
379
380
381  /**
382   * Encodes the provided properties into a form that can be used as the value
383   * for this control.
384   *
385   * @param  properties  The properties to be encoded.
386   *
387   * @return  An ASN.1 octet string that can be used as the request control
388   *          value.
389   */
390  @NotNull()
391  private static ASN1OctetString encodeValue(
392       @NotNull final PasswordUpdateBehaviorRequestControlProperties properties)
393  {
394    final ArrayList<ASN1Element> elements = new ArrayList<>(6);
395
396    if (properties.getIsSelfChange() != null)
397    {
398      elements.add(new ASN1Boolean(TYPE_IS_SELF_CHANGE,
399           properties.getIsSelfChange()));
400    }
401
402    if (properties.getAllowPreEncodedPassword() != null)
403    {
404      elements.add(new ASN1Boolean(TYPE_ALLOW_PRE_ENCODED_PASSWORD,
405           properties.getAllowPreEncodedPassword()));
406    }
407
408    if (properties.getSkipPasswordValidation() != null)
409    {
410      elements.add(new ASN1Boolean(TYPE_SKIP_PASSWORD_VALIDATION,
411           properties.getSkipPasswordValidation()));
412    }
413
414    if (properties.getIgnorePasswordHistory() != null)
415    {
416      elements.add(new ASN1Boolean(TYPE_IGNORE_PASSWORD_HISTORY,
417           properties.getIgnorePasswordHistory()));
418    }
419
420    if (properties.getIgnoreMinimumPasswordAge() != null)
421    {
422      elements.add(new ASN1Boolean(TYPE_IGNORE_MINIMUM_PASSWORD_AGE,
423           properties.getIgnoreMinimumPasswordAge()));
424    }
425
426    if (properties.getPasswordStorageScheme() != null)
427    {
428      elements.add(new ASN1OctetString(TYPE_PASSWORD_STORAGE_SCHEME,
429           properties.getPasswordStorageScheme()));
430    }
431
432    if (properties.getMustChangePassword() != null)
433    {
434      elements.add(new ASN1Boolean(TYPE_MUST_CHANGE_PASSWORD,
435           properties.getMustChangePassword()));
436    }
437
438    return new ASN1OctetString(new ASN1Sequence(elements).encode());
439  }
440
441
442
443  /**
444   * Indicates whether this control should override the server's automatic
445   * classification of the password update as a self change or an administrative
446   * reset, and if so, what the overridden value should be.
447   *
448   * @return  {@code Boolean.TRUE} if the server should treat the password
449   *          update as a self change, {@code Boolean.FALSE} if the server
450   *          should treat the password update as an administrative reset, or
451   *          {@code null} if the server should automatically determine whether
452   *          the password update is a self change or an administrative reset.
453   */
454  @Nullable()
455  public Boolean getIsSelfChange()
456  {
457    return isSelfChange;
458  }
459
460
461
462  /**
463   * Indicates whether this control should override the value of the
464   * {@code allow-pre-encoded-passwords} configuration property for the target
465   * user's password policy, and if so, what the overridden value should be.
466   *
467   * @return  {@code Boolean.TRUE} if the server should accept a pre-encoded
468   *          password in the password update even if the server's password
469   *          policy configuration would normally not permit this,
470   *          {@code Boolean.FALSE} if the server should reject a pre-encoded
471   *          password in the password update even if the server's password
472   *          policy configuration would normally accept it, or {@code null} if
473   *          the password policy configuration should be used to determine
474   *          whether to accept pre-encoded passwords.
475   */
476  @Nullable()
477  public Boolean getAllowPreEncodedPassword()
478  {
479    return allowPreEncodedPassword;
480  }
481
482
483
484  /**
485   * Indicates whether this control should override the server's normal behavior
486   * with regard to invoking password validators for any new passwords included
487   * in the password update, and if so, what the overridden behavior should be.
488   *
489   * @return  {@code Boolean.TRUE} if the server should skip invoking the
490   *          password validators configured in the target user's password
491   *          policy validators for any new passwords included in the password
492   *          update even if the server would normally perform password
493   *          validation, {@code Boolean.FALSE} if the server should invoke the
494   *          password validators even if it would normally skip them, or
495   *          {@code null} if the password policy configuration should be used
496   *          to determine whether to skip password validation.
497   */
498  @Nullable()
499  public Boolean getSkipPasswordValidation()
500  {
501    return skipPasswordValidation;
502  }
503
504
505
506  /**
507   * Indicates whether this control should override the server's normal behavior
508   * with regard to checking the password history for any new passwords included
509   * in the password update, and if so, what the overridden behavior should be.
510   *
511   * @return  {@code Boolean.TRUE} if the server should not check to see whether
512   *          any new password matches the current password or is in the user's
513   *          password history even if it would normally perform that check,
514   *          {@code Boolean.FALSE} if the server should check to see whether
515   *          any new password matches the current or previous password even if
516   *          it would normally not perform such a check, or {@code null} if the
517   *          password policy configuration should be used to determine whether
518   *          to ignore the password history.
519   */
520  @Nullable()
521  public Boolean getIgnorePasswordHistory()
522  {
523    return ignorePasswordHistory;
524  }
525
526
527
528  /**
529   * Indicates whether this control should override the server's normal behavior
530   * with regard to checking the minimum password age, and if so, what the
531   * overridden behavior should be.
532   *
533   * @return  {@code Boolean.TRUE} if the server should accept the password
534   *          change even if it has been less than the configured minimum
535   *          password age since the password was last changed,
536   *          {@code Boolean.FALSE} if the server should reject the password
537   *          change if it has been less than teh configured minimum password
538   *          age, or {@code null} if the password policy configuration should
539   *          be used to determine the appropriate behavior.
540   */
541  @Nullable()
542  public Boolean getIgnoreMinimumPasswordAge()
543  {
544    return ignoreMinimumPasswordAge;
545  }
546
547
548
549  /**
550   * Indicates whether this control should override the server's normal behavior
551   * with regard to selecting the password storage scheme to use to encode new
552   * password values, and if so, which password storage scheme should be used.
553   *
554   * @return  The name of the password storage scheme that should be used to
555   *          encode any new password values, or {@code null} if the target
556   *          user's password policy configuration should determine the
557   *          appropriate schemes for encoding new passwords.
558   */
559  @Nullable()
560  public String getPasswordStorageScheme()
561  {
562    return passwordStorageScheme;
563  }
564
565
566
567  /**
568   * Indicates whether this control should override the server's normal behavior
569   * with regard to requiring a password change, and if so, what that behavior
570   * should be.
571   *
572   * @return  {@code Boolean.TRUE} if the user will be required to change their
573   *          password before being allowed to perform any other operation,
574   *          {@code Boolean.FALSE} if the user will not be required to change
575   *          their password before being allowed to perform any other
576   *          operation, or {@code null} if the password policy configuration
577   *          should be used to control this behavior.
578   */
579  @Nullable()
580  public Boolean getMustChangePassword()
581  {
582    return mustChangePassword;
583  }
584
585
586
587  /**
588   * {@inheritDoc}
589   */
590  @Override()
591  @NotNull()
592  public String getControlName()
593  {
594    return INFO_PW_UPDATE_BEHAVIOR_REQ_CONTROL_NAME.get();
595  }
596
597
598
599  /**
600   * Retrieves a representation of this password update behavior request control
601   * as a JSON object.  The JSON object uses the following fields:
602   * <UL>
603   *   <LI>
604   *     {@code oid} -- A mandatory string field whose value is the object
605   *     identifier for this control.  For the password update behavior request
606   *     control, the OID is "1.3.6.1.4.1.30221.2.5.51".
607   *   </LI>
608   *   <LI>
609   *     {@code control-name} -- An optional string field whose value is a
610   *     human-readable name for this control.  This field is only intended for
611   *     descriptive purposes, and when decoding a control, the {@code oid}
612   *     field should be used to identify the type of control.
613   *   </LI>
614   *   <LI>
615   *     {@code criticality} -- A mandatory Boolean field used to indicate
616   *     whether this control is considered critical.
617   *   </LI>
618   *   <LI>
619   *     {@code value-base64} -- An optional string field whose value is a
620   *     base64-encoded representation of the raw value for this password update
621   *     behavior request control.  Exactly one of the {@code value-base64} and
622   *     {@code value-json} fields must be present.
623   *   </LI>
624   *   <LI>
625   *     {@code value-json} -- An optional JSON object field whose value is a
626   *     user-friendly representation of the value for this password update
627   *     behavior request control.  Exactly one of the {@code value-base64} and
628   *     {@code value-json} fields must be present, and if the
629   *     {@code value-json} field is used, then it will use the following
630   *     fields:
631   *     <UL>
632   *       <LI>
633   *         {@code is-self-change} -- An optional Boolean field that may be
634   *         used to explicitly indicate whether the server should treat the
635   *         password update as a self change or an administrative reset.
636   *       </LI>
637   *       <LI>
638   *         {@code allow-pre-encoded-password} -- An optional Boolean field
639   *         that may be used to explicitly indicate whether the server should
640   *         allow the new password to be provided in pre-encoded form.
641   *       </LI>
642   *       <LI>
643   *         {@code skip-password-validation} -- An optional Boolean field that
644   *         may be used to explicitly indicate whether the server should skip
645   *         the password validation processing that it may otherwise perform
646   *         for the new password.
647   *       </LI>
648   *       <LI>
649   *         {@code ignore-password-history} -- An optional Boolean field that
650   *         may be used to explicitly indicate whether the server should ignore
651   *         the user's password history when determining whether to accept the
652   *         new password.
653   *       </LI>
654   *       <LI>
655   *         {@code ignore-minimum-password-age} -- An optional Boolean field
656   *         that may be used to explicitly indicate whether the server should
657   *         ignore any minimum password age constraints that may otherwise be
658   *         in place for the user.
659   *       </LI>
660   *       <LI>
661   *         {@code password-storage-scheme} -- An optional string field whose
662   *         value is the name of the password storage scheme that the server
663   *         should use when encoding the new password.
664   *       </LI>
665   *       <LI>
666   *         {@code must-change-password} -- An optional Boolean field that may
667   *         be used to explicitly indicate whether the server should require
668   *         the user to change their password before they will be allowed to
669   *         request any other operations.
670   *       </LI>
671   *     </UL>
672   *   </LI>
673   * </UL>
674   *
675   * @return  A JSON object that contains a representation of this control.
676   */
677  @Override()
678  @NotNull()
679  public JSONObject toJSONControl()
680  {
681    final Map<String,JSONValue> valueFields = new LinkedHashMap<>();
682
683    if (isSelfChange != null)
684    {
685      valueFields.put(JSON_FIELD_IS_SELF_CHANGE, new JSONBoolean(isSelfChange));
686    }
687
688    if (allowPreEncodedPassword != null)
689    {
690      valueFields.put(JSON_FIELD_ALLOW_PRE_ENCODED_PASSWORD,
691           new JSONBoolean(allowPreEncodedPassword));
692    }
693
694    if (skipPasswordValidation != null)
695    {
696      valueFields.put(JSON_FIELD_SKIP_PASSWORD_VALIDATION,
697           new JSONBoolean(skipPasswordValidation));
698    }
699
700    if (ignorePasswordHistory != null)
701    {
702      valueFields.put(JSON_FIELD_IGNORE_PASSWORD_HISTORY,
703           new JSONBoolean(ignorePasswordHistory));
704    }
705
706    if (ignoreMinimumPasswordAge != null)
707    {
708      valueFields.put(JSON_FIELD_IGNORE_MINIMUM_PASSWORD_AGE,
709           new JSONBoolean(ignoreMinimumPasswordAge));
710    }
711
712    if (passwordStorageScheme != null)
713    {
714      valueFields.put(JSON_FIELD_PASSWORD_STORAGE_SCHEME,
715           new JSONString(passwordStorageScheme));
716    }
717
718    if (mustChangePassword != null)
719    {
720      valueFields.put(JSON_FIELD_MUST_CHANGE_PASSWORD,
721           new JSONBoolean(mustChangePassword));
722    }
723
724    return new JSONObject(
725         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
726              PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID),
727         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
728              INFO_PW_UPDATE_BEHAVIOR_REQ_CONTROL_NAME.get()),
729         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
730              isCritical()),
731         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
732              new JSONObject(valueFields)));
733  }
734
735
736
737  /**
738   * Attempts to decode the provided object as a JSON representation of a
739   * password update behavior control.
740   *
741   * @param  controlObject  The JSON object to be decoded.  It must not be
742   *                        {@code null}.
743   * @param  strict         Indicates whether to use strict mode when decoding
744   *                        the provided JSON object.  If this is {@code true},
745   *                        then this method will throw an exception if the
746   *                        provided JSON object contains any unrecognized
747   *                        fields.  If this is {@code false}, then unrecognized
748   *                        fields will be ignored.
749   *
750   * @return  The password update behavior request control that was decoded from
751   *          the provided JSON object.
752   *
753   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
754   *                         valid password update behavior request control.
755   */
756  @NotNull()
757  public static PasswordUpdateBehaviorRequestControl decodeJSONControl(
758              @NotNull final JSONObject controlObject,
759              final boolean strict)
760         throws LDAPException
761  {
762    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
763         controlObject, strict, true, true);
764
765    final ASN1OctetString rawValue = jsonControl.getRawValue();
766    if (rawValue != null)
767    {
768      return new PasswordUpdateBehaviorRequestControl(new Control(
769           jsonControl.getOID(), jsonControl.getCriticality(), rawValue));
770    }
771
772
773    final PasswordUpdateBehaviorRequestControlProperties properties =
774         new PasswordUpdateBehaviorRequestControlProperties();
775    final JSONObject valueObject = jsonControl.getValueObject();
776
777    final Boolean isSelfChange =
778         valueObject.getFieldAsBoolean(JSON_FIELD_IS_SELF_CHANGE);
779    if (isSelfChange != null)
780    {
781      properties.setIsSelfChange(isSelfChange);
782    }
783
784    final Boolean allowPreEncodedPassword =
785         valueObject.getFieldAsBoolean(JSON_FIELD_ALLOW_PRE_ENCODED_PASSWORD);
786    if (allowPreEncodedPassword != null)
787    {
788      properties.setAllowPreEncodedPassword(allowPreEncodedPassword);
789    }
790
791    final Boolean skipPasswordValidation =
792         valueObject.getFieldAsBoolean(JSON_FIELD_SKIP_PASSWORD_VALIDATION);
793    if (skipPasswordValidation != null)
794    {
795      properties.setSkipPasswordValidation(skipPasswordValidation);
796    }
797
798    final Boolean ignorePasswordHistory =
799         valueObject.getFieldAsBoolean(JSON_FIELD_IGNORE_PASSWORD_HISTORY);
800    if (ignorePasswordHistory != null)
801    {
802      properties.setIgnorePasswordHistory(ignorePasswordHistory);
803    }
804
805    final Boolean ignoreMinimumPasswordAge =
806         valueObject.getFieldAsBoolean(JSON_FIELD_IGNORE_MINIMUM_PASSWORD_AGE);
807    if (ignoreMinimumPasswordAge != null)
808    {
809      properties.setIgnoreMinimumPasswordAge(ignoreMinimumPasswordAge);
810    }
811
812    final String passwordStorageScheme =
813         valueObject.getFieldAsString(JSON_FIELD_PASSWORD_STORAGE_SCHEME);
814    if (passwordStorageScheme != null)
815    {
816      properties.setPasswordStorageScheme(passwordStorageScheme);
817    }
818
819    final Boolean mustChangePassword =
820         valueObject.getFieldAsBoolean(JSON_FIELD_MUST_CHANGE_PASSWORD);
821    if (mustChangePassword != null)
822    {
823      properties.setMustChangePassword(mustChangePassword);
824    }
825
826
827    if (strict)
828    {
829      final List<String> unrecognizedFields =
830           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
831                valueObject, JSON_FIELD_IS_SELF_CHANGE,
832                JSON_FIELD_ALLOW_PRE_ENCODED_PASSWORD,
833                JSON_FIELD_SKIP_PASSWORD_VALIDATION,
834                JSON_FIELD_IGNORE_PASSWORD_HISTORY,
835                JSON_FIELD_IGNORE_MINIMUM_PASSWORD_AGE,
836                JSON_FIELD_PASSWORD_STORAGE_SCHEME,
837                JSON_FIELD_MUST_CHANGE_PASSWORD);
838      if (! unrecognizedFields.isEmpty())
839      {
840        throw new LDAPException(ResultCode.DECODING_ERROR,
841             ERR_PW_UPDATE_BEHAVIOR_REQ_JSON_UNRECOGNIZED_FIELD.get(
842                  controlObject.toSingleLineString(),
843                  unrecognizedFields.get(0)));
844      }
845    }
846
847
848    return new PasswordUpdateBehaviorRequestControl(properties,
849         jsonControl.getCriticality());
850  }
851
852
853
854  /**
855   * {@inheritDoc}
856   */
857  @Override()
858  public void toString(@NotNull final StringBuilder buffer)
859  {
860    buffer.append("PasswordUpdateBehaviorRequestControl(oid='");
861    buffer.append(PASSWORD_UPDATE_BEHAVIOR_REQUEST_OID);
862    buffer.append("', isCritical=");
863    buffer.append(isCritical());
864    buffer.append(", properties=");
865    new PasswordUpdateBehaviorRequestControlProperties(this).toString(buffer);
866    buffer.append(')');
867  }
868}