001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 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.controls; 022 023 024 025import java.util.ArrayList; 026import java.util.Collection; 027 028import com.unboundid.asn1.ASN1Boolean; 029import com.unboundid.asn1.ASN1Element; 030import com.unboundid.asn1.ASN1Exception; 031import com.unboundid.asn1.ASN1OctetString; 032import com.unboundid.asn1.ASN1Sequence; 033import com.unboundid.ldap.sdk.Attribute; 034import com.unboundid.ldap.sdk.BindResult; 035import com.unboundid.ldap.sdk.Control; 036import com.unboundid.ldap.sdk.DecodeableControl; 037import com.unboundid.ldap.sdk.LDAPException; 038import com.unboundid.ldap.sdk.ReadOnlyEntry; 039import com.unboundid.ldap.sdk.ResultCode; 040import com.unboundid.util.Debug; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.StaticUtils; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045 046import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 047 048 049 050/** 051 * This class provides an implementation of an LDAP control that may be included 052 * in a bind response to provide information about the authenticated and/or 053 * authorized user. 054 * <BR> 055 * <BLOCKQUOTE> 056 * <B>NOTE:</B> This class, and other classes within the 057 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 058 * supported for use against Ping Identity, UnboundID, and 059 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 060 * for proprietary functionality or for external specifications that are not 061 * considered stable or mature enough to be guaranteed to work in an 062 * interoperable way with other types of LDAP servers. 063 * </BLOCKQUOTE> 064 * <BR> 065 * The value of this control will be encoded as follows: 066 * <PRE> 067 * GetAuthorizationEntryResponse ::= SEQUENCE { 068 * isAuthenticated [0] BOOLEAN, 069 * identitiesMatch [1] BOOLEAN, 070 * authNEntry [2] AuthEntry OPTIONAL, 071 * authZEntry [3] AuthEntry OPTIONAL } 072 * 073 * AuthEntry ::= SEQUENCE { 074 * authID [0] AuthzId OPTIONAL, 075 * authDN [1] LDAPDN, 076 * attributes [2] PartialAttributeList } 077 * </PRE> 078 * <BR><BR> 079 * See the documentation for the {@link GetAuthorizationEntryRequestControl} 080 * class for more information and an example demonstrating the use of these 081 * controls. 082 */ 083@NotMutable() 084@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 085public final class GetAuthorizationEntryResponseControl 086 extends Control 087 implements DecodeableControl 088{ 089 /** 090 * The OID (1.3.6.1.4.1.30221.2.5.6) for the get authorization entry response 091 * control. 092 */ 093 public static final String GET_AUTHORIZATION_ENTRY_RESPONSE_OID = 094 "1.3.6.1.4.1.30221.2.5.6"; 095 096 097 098 /** 099 * The BER type for the {@code isAuthenticated} element. 100 */ 101 private static final byte TYPE_IS_AUTHENTICATED = (byte) 0x80; 102 103 104 105 /** 106 * The BER type for the {@code identitiesMatch} element. 107 */ 108 private static final byte TYPE_IDENTITIES_MATCH = (byte) 0x81; 109 110 111 112 /** 113 * The BER type for the {@code authNEntry} element. 114 */ 115 private static final byte TYPE_AUTHN_ENTRY = (byte) 0xA2; 116 117 118 119 /** 120 * The BER type for the {@code authZEntry} element. 121 */ 122 private static final byte TYPE_AUTHZ_ENTRY = (byte) 0xA3; 123 124 125 126 /** 127 * The BER type for the {@code authID} element. 128 */ 129 private static final byte TYPE_AUTHID = (byte) 0x80; 130 131 132 133 /** 134 * The BER type for the {@code authDN} element. 135 */ 136 private static final byte TYPE_AUTHDN = (byte) 0x81; 137 138 139 140 /** 141 * The BER type for the {@code attributesDN} element. 142 */ 143 private static final byte TYPE_ATTRIBUTES= (byte) 0xA2; 144 145 146 147 /** 148 * The serial version UID for this serializable class. 149 */ 150 private static final long serialVersionUID = -5443107150740697226L; 151 152 153 154 // Indicates whether the authentication and authorization identities are the 155 // same. 156 private final boolean identitiesMatch; 157 158 // Indicates whether the client is authenticated. 159 private final boolean isAuthenticated; 160 161 // The entry for the authentication identity, if available. 162 private final ReadOnlyEntry authNEntry; 163 164 // The entry for the authorization identity, if available. 165 private final ReadOnlyEntry authZEntry; 166 167 // The authID for the authentication identity, if available. 168 private final String authNID; 169 170 // The authID for the authorization identity, if available. 171 private final String authZID; 172 173 174 175 /** 176 * Creates a new empty control instance that is intended to be used only for 177 * decoding controls via the {@code DecodeableControl} interface. 178 */ 179 GetAuthorizationEntryResponseControl() 180 { 181 isAuthenticated = false; 182 identitiesMatch = true; 183 authNEntry = null; 184 authNID = null; 185 authZEntry = null; 186 authZID = null; 187 } 188 189 190 191 /** 192 * Creates a new get authorization entry response control with the provided 193 * information. 194 * 195 * @param isAuthenticated Indicates whether the client is authenticated. 196 * @param identitiesMatch Indicates whether the authentication identity is 197 * the same as the authorization identity. 198 * @param authNID The string that may be used to reference the 199 * authentication identity. It may be {@code null} 200 * if information about the authentication identity 201 * is not to be included, or if the identifier should 202 * be derived from the DN. 203 * @param authNEntry The entry for the authentication identity. It may 204 * be {@code null} if the information about the 205 * authentication identity is not to be included. 206 * @param authZID The string that may be used to reference the 207 * authorization identity. It may be {@code null} 208 * if information about the authentication identity 209 * is not to be included, if the identifier should 210 * be derived from the DN, or if the authentication 211 * and authorization identities are the same. 212 * @param authZEntry The entry for the authentication identity. It may 213 * be {@code null} if the information about the 214 * authentication identity is not to be included, or 215 * if the authentication and authorization identities 216 * are the same. 217 */ 218 public GetAuthorizationEntryResponseControl(final boolean isAuthenticated, 219 final boolean identitiesMatch, final String authNID, 220 final ReadOnlyEntry authNEntry, final String authZID, 221 final ReadOnlyEntry authZEntry) 222 { 223 super(GET_AUTHORIZATION_ENTRY_RESPONSE_OID, false, 224 encodeValue(isAuthenticated, identitiesMatch, authNID, authNEntry, 225 authZID, authZEntry)); 226 227 this.isAuthenticated = isAuthenticated; 228 this.identitiesMatch = identitiesMatch; 229 this.authNID = authNID; 230 this.authNEntry = authNEntry; 231 this.authZID = authZID; 232 this.authZEntry = authZEntry; 233 } 234 235 236 237 /** 238 * Creates a new get authorization entry response control with the provided 239 * information. 240 * 241 * @param oid The OID for the control. 242 * @param isCritical Indicates whether the control should be marked 243 * critical. 244 * @param value The encoded value for the control. This may be 245 * {@code null} if no value was provided. 246 * 247 * @throws LDAPException If the provided control cannot be decoded as a get 248 * authorization entry response control. 249 */ 250 public GetAuthorizationEntryResponseControl(final String oid, 251 final boolean isCritical, 252 final ASN1OctetString value) 253 throws LDAPException 254 { 255 super(oid, isCritical, value); 256 257 if (value == null) 258 { 259 throw new LDAPException(ResultCode.DECODING_ERROR, 260 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_NO_VALUE.get()); 261 } 262 263 try 264 { 265 boolean isAuth = false; 266 boolean idsMatch = false; 267 String nID = null; 268 String zID = null; 269 ReadOnlyEntry nEntry = null; 270 ReadOnlyEntry zEntry = null; 271 272 final ASN1Element valElement = ASN1Element.decode(value.getValue()); 273 for (final ASN1Element e : 274 ASN1Sequence.decodeAsSequence(valElement).elements()) 275 { 276 switch (e.getType()) 277 { 278 case TYPE_IS_AUTHENTICATED: 279 isAuth = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 280 break; 281 case TYPE_IDENTITIES_MATCH: 282 idsMatch = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 283 break; 284 case TYPE_AUTHN_ENTRY: 285 final Object[] nObjects = decodeAuthEntry(e); 286 nID = (String) nObjects[0]; 287 nEntry = (ReadOnlyEntry) nObjects[1]; 288 break; 289 case TYPE_AUTHZ_ENTRY: 290 final Object[] zObjects = decodeAuthEntry(e); 291 zID = (String) zObjects[0]; 292 zEntry = (ReadOnlyEntry) zObjects[1]; 293 break; 294 default: 295 throw new LDAPException(ResultCode.DECODING_ERROR, 296 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_VALUE_TYPE.get( 297 StaticUtils.toHex(e.getType()))); 298 } 299 } 300 301 isAuthenticated = isAuth; 302 identitiesMatch = idsMatch; 303 authNID = nID; 304 authNEntry = nEntry; 305 authZID = zID; 306 authZEntry = zEntry; 307 } 308 catch (final Exception e) 309 { 310 Debug.debugException(e); 311 throw new LDAPException(ResultCode.DECODING_ERROR, 312 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_CANNOT_DECODE_VALUE.get( 313 StaticUtils.getExceptionMessage(e)), 314 e); 315 } 316 } 317 318 319 320 /** 321 * {@inheritDoc} 322 */ 323 @Override() 324 public GetAuthorizationEntryResponseControl decodeControl(final String oid, 325 final boolean isCritical, 326 final ASN1OctetString value) 327 throws LDAPException 328 { 329 return new GetAuthorizationEntryResponseControl(oid, isCritical, value); 330 } 331 332 333 334 /** 335 * Extracts a get authorization entry response control from the provided 336 * result. 337 * 338 * @param result The result from which to retrieve the get authorization 339 * entry response control. 340 * 341 * @return The get authorization entry response control contained in the 342 * provided result, or {@code null} if the result did not contain a 343 * get authorization entry response control. 344 * 345 * @throws LDAPException If a problem is encountered while attempting to 346 * decode the get authorization entry response control 347 * contained in the provided result. 348 */ 349 public static GetAuthorizationEntryResponseControl 350 get(final BindResult result) 351 throws LDAPException 352 { 353 final Control c = 354 result.getResponseControl(GET_AUTHORIZATION_ENTRY_RESPONSE_OID); 355 if (c == null) 356 { 357 return null; 358 } 359 360 if (c instanceof GetAuthorizationEntryResponseControl) 361 { 362 return (GetAuthorizationEntryResponseControl) c; 363 } 364 else 365 { 366 return new GetAuthorizationEntryResponseControl(c.getOID(), 367 c.isCritical(), c.getValue()); 368 } 369 } 370 371 372 373 /** 374 * Encodes the provided information appropriately for use as the value of this 375 * control. 376 * 377 * @param isAuthenticated Indicates whether the client is authenticated. 378 * @param identitiesMatch Indicates whether the authentication identity is 379 * the same as the authorization identity. 380 * @param authNID The string that may be used to reference the 381 * authentication identity. It may be {@code null} 382 * if information about the authentication identity 383 * is not to be included, or if the identifier should 384 * be derived from the DN. 385 * @param authNEntry The entry for the authentication identity. It may 386 * be {@code null} if the information about the 387 * authentication identity is not to be included. 388 * @param authZID The string that may be used to reference the 389 * authorization identity. It may be {@code null} 390 * if information about the authentication identity 391 * is not to be included, if the identifier should 392 * be derived from the DN, or if the authentication 393 * and authorization identities are the same. 394 * @param authZEntry The entry for the authentication identity. It may 395 * be {@code null} if the information about the 396 * authentication identity is not to be included, or 397 * if the authentication and authorization identities 398 * are the same. 399 * 400 * @return The ASN.1 octet string suitable for use as the control value. 401 */ 402 private static ASN1OctetString encodeValue(final boolean isAuthenticated, 403 final boolean identitiesMatch, 404 final String authNID, 405 final ReadOnlyEntry authNEntry, 406 final String authZID, 407 final ReadOnlyEntry authZEntry) 408 { 409 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 410 elements.add(new ASN1Boolean(TYPE_IS_AUTHENTICATED, isAuthenticated)); 411 elements.add(new ASN1Boolean(TYPE_IDENTITIES_MATCH, identitiesMatch)); 412 413 if (authNEntry != null) 414 { 415 elements.add(encodeAuthEntry(TYPE_AUTHN_ENTRY, authNID, authNEntry)); 416 } 417 418 if (authZEntry != null) 419 { 420 elements.add(encodeAuthEntry(TYPE_AUTHZ_ENTRY, authZID, authZEntry)); 421 } 422 423 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 424 } 425 426 427 428 /** 429 * Encodes the provided information as appropriate for an auth entry. 430 * 431 * @param type The BER type to use for the element. 432 * @param authID The authID to be encoded, if available. 433 * @param authEntry The entry to be encoded. 434 * 435 * @return The ASN.1 sequence containing the encoded auth entry. 436 */ 437 private static ASN1Sequence encodeAuthEntry(final byte type, 438 final String authID, 439 final ReadOnlyEntry authEntry) 440 { 441 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 442 443 if (authID != null) 444 { 445 elements.add(new ASN1OctetString(TYPE_AUTHID, authID)); 446 } 447 448 elements.add(new ASN1OctetString(TYPE_AUTHDN, authEntry.getDN())); 449 450 final Collection<Attribute> attributes = authEntry.getAttributes(); 451 final ArrayList<ASN1Element> attrElements = 452 new ArrayList<>(attributes.size()); 453 for (final Attribute a : attributes) 454 { 455 attrElements.add(a.encode()); 456 } 457 elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements)); 458 459 return new ASN1Sequence(type, elements); 460 } 461 462 463 464 /** 465 * Decodes the provided ASN.1 element into an array of auth entry elements. 466 * The first element of the array will be the auth ID, and the second element 467 * will be the read-only entry. 468 * 469 * @param element The element to decode. 470 * 471 * @return The decoded array of elements. 472 * 473 * @throws ASN1Exception If a problem occurs while performing ASN.1 parsing. 474 * 475 * @throws LDAPException If a problem occurs while performing LDAP parsing. 476 */ 477 private static Object[] decodeAuthEntry(final ASN1Element element) 478 throws ASN1Exception, LDAPException 479 { 480 String authID = null; 481 String authDN = null; 482 final ArrayList<Attribute> attrs = new ArrayList<>(20); 483 484 for (final ASN1Element e : 485 ASN1Sequence.decodeAsSequence(element).elements()) 486 { 487 switch (e.getType()) 488 { 489 case TYPE_AUTHID: 490 authID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 491 break; 492 case TYPE_AUTHDN: 493 authDN = ASN1OctetString.decodeAsOctetString(e).stringValue(); 494 break; 495 case TYPE_ATTRIBUTES: 496 for (final ASN1Element ae : 497 ASN1Sequence.decodeAsSequence(e).elements()) 498 { 499 attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(ae))); 500 } 501 break; 502 default: 503 throw new LDAPException(ResultCode.DECODING_ERROR, 504 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_ENTRY_TYPE.get( 505 StaticUtils.toHex(e.getType()))); 506 } 507 } 508 509 return new Object[] { authID, new ReadOnlyEntry(authDN, attrs) }; 510 } 511 512 513 514 /** 515 * Indicates whether the client is authenticated. 516 * 517 * @return {@code true} if the client is authenticated, or {@code false} if 518 * not. 519 */ 520 public boolean isAuthenticated() 521 { 522 return isAuthenticated; 523 } 524 525 526 527 /** 528 * Indicates whether the authentication identity and the authorization 529 * identity reference the same user. 530 * 531 * @return {@code true} if both the authentication identity and the 532 * authorization identity reference the same user, or {@code false} 533 * if not. 534 */ 535 public boolean identitiesMatch() 536 { 537 return identitiesMatch; 538 } 539 540 541 542 /** 543 * Retrieves the identifier that may be used to reference the authentication 544 * identity in the directory server, if it is available. 545 * 546 * @return The identifier that may be used to reference the authentication 547 * identity in the directory server, or {@code null} if it is not 548 * available. 549 */ 550 public String getAuthNID() 551 { 552 if ((authNID == null) && identitiesMatch) 553 { 554 return authZID; 555 } 556 557 return authNID; 558 } 559 560 561 562 /** 563 * Retrieves the entry for the user specified as the authentication identity, 564 * if it is available. 565 * 566 * @return The entry for the user specified as the authentication identity, 567 * or {@code null} if it is not available. 568 */ 569 public ReadOnlyEntry getAuthNEntry() 570 { 571 if ((authNEntry == null) && identitiesMatch) 572 { 573 return authZEntry; 574 } 575 576 return authNEntry; 577 } 578 579 580 581 /** 582 * Retrieves the identifier that may be used to reference the authorization 583 * identity in the directory server, if it is available. 584 * 585 * @return The identifier that may be used to reference the authorization 586 * identity in the directory server, or {@code null} if it is not 587 * available. 588 */ 589 public String getAuthZID() 590 { 591 if ((authZID == null) && identitiesMatch) 592 { 593 return authNID; 594 } 595 596 return authZID; 597 } 598 599 600 601 /** 602 * Retrieves the entry for the user specified as the authorization identity, 603 * if it is available. 604 * 605 * @return The entry for the user specified as the authorization identity, 606 * or {@code null} if it is not available. 607 */ 608 public ReadOnlyEntry getAuthZEntry() 609 { 610 if ((authZEntry == null) && identitiesMatch) 611 { 612 return authNEntry; 613 } 614 615 return authZEntry; 616 } 617 618 619 620 /** 621 * {@inheritDoc} 622 */ 623 @Override() 624 public String getControlName() 625 { 626 return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get(); 627 } 628 629 630 631 /** 632 * {@inheritDoc} 633 */ 634 @Override() 635 public void toString(final StringBuilder buffer) 636 { 637 buffer.append("GetAuthorizationEntryResponseControl(identitiesMatch="); 638 buffer.append(identitiesMatch); 639 640 if (authNID != null) 641 { 642 buffer.append(", authNID='"); 643 buffer.append(authNID); 644 buffer.append('\''); 645 } 646 647 if (authNEntry != null) 648 { 649 buffer.append(", authNEntry="); 650 authNEntry.toString(buffer); 651 } 652 653 if (authZID != null) 654 { 655 buffer.append(", authZID='"); 656 buffer.append(authZID); 657 buffer.append('\''); 658 } 659 660 if (authZEntry != null) 661 { 662 buffer.append(", authZEntry="); 663 authZEntry.toString(buffer); 664 } 665 666 buffer.append(')'); 667 } 668}