001/* 002 * Copyright 2017-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2018 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 Alcatel-Lucent 8661 054 * server products. These classes provide support for proprietary 055 * functionality or for external specifications that are not considered stable 056 * or mature enough to be guaranteed to work in an interoperable way with 057 * 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}