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.NotMutable; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043 044import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 045import static com.unboundid.util.Debug.*; 046import static com.unboundid.util.StaticUtils.*; 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 Alcatel-Lucent 8661 059 * server products. These classes provide support for proprietary 060 * functionality or for external specifications that are not considered stable 061 * or mature enough to be guaranteed to work in an interoperable way with 062 * 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 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 debugException(e); 311 throw new LDAPException(ResultCode.DECODING_ERROR, 312 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_CANNOT_DECODE_VALUE.get( 313 getExceptionMessage(e)), e); 314 } 315 } 316 317 318 319 /** 320 * {@inheritDoc} 321 */ 322 @Override() 323 public GetAuthorizationEntryResponseControl decodeControl(final String oid, 324 final boolean isCritical, 325 final ASN1OctetString value) 326 throws LDAPException 327 { 328 return new GetAuthorizationEntryResponseControl(oid, isCritical, value); 329 } 330 331 332 333 /** 334 * Extracts a get authorization entry response control from the provided 335 * result. 336 * 337 * @param result The result from which to retrieve the get authorization 338 * entry response control. 339 * 340 * @return The get authorization entry response control contained in the 341 * provided result, or {@code null} if the result did not contain a 342 * get authorization entry response control. 343 * 344 * @throws LDAPException If a problem is encountered while attempting to 345 * decode the get authorization entry response control 346 * contained in the provided result. 347 */ 348 public static GetAuthorizationEntryResponseControl 349 get(final BindResult result) 350 throws LDAPException 351 { 352 final Control c = 353 result.getResponseControl(GET_AUTHORIZATION_ENTRY_RESPONSE_OID); 354 if (c == null) 355 { 356 return null; 357 } 358 359 if (c instanceof GetAuthorizationEntryResponseControl) 360 { 361 return (GetAuthorizationEntryResponseControl) c; 362 } 363 else 364 { 365 return new GetAuthorizationEntryResponseControl(c.getOID(), 366 c.isCritical(), c.getValue()); 367 } 368 } 369 370 371 372 /** 373 * Encodes the provided information appropriately for use as the value of this 374 * control. 375 * 376 * @param isAuthenticated Indicates whether the client is authenticated. 377 * @param identitiesMatch Indicates whether the authentication identity is 378 * the same as the authorization identity. 379 * @param authNID The string that may be used to reference the 380 * authentication identity. It may be {@code null} 381 * if information about the authentication identity 382 * is not to be included, or if the identifier should 383 * be derived from the DN. 384 * @param authNEntry The entry for the authentication identity. It may 385 * be {@code null} if the information about the 386 * authentication identity is not to be included. 387 * @param authZID The string that may be used to reference the 388 * authorization identity. It may be {@code null} 389 * if information about the authentication identity 390 * is not to be included, if the identifier should 391 * be derived from the DN, or if the authentication 392 * and authorization identities are the same. 393 * @param authZEntry The entry for the authentication identity. It may 394 * be {@code null} if the information about the 395 * authentication identity is not to be included, or 396 * if the authentication and authorization identities 397 * are the same. 398 * 399 * @return The ASN.1 octet string suitable for use as the control value. 400 */ 401 private static ASN1OctetString encodeValue(final boolean isAuthenticated, 402 final boolean identitiesMatch, 403 final String authNID, 404 final ReadOnlyEntry authNEntry, 405 final String authZID, 406 final ReadOnlyEntry authZEntry) 407 { 408 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4); 409 elements.add(new ASN1Boolean(TYPE_IS_AUTHENTICATED, isAuthenticated)); 410 elements.add(new ASN1Boolean(TYPE_IDENTITIES_MATCH, identitiesMatch)); 411 412 if (authNEntry != null) 413 { 414 elements.add(encodeAuthEntry(TYPE_AUTHN_ENTRY, authNID, authNEntry)); 415 } 416 417 if (authZEntry != null) 418 { 419 elements.add(encodeAuthEntry(TYPE_AUTHZ_ENTRY, authZID, authZEntry)); 420 } 421 422 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 423 } 424 425 426 427 /** 428 * Encodes the provided information as appropriate for an auth entry. 429 * 430 * @param type The BER type to use for the element. 431 * @param authID The authID to be encoded, if available. 432 * @param authEntry The entry to be encoded. 433 * 434 * @return The ASN.1 sequence containing the encoded auth entry. 435 */ 436 private static ASN1Sequence encodeAuthEntry(final byte type, 437 final String authID, 438 final ReadOnlyEntry authEntry) 439 { 440 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3); 441 442 if (authID != null) 443 { 444 elements.add(new ASN1OctetString(TYPE_AUTHID, authID)); 445 } 446 447 elements.add(new ASN1OctetString(TYPE_AUTHDN, authEntry.getDN())); 448 449 final Collection<Attribute> attributes = authEntry.getAttributes(); 450 final ArrayList<ASN1Element> attrElements = 451 new ArrayList<ASN1Element>(attributes.size()); 452 for (final Attribute a : attributes) 453 { 454 attrElements.add(a.encode()); 455 } 456 elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements)); 457 458 return new ASN1Sequence(type, elements); 459 } 460 461 462 463 /** 464 * Decodes the provided ASN.1 element into an array of auth entry elements. 465 * The first element of the array will be the auth ID, and the second element 466 * will be the read-only entry. 467 * 468 * @param element The element to decode. 469 * 470 * @return The decoded array of elements. 471 * 472 * @throws ASN1Exception If a problem occurs while performing ASN.1 parsing. 473 * 474 * @throws LDAPException If a problem occurs while performing LDAP parsing. 475 */ 476 private static Object[] decodeAuthEntry(final ASN1Element element) 477 throws ASN1Exception, LDAPException 478 { 479 String authID = null; 480 String authDN = null; 481 final ArrayList<Attribute> attrs = new ArrayList<Attribute>(); 482 483 for (final ASN1Element e : 484 ASN1Sequence.decodeAsSequence(element).elements()) 485 { 486 switch (e.getType()) 487 { 488 case TYPE_AUTHID: 489 authID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 490 break; 491 case TYPE_AUTHDN: 492 authDN = ASN1OctetString.decodeAsOctetString(e).stringValue(); 493 break; 494 case TYPE_ATTRIBUTES: 495 for (final ASN1Element ae : 496 ASN1Sequence.decodeAsSequence(e).elements()) 497 { 498 attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(ae))); 499 } 500 break; 501 default: 502 throw new LDAPException(ResultCode.DECODING_ERROR, 503 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_ENTRY_TYPE.get( 504 toHex(e.getType()))); 505 } 506 } 507 508 return new Object[] { authID, new ReadOnlyEntry(authDN, attrs) }; 509 } 510 511 512 513 /** 514 * Indicates whether the client is authenticated. 515 * 516 * @return {@code true} if the client is authenticated, or {@code false} if 517 * not. 518 */ 519 public boolean isAuthenticated() 520 { 521 return isAuthenticated; 522 } 523 524 525 526 /** 527 * Indicates whether the authentication identity and the authorization 528 * identity reference the same user. 529 * 530 * @return {@code true} if both the authentication identity and the 531 * authorization identity reference the same user, or {@code false} 532 * if not. 533 */ 534 public boolean identitiesMatch() 535 { 536 return identitiesMatch; 537 } 538 539 540 541 /** 542 * Retrieves the identifier that may be used to reference the authentication 543 * identity in the directory server, if it is available. 544 * 545 * @return The identifier that may be used to reference the authentication 546 * identity in the directory server, or {@code null} if it is not 547 * available. 548 */ 549 public String getAuthNID() 550 { 551 if ((authNID == null) && identitiesMatch) 552 { 553 return authZID; 554 } 555 556 return authNID; 557 } 558 559 560 561 /** 562 * Retrieves the entry for the user specified as the authentication identity, 563 * if it is available. 564 * 565 * @return The entry for the user specified as the authentication identity, 566 * or {@code null} if it is not available. 567 */ 568 public ReadOnlyEntry getAuthNEntry() 569 { 570 if ((authNEntry == null) && identitiesMatch) 571 { 572 return authZEntry; 573 } 574 575 return authNEntry; 576 } 577 578 579 580 /** 581 * Retrieves the identifier that may be used to reference the authorization 582 * identity in the directory server, if it is available. 583 * 584 * @return The identifier that may be used to reference the authorization 585 * identity in the directory server, or {@code null} if it is not 586 * available. 587 */ 588 public String getAuthZID() 589 { 590 if ((authZID == null) && identitiesMatch) 591 { 592 return authNID; 593 } 594 595 return authZID; 596 } 597 598 599 600 /** 601 * Retrieves the entry for the user specified as the authorization identity, 602 * if it is available. 603 * 604 * @return The entry for the user specified as the authorization identity, 605 * or {@code null} if it is not available. 606 */ 607 public ReadOnlyEntry getAuthZEntry() 608 { 609 if ((authZEntry == null) && identitiesMatch) 610 { 611 return authNEntry; 612 } 613 614 return authZEntry; 615 } 616 617 618 619 /** 620 * {@inheritDoc} 621 */ 622 @Override() 623 public String getControlName() 624 { 625 return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get(); 626 } 627 628 629 630 /** 631 * {@inheritDoc} 632 */ 633 @Override() 634 public void toString(final StringBuilder buffer) 635 { 636 buffer.append("GetAuthorizationEntryResponseControl(identitiesMatch="); 637 buffer.append(identitiesMatch); 638 639 if (authNID != null) 640 { 641 buffer.append(", authNID='"); 642 buffer.append(authNID); 643 buffer.append('\''); 644 } 645 646 if (authNEntry != null) 647 { 648 buffer.append(", authNEntry="); 649 authNEntry.toString(buffer); 650 } 651 652 if (authZID != null) 653 { 654 buffer.append(", authZID='"); 655 buffer.append(authZID); 656 buffer.append('\''); 657 } 658 659 if (authZEntry != null) 660 { 661 buffer.append(", authZEntry="); 662 authZEntry.toString(buffer); 663 } 664 665 buffer.append(')'); 666 } 667}