001/* 002 * Copyright 2008-2017 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2017 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.extensions; 022 023 024 025import java.text.ParseException; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.Date; 029import java.util.Iterator; 030import java.util.LinkedHashMap; 031import java.util.Map; 032import java.util.NoSuchElementException; 033 034import com.unboundid.asn1.ASN1Element; 035import com.unboundid.asn1.ASN1OctetString; 036import com.unboundid.asn1.ASN1Sequence; 037import com.unboundid.ldap.sdk.Control; 038import com.unboundid.ldap.sdk.ExtendedResult; 039import com.unboundid.ldap.sdk.LDAPException; 040import com.unboundid.ldap.sdk.ResultCode; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.ThreadSafety; 043import com.unboundid.util.ThreadSafetyLevel; 044 045import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 046import static com.unboundid.util.Debug.*; 047 048 049 050/** 051 * This class implements a data structure for storing the information from an 052 * extended result for the password policy state extended request as used in the 053 * Ping Identity, UnboundID, or Alcatel-Lucent 8661 Directory Server. It is 054 * able to decode a generic extended result to obtain the user DN and 055 * operations. See the documentation in the 056 * {@link PasswordPolicyStateExtendedRequest} class for an example that 057 * demonstrates the use of the password policy state extended operation. 058 * <BR> 059 * <BLOCKQUOTE> 060 * <B>NOTE:</B> This class, and other classes within the 061 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 062 * supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661 063 * server products. These classes provide support for proprietary 064 * functionality or for external specifications that are not considered stable 065 * or mature enough to be guaranteed to work in an interoperable way with 066 * other types of LDAP servers. 067 * </BLOCKQUOTE> 068 */ 069@NotMutable() 070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 071public final class PasswordPolicyStateExtendedResult 072 extends ExtendedResult 073{ 074 /** 075 * The serial version UID for this serializable class. 076 */ 077 private static final long serialVersionUID = 7140468768443263344L; 078 079 080 081 // A map containing all of the response operations, indexed by operation type. 082 private final Map<Integer,PasswordPolicyStateOperation> operations; 083 084 // The user DN from the response. 085 private final String userDN; 086 087 088 089 /** 090 * Creates a new password policy state extended result from the provided 091 * extended result. 092 * 093 * @param extendedResult The extended result to be decoded as a password 094 * policy state extended result. It must not be 095 * {@code null}. 096 * 097 * @throws LDAPException If the provided extended result cannot be decoded 098 * as a password policy state extended result. 099 */ 100 public PasswordPolicyStateExtendedResult(final ExtendedResult extendedResult) 101 throws LDAPException 102 { 103 super(extendedResult); 104 105 final ASN1OctetString value = extendedResult.getValue(); 106 if (value == null) 107 { 108 userDN = null; 109 operations = Collections.emptyMap(); 110 return; 111 } 112 113 final ASN1Element[] elements; 114 try 115 { 116 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 117 elements = ASN1Sequence.decodeAsSequence(valueElement).elements(); 118 } 119 catch (final Exception e) 120 { 121 debugException(e); 122 throw new LDAPException(ResultCode.DECODING_ERROR, 123 ERR_PWP_STATE_RESPONSE_VALUE_NOT_SEQUENCE.get(e), 124 e); 125 } 126 127 if ((elements.length < 1) || (elements.length > 2)) 128 { 129 throw new LDAPException(ResultCode.DECODING_ERROR, 130 ERR_PWP_STATE_RESPONSE_INVALID_ELEMENT_COUNT.get( 131 elements.length)); 132 } 133 134 userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 135 136 final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops = 137 new LinkedHashMap<Integer,PasswordPolicyStateOperation>(); 138 if (elements.length == 2) 139 { 140 try 141 { 142 final ASN1Element[] opElements = 143 ASN1Sequence.decodeAsSequence(elements[1]).elements(); 144 for (final ASN1Element e : opElements) 145 { 146 final PasswordPolicyStateOperation op = 147 PasswordPolicyStateOperation.decode(e); 148 ops.put(op.getOperationType(), op); 149 } 150 } 151 catch (final Exception e) 152 { 153 debugException(e); 154 throw new LDAPException(ResultCode.DECODING_ERROR, 155 ERR_PWP_STATE_RESPONSE_CANNOT_DECODE_OPS.get(e), 156 e); 157 } 158 } 159 160 operations = Collections.unmodifiableMap(ops); 161 } 162 163 164 165 /** 166 * Creates a new password policy state extended result with the provided 167 * information. 168 * @param messageID The message ID for the LDAP message that is 169 * associated with this LDAP result. 170 * @param resultCode The result code from the response. 171 * @param diagnosticMessage The diagnostic message from the response, if 172 * available. 173 * @param matchedDN The matched DN from the response, if available. 174 * @param referralURLs The set of referral URLs from the response, if 175 * available. 176 * @param userDN The user DN from the response. 177 * @param operations The set of operations from the response, mapped 178 * from operation type to the corresponding 179 * operation data. 180 * @param responseControls The set of controls from the response, if 181 * available. 182 */ 183 public PasswordPolicyStateExtendedResult(final int messageID, 184 final ResultCode resultCode, final String diagnosticMessage, 185 final String matchedDN, final String[] referralURLs, 186 final String userDN, 187 final PasswordPolicyStateOperation[] operations, 188 final Control[] responseControls) 189 { 190 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 191 null, encodeValue(userDN, operations), responseControls); 192 193 this.userDN = userDN; 194 195 if ((operations == null) || (operations.length == 0)) 196 { 197 this.operations = Collections.emptyMap(); 198 } 199 else 200 { 201 final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops = 202 new LinkedHashMap<Integer,PasswordPolicyStateOperation>( 203 operations.length); 204 for (final PasswordPolicyStateOperation o : operations) 205 { 206 ops.put(o.getOperationType(), o); 207 } 208 this.operations = Collections.unmodifiableMap(ops); 209 } 210 } 211 212 213 214 /** 215 * Encodes the provided information into a suitable value for this control. 216 * 217 * @param userDN The user DN from the response. 218 * @param operations The set of operations from the response, mapped 219 * from operation type to the corresponding 220 * operation data. 221 * 222 * @return An ASN.1 octet string containing the appropriately-encoded value 223 * for this control, or {@code null} if there should not be a value. 224 */ 225 private static ASN1OctetString encodeValue(final String userDN, 226 final PasswordPolicyStateOperation[] operations) 227 { 228 if ((userDN == null) && ((operations == null) || (operations.length == 0))) 229 { 230 return null; 231 } 232 233 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2); 234 elements.add(new ASN1OctetString(userDN)); 235 236 if ((operations != null) && (operations.length > 0)) 237 { 238 final ASN1Element[] opElements = new ASN1Element[operations.length]; 239 for (int i=0; i < operations.length; i++) 240 { 241 opElements[i] = operations[i].encode(); 242 } 243 244 elements.add(new ASN1Sequence(opElements)); 245 } 246 247 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 248 } 249 250 251 252 253 /** 254 * Retrieves the user DN included in the response. 255 * 256 * @return The user DN included in the response, or {@code null} if the user 257 * DN is not available (e.g., if this is an error response). 258 */ 259 public String getUserDN() 260 { 261 return userDN; 262 } 263 264 265 266 /** 267 * Retrieves the set of password policy operations included in the response. 268 * 269 * @return The set of password policy operations included in the response. 270 */ 271 public Iterable<PasswordPolicyStateOperation> getOperations() 272 { 273 return operations.values(); 274 } 275 276 277 278 /** 279 * Retrieves the specified password policy state operation from the response. 280 * 281 * @param opType The operation type for the password policy state operation 282 * to retrieve. 283 * 284 * @return The requested password policy state operation, or {@code null} if 285 * no such operation was included in the response. 286 */ 287 public PasswordPolicyStateOperation getOperation(final int opType) 288 { 289 return operations.get(opType); 290 } 291 292 293 294 /** 295 * Retrieves the value for the specified password policy state operation as a 296 * string. 297 * 298 * @param opType The operation type for the password policy state operation 299 * to retrieve. 300 * 301 * @return The string value of the requested password policy state operation, 302 * or {@code null} if the specified operation was not included in the 303 * response or did not have any values. 304 */ 305 public String getStringValue(final int opType) 306 { 307 final PasswordPolicyStateOperation op = operations.get(opType); 308 if (op == null) 309 { 310 return null; 311 } 312 313 return op.getStringValue(); 314 } 315 316 317 318 /** 319 * Retrieves the set of string values for the specified password policy state 320 * operation. 321 * 322 * @param opType The operation type for the password policy state operation 323 * to retrieve. 324 * 325 * @return The set of string values for the requested password policy state 326 * operation, or {@code null} if the specified operation was not 327 * included in the response. 328 */ 329 public String[] getStringValues(final int opType) 330 { 331 final PasswordPolicyStateOperation op = operations.get(opType); 332 if (op == null) 333 { 334 return null; 335 } 336 337 return op.getStringValues(); 338 } 339 340 341 342 /** 343 * Retrieves the value of the specified password policy state operation as a 344 * boolean. 345 * 346 * @param opType The operation type for the password policy state operation 347 * to retrieve. 348 * 349 * @return The boolean value of the requested password policy state 350 * operation. 351 * 352 * @throws NoSuchElementException If the specified operation was not 353 * included in the response. 354 * 355 * @throws IllegalStateException If the specified password policy state 356 * operation does not have exactly one value, 357 * or if the value cannot be parsed as a 358 * boolean value. 359 */ 360 public boolean getBooleanValue(final int opType) 361 throws NoSuchElementException, IllegalStateException 362 { 363 final PasswordPolicyStateOperation op = operations.get(opType); 364 if (op == null) 365 { 366 throw new NoSuchElementException( 367 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get()); 368 } 369 370 return op.getBooleanValue(); 371 } 372 373 374 375 /** 376 * Retrieves the value of the specified password policy state operation as an 377 * integer. 378 * 379 * @param opType The operation type for the password policy state operation 380 * to retrieve. 381 * 382 * @return The integer value of the requested password policy state 383 * operation. 384 * 385 * @throws NoSuchElementException If the specified operation was not 386 * included in the response. 387 * 388 * @throws IllegalStateException If the value of the specified password 389 * policy state operation cannot be parsed as 390 * an integer value. 391 */ 392 public int getIntValue(final int opType) 393 throws NoSuchElementException, IllegalStateException 394 { 395 final PasswordPolicyStateOperation op = operations.get(opType); 396 if (op == null) 397 { 398 throw new NoSuchElementException( 399 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get()); 400 } 401 402 return op.getIntValue(); 403 } 404 405 406 407 /** 408 * Retrieves the value for the specified password policy state operation as a 409 * {@code Date} in generalized time format. 410 * 411 * @param opType The operation type for the password policy state operation 412 * to retrieve. 413 * 414 * @return The value of the requested password policy state operation as a 415 * {@code Date}, or {@code null} if the specified operation was not 416 * included in the response or did not have any values. 417 * 418 * @throws ParseException If the value cannot be parsed as a date in 419 * generalized time format. 420 */ 421 public Date getGeneralizedTimeValue(final int opType) 422 throws ParseException 423 { 424 final PasswordPolicyStateOperation op = operations.get(opType); 425 if (op == null) 426 { 427 return null; 428 } 429 430 return op.getGeneralizedTimeValue(); 431 } 432 433 434 435 /** 436 * Retrieves the set of values for the specified password policy state 437 * operation as {@code Date}s in generalized time format. 438 * 439 * @param opType The operation type for the password policy state operation 440 * to retrieve. 441 * 442 * @return The set of values of the requested password policy state operation 443 * as {@code Date}s. 444 * 445 * @throws ParseException If any of the values cannot be parsed as a date in 446 * generalized time format. 447 */ 448 public Date[] getGeneralizedTimeValues(final int opType) 449 throws ParseException 450 { 451 final PasswordPolicyStateOperation op = operations.get(opType); 452 if (op == null) 453 { 454 return null; 455 } 456 457 return op.getGeneralizedTimeValues(); 458 } 459 460 461 462 /** 463 * {@inheritDoc} 464 */ 465 @Override() 466 public String getExtendedResultName() 467 { 468 return INFO_EXTENDED_RESULT_NAME_PW_POLICY_STATE.get(); 469 } 470 471 472 473 /** 474 * Appends a string representation of this extended result to the provided 475 * buffer. 476 * 477 * @param buffer The buffer to which a string representation of this 478 * extended result will be appended. 479 */ 480 @Override() 481 public void toString(final StringBuilder buffer) 482 { 483 buffer.append("PasswordPolicyStateExtendedResult(resultCode="); 484 buffer.append(getResultCode()); 485 486 final int messageID = getMessageID(); 487 if (messageID >= 0) 488 { 489 buffer.append(", messageID="); 490 buffer.append(messageID); 491 } 492 493 buffer.append(", userDN='"); 494 buffer.append(userDN); 495 buffer.append("', operations={"); 496 497 final Iterator<PasswordPolicyStateOperation> iterator = 498 operations.values().iterator(); 499 while (iterator.hasNext()) 500 { 501 iterator.next().toString(buffer); 502 if (iterator.hasNext()) 503 { 504 buffer.append(", "); 505 } 506 } 507 buffer.append('}'); 508 509 final String diagnosticMessage = getDiagnosticMessage(); 510 if (diagnosticMessage != null) 511 { 512 buffer.append(", diagnosticMessage='"); 513 buffer.append(diagnosticMessage); 514 buffer.append('\''); 515 } 516 517 final String matchedDN = getMatchedDN(); 518 if (matchedDN != null) 519 { 520 buffer.append(", matchedDN='"); 521 buffer.append(matchedDN); 522 buffer.append('\''); 523 } 524 525 final String[] referralURLs = getReferralURLs(); 526 if (referralURLs.length > 0) 527 { 528 buffer.append(", referralURLs={"); 529 for (int i=0; i < referralURLs.length; i++) 530 { 531 if (i > 0) 532 { 533 buffer.append(", "); 534 } 535 536 buffer.append('\''); 537 buffer.append(referralURLs[i]); 538 buffer.append('\''); 539 } 540 buffer.append('}'); 541 } 542 543 final Control[] responseControls = getResponseControls(); 544 if (responseControls.length > 0) 545 { 546 buffer.append(", responseControls={"); 547 for (int i=0; i < responseControls.length; i++) 548 { 549 if (i > 0) 550 { 551 buffer.append(", "); 552 } 553 554 buffer.append(responseControls[i]); 555 } 556 buffer.append('}'); 557 } 558 559 buffer.append(')'); 560 } 561}