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