001 /* 002 * Copyright 2007-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 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 */ 021 package com.unboundid.ldap.sdk.unboundidds.controls; 022 023 024 025 import java.util.ArrayList; 026 027 import com.unboundid.asn1.ASN1Element; 028 import com.unboundid.asn1.ASN1Enumerated; 029 import com.unboundid.asn1.ASN1Exception; 030 import com.unboundid.asn1.ASN1Integer; 031 import com.unboundid.asn1.ASN1OctetString; 032 import com.unboundid.asn1.ASN1Sequence; 033 import com.unboundid.ldap.sdk.Control; 034 import com.unboundid.ldap.sdk.DecodeableControl; 035 import com.unboundid.ldap.sdk.LDAPException; 036 import com.unboundid.ldap.sdk.LDAPResult; 037 import com.unboundid.ldap.sdk.ResultCode; 038 import com.unboundid.util.NotMutable; 039 import com.unboundid.util.ThreadSafety; 040 import com.unboundid.util.ThreadSafetyLevel; 041 042 import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 043 import static com.unboundid.util.Debug.*; 044 import static com.unboundid.util.StaticUtils.*; 045 046 047 048 /** 049 * <BLOCKQUOTE> 050 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 051 * LDAP SDK for Java. It is not available for use in applications that 052 * include only the Standard Edition of the LDAP SDK, and is not supported for 053 * use in conjunction with non-UnboundID products. 054 * </BLOCKQUOTE> 055 * This class provides an implementation of the password policy response control 056 * as described in draft-behera-ldap-password-policy. It may be used to provide 057 * information related to a user's password policy. It may include at most one 058 * warning from the set of {@link PasswordPolicyWarningType} values and at most 059 * one error from the set of {@link PasswordPolicyErrorType} values. See the 060 * documentation for those classes for more information on the information that 061 * may be included. See the {@link PasswordPolicyRequestControl} documentation 062 * for an example that demonstrates the use of the password policy request and 063 * response controls. 064 */ 065 @NotMutable() 066 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 067 public final class PasswordPolicyResponseControl 068 extends Control 069 implements DecodeableControl 070 { 071 /** 072 * The OID (1.3.6.1.4.1.42.2.27.8.5.1) for the password policy response 073 * control. 074 */ 075 public static final String PASSWORD_POLICY_RESPONSE_OID = 076 "1.3.6.1.4.1.42.2.27.8.5.1"; 077 078 079 080 /** 081 * The BER type for the password policy warning element. 082 */ 083 private static final byte TYPE_WARNING = (byte) 0xA0; 084 085 086 087 /** 088 * The BER type for the password policy error element. 089 */ 090 private static final byte TYPE_ERROR = (byte) 0x81; 091 092 093 094 /** 095 * The BER type for the "time before expiration" warning element. 096 */ 097 private static final byte TYPE_TIME_BEFORE_EXPIRATION = (byte) 0x80; 098 099 100 101 /** 102 * The BER type for the "grace logins remaining" warning element. 103 */ 104 private static final byte TYPE_GRACE_LOGINS_REMAINING = (byte) 0x81; 105 106 107 108 /** 109 * The serial version UID for this serializable class. 110 */ 111 private static final long serialVersionUID = 1835830253434331833L; 112 113 114 115 // The password policy warning value, if applicable. 116 private final int warningValue; 117 118 // The password policy error type, if applicable. 119 private final PasswordPolicyErrorType errorType; 120 121 // The password policy warning type, if applicable. 122 private final PasswordPolicyWarningType warningType; 123 124 125 126 /** 127 * Creates a new empty control instance that is intended to be used only for 128 * decoding controls via the {@code DecodeableControl} interface. 129 */ 130 PasswordPolicyResponseControl() 131 { 132 warningType = null; 133 errorType = null; 134 warningValue = -1; 135 } 136 137 138 139 /** 140 * Creates a new password policy response control with the provided 141 * information. It will not be critical. 142 * 143 * @param warningType The password policy warning type for this response 144 * control, or {@code null} if there should be no 145 * warning type. 146 * @param warningValue The value for the password policy warning type, or -1 147 * if there is no warning type. 148 * @param errorType The password policy error type for this response 149 * control, or {@code null} if there should be no error 150 * type. 151 */ 152 public PasswordPolicyResponseControl( 153 final PasswordPolicyWarningType warningType, 154 final int warningValue, final PasswordPolicyErrorType errorType) 155 { 156 this(warningType, warningValue, errorType, false); 157 } 158 159 160 161 /** 162 * Creates a new password policy response control with the provided 163 * information. 164 * 165 * @param warningType The password policy warning type for this response 166 * control, or {@code null} if there should be no 167 * warning type. 168 * @param warningValue The value for the password policy warning type, or -1 169 * if there is no warning type. 170 * @param errorType The password policy error type for this response 171 * control, or {@code null} if there should be no error 172 * type. 173 * @param isCritical Indicates whether this control should be marked 174 * critical. 175 */ 176 public PasswordPolicyResponseControl( 177 final PasswordPolicyWarningType warningType, 178 final int warningValue, final PasswordPolicyErrorType errorType, 179 final boolean isCritical) 180 { 181 super(PASSWORD_POLICY_RESPONSE_OID, isCritical, 182 encodeValue(warningType, warningValue, errorType)); 183 184 this.warningType = warningType; 185 this.errorType = errorType; 186 187 if (warningType == null) 188 { 189 this.warningValue = -1; 190 } 191 else 192 { 193 this.warningValue = warningValue; 194 } 195 } 196 197 198 199 /** 200 * Creates a new password policy response control with the provided 201 * information. 202 * 203 * @param oid The OID for the control. 204 * @param isCritical Indicates whether the control should be marked 205 * critical. 206 * @param value The encoded value for the control. This may be 207 * {@code null} if no value was provided. 208 * 209 * @throws LDAPException If the provided control cannot be decoded as a 210 * password policy response control. 211 */ 212 public PasswordPolicyResponseControl(final String oid, 213 final boolean isCritical, 214 final ASN1OctetString value) 215 throws LDAPException 216 { 217 super(oid, isCritical, value); 218 219 if (value == null) 220 { 221 throw new LDAPException(ResultCode.DECODING_ERROR, 222 ERR_PWP_RESPONSE_NO_VALUE.get()); 223 } 224 225 final ASN1Sequence valueSequence; 226 try 227 { 228 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 229 valueSequence = ASN1Sequence.decodeAsSequence(valueElement); 230 } 231 catch (ASN1Exception ae) 232 { 233 debugException(ae); 234 throw new LDAPException(ResultCode.DECODING_ERROR, 235 ERR_PWP_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae); 236 } 237 238 final ASN1Element[] valueElements = valueSequence.elements(); 239 if (valueElements.length > 2) 240 { 241 throw new LDAPException(ResultCode.DECODING_ERROR, 242 ERR_PWP_RESPONSE_INVALID_ELEMENT_COUNT.get( 243 valueElements.length)); 244 } 245 246 int wv = -1; 247 PasswordPolicyErrorType et = null; 248 PasswordPolicyWarningType wt = null; 249 for (final ASN1Element e : valueElements) 250 { 251 switch (e.getType()) 252 { 253 case TYPE_WARNING: 254 if (wt == null) 255 { 256 try 257 { 258 final ASN1Element warningElement = 259 ASN1Element.decode(e.getValue()); 260 wv = ASN1Integer.decodeAsInteger(warningElement).intValue(); 261 switch (warningElement.getType()) 262 { 263 case TYPE_TIME_BEFORE_EXPIRATION: 264 wt = PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION; 265 break; 266 267 case TYPE_GRACE_LOGINS_REMAINING: 268 wt = PasswordPolicyWarningType.GRACE_LOGINS_REMAINING; 269 break; 270 271 default: 272 throw new LDAPException(ResultCode.DECODING_ERROR, 273 ERR_PWP_RESPONSE_INVALID_WARNING_TYPE.get( 274 toHex(warningElement.getType()))); 275 } 276 } 277 catch (ASN1Exception ae) 278 { 279 debugException(ae); 280 throw new LDAPException(ResultCode.DECODING_ERROR, 281 ERR_PWP_RESPONSE_CANNOT_DECODE_WARNING.get(ae), 282 ae); 283 } 284 } 285 else 286 { 287 throw new LDAPException(ResultCode.DECODING_ERROR, 288 ERR_PWP_RESPONSE_MULTIPLE_WARNING.get()); 289 } 290 break; 291 292 case TYPE_ERROR: 293 if (et == null) 294 { 295 try 296 { 297 final ASN1Enumerated errorElement = 298 ASN1Enumerated.decodeAsEnumerated(e); 299 et = PasswordPolicyErrorType.valueOf(errorElement.intValue()); 300 if (et == null) 301 { 302 throw new LDAPException(ResultCode.DECODING_ERROR, 303 ERR_PWP_RESPONSE_INVALID_ERROR_TYPE.get( 304 errorElement.intValue())); 305 } 306 } 307 catch (ASN1Exception ae) 308 { 309 debugException(ae); 310 throw new LDAPException(ResultCode.DECODING_ERROR, 311 ERR_PWP_RESPONSE_CANNOT_DECODE_ERROR.get(ae), ae); 312 } 313 } 314 else 315 { 316 throw new LDAPException(ResultCode.DECODING_ERROR, 317 ERR_PWP_RESPONSE_MULTIPLE_ERROR.get()); 318 } 319 break; 320 321 default: 322 throw new LDAPException(ResultCode.DECODING_ERROR, 323 ERR_PWP_RESPONSE_INVALID_TYPE.get( 324 toHex(e.getType()))); 325 } 326 } 327 328 warningType = wt; 329 warningValue = wv; 330 errorType = et; 331 } 332 333 334 335 /** 336 * {@inheritDoc} 337 */ 338 public PasswordPolicyResponseControl 339 decodeControl(final String oid, final boolean isCritical, 340 final ASN1OctetString value) 341 throws LDAPException 342 { 343 return new PasswordPolicyResponseControl(oid, isCritical, value); 344 } 345 346 347 348 /** 349 * Extracts a password policy response control from the provided result. 350 * 351 * @param result The result from which to retrieve the password policy 352 * response control. 353 * 354 * @return The password policy response control contained in the provided 355 * result, or {@code null} if the result did not contain a password 356 * policy response control. 357 * 358 * @throws LDAPException If a problem is encountered while attempting to 359 * decode the password policy response control 360 * contained in the provided result. 361 */ 362 public static PasswordPolicyResponseControl get(final LDAPResult result) 363 throws LDAPException 364 { 365 final Control c = result.getResponseControl(PASSWORD_POLICY_RESPONSE_OID); 366 if (c == null) 367 { 368 return null; 369 } 370 371 if (c instanceof PasswordPolicyResponseControl) 372 { 373 return (PasswordPolicyResponseControl) c; 374 } 375 else 376 { 377 return new PasswordPolicyResponseControl(c.getOID(), c.isCritical(), 378 c.getValue()); 379 } 380 } 381 382 383 384 /** 385 * Encodes the provided information as appropriate for use as the value of a 386 * password policy response control. 387 * 388 * @param warningType The warning type to use for the warning element, or 389 * {@code null} if there is not to be a warning element. 390 * @param warningValue The value to use for the warning element. 391 * @param errorType The error type to use for the error element, or 392 * {@code null} if there is not to be an error element. 393 * 394 * @return The ASN.1 octet string containing the encoded control value. 395 */ 396 private static ASN1OctetString 397 encodeValue(final PasswordPolicyWarningType warningType, 398 final int warningValue, 399 final PasswordPolicyErrorType errorType) 400 { 401 final ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(2); 402 403 if (warningType != null) 404 { 405 switch (warningType) 406 { 407 case TIME_BEFORE_EXPIRATION: 408 valueElements.add(new ASN1Element(TYPE_WARNING, 409 new ASN1Integer(TYPE_TIME_BEFORE_EXPIRATION, 410 warningValue).encode())); 411 break; 412 413 case GRACE_LOGINS_REMAINING: 414 valueElements.add(new ASN1Element(TYPE_WARNING, 415 new ASN1Integer(TYPE_GRACE_LOGINS_REMAINING, 416 warningValue).encode())); 417 break; 418 } 419 } 420 421 if (errorType != null) 422 { 423 valueElements.add(new ASN1Enumerated(TYPE_ERROR, errorType.intValue())); 424 } 425 426 return new ASN1OctetString(new ASN1Sequence(valueElements).encode()); 427 } 428 429 430 431 /** 432 * Retrieves the warning type for this password policy response control, if 433 * available. 434 * 435 * @return The warning type for this password policy response control, or 436 * {@code null} if there is no warning type. 437 */ 438 public PasswordPolicyWarningType getWarningType() 439 { 440 return warningType; 441 } 442 443 444 445 /** 446 * Retrieves the warning value for this password policy response control, if 447 * available. 448 * 449 * @return The warning value for this password policy response control, or -1 450 * if there is no warning type. 451 */ 452 public int getWarningValue() 453 { 454 return warningValue; 455 } 456 457 458 459 /** 460 * Retrieves the error type for this password policy response control, if 461 * available. 462 * 463 * @return The error type for this password policy response control, or 464 * {@code null} if there is no error type. 465 */ 466 public PasswordPolicyErrorType getErrorType() 467 { 468 return errorType; 469 } 470 471 472 473 /** 474 * {@inheritDoc} 475 */ 476 @Override() 477 public String getControlName() 478 { 479 return INFO_CONTROL_NAME_PW_POLICY_RESPONSE.get(); 480 } 481 482 483 484 /** 485 * {@inheritDoc} 486 */ 487 @Override() 488 public void toString(final StringBuilder buffer) 489 { 490 boolean elementAdded = false; 491 492 buffer.append("PasswordPolicyResponseControl("); 493 494 if (warningType != null) 495 { 496 buffer.append("warningType='"); 497 buffer.append(warningType.getName()); 498 buffer.append("', warningValue="); 499 buffer.append(warningValue); 500 elementAdded = true; 501 } 502 503 if (errorType != null) 504 { 505 if (elementAdded) 506 { 507 buffer.append(", "); 508 } 509 510 buffer.append("errorType='"); 511 buffer.append(errorType.getName()); 512 buffer.append('\''); 513 elementAdded = true; 514 } 515 516 if (elementAdded) 517 { 518 buffer.append(", "); 519 } 520 521 buffer.append("isCritical="); 522 buffer.append(isCritical()); 523 buffer.append(')'); 524 } 525 }