001/* 002 * Copyright 2009-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-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.protocol; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1Buffer; 031import com.unboundid.asn1.ASN1BufferSequence; 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1Enumerated; 034import com.unboundid.asn1.ASN1OctetString; 035import com.unboundid.asn1.ASN1Sequence; 036import com.unboundid.asn1.ASN1StreamReader; 037import com.unboundid.asn1.ASN1StreamReaderSequence; 038import com.unboundid.ldap.sdk.BindResult; 039import com.unboundid.ldap.sdk.Control; 040import com.unboundid.ldap.sdk.LDAPException; 041import com.unboundid.ldap.sdk.LDAPResult; 042import com.unboundid.ldap.sdk.ResultCode; 043import com.unboundid.util.Debug; 044import com.unboundid.util.InternalUseOnly; 045import com.unboundid.util.NotMutable; 046import com.unboundid.util.StaticUtils; 047import com.unboundid.util.ThreadSafety; 048import com.unboundid.util.ThreadSafetyLevel; 049import com.unboundid.util.Validator; 050 051import static com.unboundid.ldap.protocol.ProtocolMessages.*; 052 053 054 055/** 056 * This class provides an implementation of a bind response protocol op. 057 */ 058@InternalUseOnly() 059@NotMutable() 060@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 061public final class BindResponseProtocolOp 062 implements ProtocolOp 063{ 064 /** 065 * The BER type for the server SASL credentials element. 066 */ 067 public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87; 068 069 070 071 /** 072 * The serial version UID for this serializable class. 073 */ 074 private static final long serialVersionUID = -7757619031268544913L; 075 076 077 078 // The server SASL credentials for this bind response. 079 private final ASN1OctetString serverSASLCredentials; 080 081 // The result code for this bind response. 082 private final int resultCode; 083 084 // The referral URLs for this bind response. 085 private final List<String> referralURLs; 086 087 // The diagnostic message for this bind response. 088 private final String diagnosticMessage; 089 090 // The matched DN for this bind response. 091 private final String matchedDN; 092 093 094 095 /** 096 * Creates a new instance of this bind response protocol op with the provided 097 * information. 098 * 099 * @param resultCode The result code for this response. 100 * @param matchedDN The matched DN for this response, if 101 * available. 102 * @param diagnosticMessage The diagnostic message for this response, if 103 * any. 104 * @param referralURLs The list of referral URLs for this response, 105 * if any. 106 * @param serverSASLCredentials The server SASL credentials for this 107 * response, if available. 108 */ 109 public BindResponseProtocolOp(final int resultCode, final String matchedDN, 110 final String diagnosticMessage, 111 final List<String> referralURLs, 112 final ASN1OctetString serverSASLCredentials) 113 { 114 this.resultCode = resultCode; 115 this.matchedDN = matchedDN; 116 this.diagnosticMessage = diagnosticMessage; 117 118 if (referralURLs == null) 119 { 120 this.referralURLs = Collections.emptyList(); 121 } 122 else 123 { 124 this.referralURLs = Collections.unmodifiableList(referralURLs); 125 } 126 127 if (serverSASLCredentials == null) 128 { 129 this.serverSASLCredentials = null; 130 } 131 else 132 { 133 this.serverSASLCredentials = new ASN1OctetString( 134 TYPE_SERVER_SASL_CREDENTIALS, serverSASLCredentials.getValue()); 135 } 136 } 137 138 139 140 /** 141 * Creates a new bind response protocol op from the provided bind result 142 * object. 143 * 144 * @param result The LDAP result object to use to create this protocol op. 145 */ 146 public BindResponseProtocolOp(final LDAPResult result) 147 { 148 resultCode = result.getResultCode().intValue(); 149 matchedDN = result.getMatchedDN(); 150 diagnosticMessage = result.getDiagnosticMessage(); 151 referralURLs = StaticUtils.toList(result.getReferralURLs()); 152 153 if (result instanceof BindResult) 154 { 155 final BindResult br = (BindResult) result; 156 serverSASLCredentials = br.getServerSASLCredentials(); 157 } 158 else 159 { 160 serverSASLCredentials = null; 161 } 162 } 163 164 165 166 /** 167 * Creates a new bind response protocol op read from the provided ASN.1 stream 168 * reader. 169 * 170 * @param reader The ASN.1 stream reader from which to read the bind 171 * response. 172 * 173 * @throws LDAPException If a problem occurs while reading or parsing the 174 * bind response. 175 */ 176 BindResponseProtocolOp(final ASN1StreamReader reader) 177 throws LDAPException 178 { 179 try 180 { 181 final ASN1StreamReaderSequence opSequence = reader.beginSequence(); 182 resultCode = reader.readEnumerated(); 183 184 String s = reader.readString(); 185 Validator.ensureNotNull(s); 186 if (s.isEmpty()) 187 { 188 matchedDN = null; 189 } 190 else 191 { 192 matchedDN = s; 193 } 194 195 s = reader.readString(); 196 Validator.ensureNotNull(s); 197 if (s.isEmpty()) 198 { 199 diagnosticMessage = null; 200 } 201 else 202 { 203 diagnosticMessage = s; 204 } 205 206 ASN1OctetString creds = null; 207 final ArrayList<String> refs = new ArrayList<>(1); 208 while (opSequence.hasMoreElements()) 209 { 210 final byte type = (byte) reader.peek(); 211 if (type == GenericResponseProtocolOp.TYPE_REFERRALS) 212 { 213 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 214 while (refSequence.hasMoreElements()) 215 { 216 refs.add(reader.readString()); 217 } 218 } 219 else if (type == TYPE_SERVER_SASL_CREDENTIALS) 220 { 221 creds = new ASN1OctetString(type, reader.readBytes()); 222 } 223 else 224 { 225 throw new LDAPException(ResultCode.DECODING_ERROR, 226 ERR_BIND_RESPONSE_INVALID_ELEMENT.get(StaticUtils.toHex(type))); 227 } 228 } 229 230 referralURLs = Collections.unmodifiableList(refs); 231 serverSASLCredentials = creds; 232 } 233 catch (final LDAPException le) 234 { 235 Debug.debugException(le); 236 throw le; 237 } 238 catch (final Exception e) 239 { 240 Debug.debugException(e); 241 throw new LDAPException(ResultCode.DECODING_ERROR, 242 ERR_BIND_RESPONSE_CANNOT_DECODE.get( 243 StaticUtils.getExceptionMessage(e)), 244 e); 245 } 246 } 247 248 249 250 /** 251 * Retrieves the result code for this bind response. 252 * 253 * @return The result code for this bind response. 254 */ 255 public int getResultCode() 256 { 257 return resultCode; 258 } 259 260 261 262 /** 263 * Retrieves the matched DN for this bind response, if any. 264 * 265 * @return The matched DN for this bind response, or {@code null} if there is 266 * no matched DN. 267 */ 268 public String getMatchedDN() 269 { 270 return matchedDN; 271 } 272 273 274 275 /** 276 * Retrieves the diagnostic message for this bind response, if any. 277 * 278 * @return The diagnostic message for this bind response, or {@code null} if 279 * there is no diagnostic message. 280 */ 281 public String getDiagnosticMessage() 282 { 283 return diagnosticMessage; 284 } 285 286 287 288 /** 289 * Retrieves the list of referral URLs for this bind response. 290 * 291 * @return The list of referral URLs for this bind response, or an empty list 292 * if there are no referral URLs. 293 */ 294 public List<String> getReferralURLs() 295 { 296 return referralURLs; 297 } 298 299 300 301 /** 302 * Retrieves the server SASL credentials for this bind response, if any. 303 * 304 * @return The server SASL credentials for this bind response, or 305 * {@code null} if there are no server SASL credentials. 306 */ 307 public ASN1OctetString getServerSASLCredentials() 308 { 309 return serverSASLCredentials; 310 } 311 312 313 314 /** 315 * {@inheritDoc} 316 */ 317 @Override() 318 public byte getProtocolOpType() 319 { 320 return LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE; 321 } 322 323 324 325 /** 326 * {@inheritDoc} 327 */ 328 @Override() 329 public ASN1Element encodeProtocolOp() 330 { 331 final ArrayList<ASN1Element> elements = new ArrayList<>(5); 332 elements.add(new ASN1Enumerated(getResultCode())); 333 334 final String mDN = getMatchedDN(); 335 if (mDN == null) 336 { 337 elements.add(new ASN1OctetString()); 338 } 339 else 340 { 341 elements.add(new ASN1OctetString(mDN)); 342 } 343 344 final String dm = getDiagnosticMessage(); 345 if (dm == null) 346 { 347 elements.add(new ASN1OctetString()); 348 } 349 else 350 { 351 elements.add(new ASN1OctetString(dm)); 352 } 353 354 final List<String> refs = getReferralURLs(); 355 if (! refs.isEmpty()) 356 { 357 final ArrayList<ASN1Element> refElements = new ArrayList<>(refs.size()); 358 for (final String r : refs) 359 { 360 refElements.add(new ASN1OctetString(r)); 361 } 362 elements.add(new ASN1Sequence(GenericResponseProtocolOp.TYPE_REFERRALS, 363 refElements)); 364 } 365 366 if (serverSASLCredentials != null) 367 { 368 elements.add(serverSASLCredentials); 369 } 370 371 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE, 372 elements); 373 } 374 375 376 377 /** 378 * Decodes the provided ASN.1 element as a bind response protocol op. 379 * 380 * @param element The ASN.1 element to be decoded. 381 * 382 * @return The decoded bind response protocol op. 383 * 384 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 385 * a bind response protocol op. 386 */ 387 public static BindResponseProtocolOp decodeProtocolOp( 388 final ASN1Element element) 389 throws LDAPException 390 { 391 try 392 { 393 final ASN1Element[] elements = 394 ASN1Sequence.decodeAsSequence(element).elements(); 395 final int resultCode = 396 ASN1Enumerated.decodeAsEnumerated(elements[0]).intValue(); 397 398 final String matchedDN; 399 final String md = 400 ASN1OctetString.decodeAsOctetString(elements[1]).stringValue(); 401 if (! md.isEmpty()) 402 { 403 matchedDN = md; 404 } 405 else 406 { 407 matchedDN = null; 408 } 409 410 final String diagnosticMessage; 411 final String dm = 412 ASN1OctetString.decodeAsOctetString(elements[2]).stringValue(); 413 if (! dm.isEmpty()) 414 { 415 diagnosticMessage = dm; 416 } 417 else 418 { 419 diagnosticMessage = null; 420 } 421 422 ASN1OctetString serverSASLCredentials = null; 423 List<String> referralURLs = null; 424 if (elements.length > 3) 425 { 426 for (int i=3; i < elements.length; i++) 427 { 428 switch (elements[i].getType()) 429 { 430 case GenericResponseProtocolOp.TYPE_REFERRALS: 431 final ASN1Element[] refElements = 432 ASN1Sequence.decodeAsSequence(elements[3]).elements(); 433 referralURLs = new ArrayList<>(refElements.length); 434 for (final ASN1Element e : refElements) 435 { 436 referralURLs.add( 437 ASN1OctetString.decodeAsOctetString(e).stringValue()); 438 } 439 break; 440 441 case TYPE_SERVER_SASL_CREDENTIALS: 442 serverSASLCredentials = 443 ASN1OctetString.decodeAsOctetString(elements[i]); 444 break; 445 446 default: 447 throw new LDAPException(ResultCode.DECODING_ERROR, 448 ERR_BIND_RESPONSE_INVALID_ELEMENT.get( 449 StaticUtils.toHex(elements[i].getType()))); 450 } 451 } 452 } 453 454 return new BindResponseProtocolOp(resultCode, matchedDN, 455 diagnosticMessage, referralURLs, serverSASLCredentials); 456 } 457 catch (final LDAPException le) 458 { 459 Debug.debugException(le); 460 throw le; 461 } 462 catch (final Exception e) 463 { 464 Debug.debugException(e); 465 throw new LDAPException(ResultCode.DECODING_ERROR, 466 ERR_BIND_RESPONSE_CANNOT_DECODE.get( 467 StaticUtils.getExceptionMessage(e)), 468 e); 469 } 470 } 471 472 473 474 /** 475 * {@inheritDoc} 476 */ 477 @Override() 478 public void writeTo(final ASN1Buffer buffer) 479 { 480 final ASN1BufferSequence opSequence = 481 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE); 482 buffer.addEnumerated(resultCode); 483 buffer.addOctetString(matchedDN); 484 buffer.addOctetString(diagnosticMessage); 485 486 if (! referralURLs.isEmpty()) 487 { 488 final ASN1BufferSequence refSequence = 489 buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS); 490 for (final String s : referralURLs) 491 { 492 buffer.addOctetString(s); 493 } 494 refSequence.end(); 495 } 496 497 if (serverSASLCredentials != null) 498 { 499 buffer.addElement(serverSASLCredentials); 500 } 501 502 opSequence.end(); 503 } 504 505 506 507 /** 508 * Creates a new LDAP result object from this response protocol op. 509 * 510 * @param controls The set of controls to include in the LDAP result. It 511 * may be empty or {@code null} if no controls should be 512 * included. 513 * 514 * @return The LDAP result that was created. 515 */ 516 public BindResult toBindResult(final Control... controls) 517 { 518 final String[] refs; 519 if (referralURLs.isEmpty()) 520 { 521 refs = StaticUtils.NO_STRINGS; 522 } 523 else 524 { 525 refs = new String[referralURLs.size()]; 526 referralURLs.toArray(refs); 527 } 528 529 return new BindResult(-1, ResultCode.valueOf(resultCode), diagnosticMessage, 530 matchedDN, refs, controls, serverSASLCredentials); 531 } 532 533 534 535 /** 536 * Retrieves a string representation of this protocol op. 537 * 538 * @return A string representation of this protocol op. 539 */ 540 @Override() 541 public String toString() 542 { 543 final StringBuilder buffer = new StringBuilder(); 544 toString(buffer); 545 return buffer.toString(); 546 } 547 548 549 550 /** 551 * {@inheritDoc} 552 */ 553 @Override() 554 public void toString(final StringBuilder buffer) 555 { 556 buffer.append("BindResponseProtocolOp(resultCode="); 557 buffer.append(resultCode); 558 559 if (matchedDN != null) 560 { 561 buffer.append(", matchedDN='"); 562 buffer.append(matchedDN); 563 buffer.append('\''); 564 } 565 566 if (diagnosticMessage != null) 567 { 568 buffer.append(", diagnosticMessage='"); 569 buffer.append(diagnosticMessage); 570 buffer.append('\''); 571 } 572 573 if (! referralURLs.isEmpty()) 574 { 575 buffer.append(", referralURLs={"); 576 577 final Iterator<String> iterator = referralURLs.iterator(); 578 while (iterator.hasNext()) 579 { 580 buffer.append('\''); 581 buffer.append(iterator.next()); 582 buffer.append('\''); 583 if (iterator.hasNext()) 584 { 585 buffer.append(','); 586 } 587 } 588 589 buffer.append('}'); 590 } 591 buffer.append(')'); 592 } 593}