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