001/* 002 * Copyright 2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 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.ASN1Long; 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.asn1.ASN1Sequence; 032import com.unboundid.ldap.sdk.Control; 033import com.unboundid.ldap.sdk.DecodeableControl; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.LDAPResult; 036import com.unboundid.ldap.sdk.ResultCode; 037import com.unboundid.util.Debug; 038import com.unboundid.util.NotMutable; 039import com.unboundid.util.StaticUtils; 040import com.unboundid.util.ThreadSafety; 041import com.unboundid.util.ThreadSafetyLevel; 042 043import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 044 045 046 047/** 048 * This class provides a response control that may be used to convey the 049 * password (and other associated information) generated in response to a 050 * {@link GeneratePasswordRequestControl}. 051 * <BR> 052 * <BLOCKQUOTE> 053 * <B>NOTE:</B> This class, and other classes within the 054 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 055 * supported for use against Ping Identity, UnboundID, and 056 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 057 * for proprietary functionality or for external specifications that are not 058 * considered stable or mature enough to be guaranteed to work in an 059 * interoperable way with other types of LDAP servers. 060 * </BLOCKQUOTE> 061 * <BR> 062 * This control has an OID of "1.3.6.1.4.1.30221.2.5.59", a criticality of 063 * false, and a value with the following encoding: 064 * <PRE> 065 * GeneratePasswordResponse ::= SEQUENCE { 066 * generatedPassword OCTET STRING, 067 * mustChangePassword BOOLEAN, 068 * secondsUntilExpiration [0] INTEGER OPTIONAL, 069 * ... } 070 * </PRE> 071 * 072 * @see GeneratePasswordRequestControl 073 */ 074@NotMutable() 075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 076public final class GeneratePasswordResponseControl 077 extends Control 078 implements DecodeableControl 079{ 080 /** 081 * The OID (1.3.6.1.4.1.30221.2.5.59) for the generate password response 082 * control. 083 */ 084 public static final String GENERATE_PASSWORD_RESPONSE_OID = 085 "1.3.6.1.4.1.30221.2.5.59"; 086 087 088 089 /** 090 * The BER type for the {@code secondsUntilExpiration} element. 091 */ 092 private static final byte TYPE_SECONDS_UNTIL_EXPIRATION = (byte) 0x80; 093 094 095 096 /** 097 * The serial version UID for this serializable class. 098 */ 099 private static final long serialVersionUID = 7542512192838228238L; 100 101 102 103 // The generated password included in the control. 104 private final ASN1OctetString generatedPassword; 105 106 // Indicates whether the user will be required to choose a new password the 107 // first time they authenticate. 108 private final boolean mustChangePassword; 109 110 // The number of seconds until the new password will expire. 111 private final Long secondsUntilExpiration; 112 113 114 115 /** 116 * Creates a new empty control instance that is intended to be used only for 117 * decoding controls via the {@code DecodeableControl} interface. 118 */ 119 GeneratePasswordResponseControl() 120 { 121 generatedPassword = null; 122 mustChangePassword = false; 123 secondsUntilExpiration = null; 124 } 125 126 127 128 /** 129 * Creates a new generate password response control with the provided 130 * information. 131 * 132 * @param generatedPassword The password generated by the server. It 133 * must not be {@code null}. 134 * @param mustChangePassword Indicates whether the user will be required 135 * to choose a new password the first time 136 * they authenticate. 137 * @param secondsUntilExpiration The number of seconds until the new 138 * password will expire. It may be 139 * {@code null} if the new password will not 140 * expire. 141 */ 142 public GeneratePasswordResponseControl(final String generatedPassword, 143 final boolean mustChangePassword, 144 final Long secondsUntilExpiration) 145 { 146 this(new ASN1OctetString(generatedPassword), mustChangePassword, 147 secondsUntilExpiration); 148 } 149 150 151 152 /** 153 * Creates a new generate password response control with the provided 154 * information. 155 * 156 * @param generatedPassword The password generated by the server. It 157 * must not be {@code null}. 158 * @param mustChangePassword Indicates whether the user will be required 159 * to choose a new password the first time 160 * they authenticate. 161 * @param secondsUntilExpiration The number of seconds until the new 162 * password will expire. It may be 163 * {@code null} if the new password will not 164 * expire. 165 */ 166 public GeneratePasswordResponseControl(final byte[] generatedPassword, 167 final boolean mustChangePassword, 168 final Long secondsUntilExpiration) 169 { 170 this(new ASN1OctetString(generatedPassword), mustChangePassword, 171 secondsUntilExpiration); 172 } 173 174 175 176 /** 177 * Creates a new generate password response control with the provided 178 * information. 179 * 180 * @param generatedPassword The password generated by the server. It 181 * must not be {@code null}. 182 * @param mustChangePassword Indicates whether the user will be required 183 * to choose a new password the first time 184 * they authenticate. 185 * @param secondsUntilExpiration The number of seconds until the new 186 * password will expire. It may be 187 * {@code null} if the new password will not 188 * expire. 189 */ 190 private GeneratePasswordResponseControl( 191 final ASN1OctetString generatedPassword, 192 final boolean mustChangePassword, 193 final Long secondsUntilExpiration) 194 { 195 super(GENERATE_PASSWORD_RESPONSE_OID, false, 196 encodeValue(generatedPassword, mustChangePassword, 197 secondsUntilExpiration)); 198 199 this.generatedPassword = generatedPassword; 200 this.mustChangePassword = mustChangePassword; 201 this.secondsUntilExpiration = secondsUntilExpiration; 202 } 203 204 205 206 /** 207 * Creates a new generate password response control with the provided 208 * information. 209 * 210 * @param oid The OID for the control. 211 * @param isCritical Indicates whether the control should be marked 212 * critical. 213 * @param value The encoded value for the control. This may be 214 * {@code null} if no value was provided. 215 * 216 * @throws LDAPException If the provided control cannot be decoded as a 217 * generate password response control. 218 */ 219 public GeneratePasswordResponseControl(final String oid, 220 final boolean isCritical, 221 final ASN1OctetString value) 222 throws LDAPException 223 { 224 super(oid, isCritical, value); 225 226 if (value == null) 227 { 228 throw new LDAPException(ResultCode.DECODING_ERROR, 229 ERR_GENERATE_PASSWORD_RESPONSE_NO_VALUE.get()); 230 } 231 232 try 233 { 234 final ASN1Element valElement = ASN1Element.decode(value.getValue()); 235 final ASN1Element[] elements = 236 ASN1Sequence.decodeAsSequence(valElement).elements(); 237 generatedPassword = ASN1OctetString.decodeAsOctetString(elements[0]); 238 mustChangePassword = 239 ASN1Boolean.decodeAsBoolean(elements[1]).booleanValue(); 240 241 Long secsUntilExp = null; 242 for (int i=2; i < elements.length; i++) 243 { 244 final ASN1Element e = elements[i]; 245 switch (e.getType()) 246 { 247 case TYPE_SECONDS_UNTIL_EXPIRATION: 248 secsUntilExp = ASN1Long.decodeAsLong(e).longValue(); 249 break; 250 default: 251 // This is a field we don't currently recognize but might be defined 252 // in the future. 253 break; 254 } 255 } 256 257 secondsUntilExpiration = secsUntilExp; 258 } 259 catch (final Exception e) 260 { 261 Debug.debugException(e); 262 throw new LDAPException(ResultCode.DECODING_ERROR, 263 ERR_GENERATE_PASSWORD_RESPONSE_CANNOT_DECODE_VALUE.get( 264 StaticUtils.getExceptionMessage(e)), 265 e); 266 } 267 } 268 269 270 271 /** 272 * {@inheritDoc} 273 */ 274 @Override() 275 public GeneratePasswordResponseControl decodeControl(final String oid, 276 final boolean isCritical, 277 final ASN1OctetString value) 278 throws LDAPException 279 { 280 return new GeneratePasswordResponseControl(oid, isCritical, value); 281 } 282 283 284 285 /** 286 * Extracts a generate password response control from the provided result. 287 * 288 * @param result The result from which to retrieve the generate password 289 * response control. 290 * 291 * @return The generate password response control contained in the provided 292 * result, or {@code null} if the result did not contain a generate 293 * password response control. 294 * 295 * @throws LDAPException If a problem is encountered while attempting to 296 * decode the generate password response control 297 * contained in the provided result. 298 */ 299 public static GeneratePasswordResponseControl get(final LDAPResult result) 300 throws LDAPException 301 { 302 final Control c = result.getResponseControl(GENERATE_PASSWORD_RESPONSE_OID); 303 if (c == null) 304 { 305 return null; 306 } 307 308 if (c instanceof GeneratePasswordResponseControl) 309 { 310 return (GeneratePasswordResponseControl) c; 311 } 312 else 313 { 314 return new GeneratePasswordResponseControl(c.getOID(), c.isCritical(), 315 c.getValue()); 316 } 317 } 318 319 320 321 /** 322 * Encodes the provided information appropriately for use as the value of this 323 * control. 324 * 325 * @param generatedPassword The password generated by the server. It 326 * must not be {@code null}. 327 * @param mustChangePassword Indicates whether the user will be required 328 * to choose a new password the first time 329 * they authenticate. 330 * @param secondsUntilExpiration The number of seconds until the new 331 * password will expire. It may be 332 * {@code null} if the new password will not 333 * expire. 334 * 335 * @return The ASN.1 octet string suitable for use as the control value. 336 */ 337 private static ASN1OctetString encodeValue( 338 final ASN1OctetString generatedPassword, 339 final boolean mustChangePassword, 340 final Long secondsUntilExpiration) 341 { 342 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 343 elements.add(generatedPassword); 344 elements.add(mustChangePassword 345 ? ASN1Boolean.UNIVERSAL_BOOLEAN_TRUE_ELEMENT 346 : ASN1Boolean.UNIVERSAL_BOOLEAN_FALSE_ELEMENT); 347 348 if (secondsUntilExpiration != null) 349 { 350 elements.add(new ASN1Long(TYPE_SECONDS_UNTIL_EXPIRATION, 351 secondsUntilExpiration)); 352 } 353 354 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 355 } 356 357 358 359 /** 360 * Retrieves the password that was generated by the server. 361 * 362 * @return The password that was generated by the server. 363 */ 364 public ASN1OctetString getGeneratedPassword() 365 { 366 return generatedPassword; 367 } 368 369 370 371 /** 372 * Retrieves a string representation of the password that was generated by the 373 * server. 374 * 375 * @return A string representation of the password that was generated by the 376 * server. 377 */ 378 public String getGeneratedPasswordString() 379 { 380 return generatedPassword.stringValue(); 381 } 382 383 384 385 /** 386 * Retrieves the bytes that comprise the password that was generated by the 387 * server. 388 * 389 * @return The bytes that comprise the password that was generated by the 390 * server. 391 */ 392 public byte[] getGeneratedPasswordBytes() 393 { 394 return generatedPassword.getValue(); 395 } 396 397 398 399 /** 400 * Indicates whether the user will be required to change their password the 401 * first time they authenticate. 402 * 403 * @return {@code true} if the user will be required to change their password 404 * the first time they authenticate, or {@code false} if not. 405 */ 406 public boolean mustChangePassword() 407 { 408 return mustChangePassword; 409 } 410 411 412 413 /** 414 * Retrieves the length of time, in seconds, until the generated password will 415 * expire. 416 * 417 * @return The length of time, in seconds, until the generated password will 418 * expire, or {@code null} if this is not available (e.g., because 419 * the generated password will not expire). 420 */ 421 public Long getSecondsUntilExpiration() 422 { 423 return secondsUntilExpiration; 424 } 425 426 427 428 /** 429 * {@inheritDoc} 430 */ 431 @Override() 432 public String getControlName() 433 { 434 return INFO_CONTROL_NAME_GENERATE_PASSWORD_RESPONSE.get(); 435 } 436 437 438 439 /** 440 * {@inheritDoc} 441 */ 442 @Override() 443 public void toString(final StringBuilder buffer) 444 { 445 buffer.append("GeneratePasswordResponseControl(mustChangePassword="); 446 buffer.append(mustChangePassword); 447 448 if (secondsUntilExpiration != null) 449 { 450 buffer.append(", secondsUntilExpiration="); 451 buffer.append(secondsUntilExpiration); 452 } 453 454 buffer.append(')'); 455 } 456}