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