001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2024 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) 2008-2024 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.unboundidds.controls; 037 038 039 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.LinkedHashMap; 043import java.util.List; 044import java.util.Map; 045 046import com.unboundid.asn1.ASN1Boolean; 047import com.unboundid.asn1.ASN1Element; 048import com.unboundid.asn1.ASN1Exception; 049import com.unboundid.asn1.ASN1OctetString; 050import com.unboundid.asn1.ASN1Sequence; 051import com.unboundid.ldap.sdk.Attribute; 052import com.unboundid.ldap.sdk.BindResult; 053import com.unboundid.ldap.sdk.Control; 054import com.unboundid.ldap.sdk.DecodeableControl; 055import com.unboundid.ldap.sdk.JSONControlDecodeHelper; 056import com.unboundid.ldap.sdk.LDAPException; 057import com.unboundid.ldap.sdk.ReadOnlyEntry; 058import com.unboundid.ldap.sdk.ResultCode; 059import com.unboundid.util.Debug; 060import com.unboundid.util.NotMutable; 061import com.unboundid.util.NotNull; 062import com.unboundid.util.Nullable; 063import com.unboundid.util.StaticUtils; 064import com.unboundid.util.ThreadSafety; 065import com.unboundid.util.ThreadSafetyLevel; 066import com.unboundid.util.json.JSONArray; 067import com.unboundid.util.json.JSONBoolean; 068import com.unboundid.util.json.JSONField; 069import com.unboundid.util.json.JSONObject; 070import com.unboundid.util.json.JSONString; 071import com.unboundid.util.json.JSONValue; 072 073import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 074 075 076 077/** 078 * This class provides an implementation of an LDAP control that may be included 079 * in a bind response to provide information about the authenticated and/or 080 * authorized user. 081 * <BR> 082 * <BLOCKQUOTE> 083 * <B>NOTE:</B> This class, and other classes within the 084 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 085 * supported for use against Ping Identity, UnboundID, and 086 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 087 * for proprietary functionality or for external specifications that are not 088 * considered stable or mature enough to be guaranteed to work in an 089 * interoperable way with other types of LDAP servers. 090 * </BLOCKQUOTE> 091 * <BR> 092 * The value of this control will be encoded as follows: 093 * <PRE> 094 * GetAuthorizationEntryResponse ::= SEQUENCE { 095 * isAuthenticated [0] BOOLEAN, 096 * identitiesMatch [1] BOOLEAN, 097 * authNEntry [2] AuthEntry OPTIONAL, 098 * authZEntry [3] AuthEntry OPTIONAL } 099 * 100 * AuthEntry ::= SEQUENCE { 101 * authID [0] AuthzId OPTIONAL, 102 * authDN [1] LDAPDN, 103 * attributes [2] PartialAttributeList } 104 * </PRE> 105 * <BR><BR> 106 * See the documentation for the {@link GetAuthorizationEntryRequestControl} 107 * class for more information and an example demonstrating the use of these 108 * controls. 109 */ 110@NotMutable() 111@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 112public final class GetAuthorizationEntryResponseControl 113 extends Control 114 implements DecodeableControl 115{ 116 /** 117 * The OID (1.3.6.1.4.1.30221.2.5.6) for the get authorization entry response 118 * control. 119 */ 120 @NotNull public static final String GET_AUTHORIZATION_ENTRY_RESPONSE_OID = 121 "1.3.6.1.4.1.30221.2.5.6"; 122 123 124 125 /** 126 * The BER type for the {@code isAuthenticated} element. 127 */ 128 private static final byte TYPE_IS_AUTHENTICATED = (byte) 0x80; 129 130 131 132 /** 133 * The BER type for the {@code identitiesMatch} element. 134 */ 135 private static final byte TYPE_IDENTITIES_MATCH = (byte) 0x81; 136 137 138 139 /** 140 * The BER type for the {@code authNEntry} element. 141 */ 142 private static final byte TYPE_AUTHN_ENTRY = (byte) 0xA2; 143 144 145 146 /** 147 * The BER type for the {@code authZEntry} element. 148 */ 149 private static final byte TYPE_AUTHZ_ENTRY = (byte) 0xA3; 150 151 152 153 /** 154 * The BER type for the {@code authID} element. 155 */ 156 private static final byte TYPE_AUTHID = (byte) 0x80; 157 158 159 160 /** 161 * The BER type for the {@code authDN} element. 162 */ 163 private static final byte TYPE_AUTHDN = (byte) 0x81; 164 165 166 167 /** 168 * The BER type for the {@code attributesDN} element. 169 */ 170 private static final byte TYPE_ATTRIBUTES= (byte) 0xA2; 171 172 173 174 /** 175 * The name of the field used to hold the authentication entry in the JSON 176 * representation of this control. 177 */ 178 @NotNull private static final String JSON_FIELD_AUTHENTICATION_ENTRY = 179 "authentication-entry"; 180 181 182 183 /** 184 * The name of the field used to hold the authentication identity in the JSON 185 * representation of this control. 186 */ 187 @NotNull private static final String JSON_FIELD_AUTHENTICATION_ID = 188 "authentication-id"; 189 190 191 192 /** 193 * The name of the field used to hold the authorization entry in the JSON 194 * representation of this control. 195 */ 196 @NotNull private static final String JSON_FIELD_AUTHORIZATION_ENTRY = 197 "authorization-entry"; 198 199 200 201 /** 202 * The name of the field used to hold the authorization identity in the JSON 203 * representation of this control. 204 */ 205 @NotNull private static final String JSON_FIELD_AUTHORIZATION_ID = 206 "authorization-id"; 207 208 209 210 /** 211 * The name of the field used to hold the DN of an entry in the JSON 212 * representation of this control. 213 */ 214 @NotNull private static final String JSON_FIELD_ENTRY_DN = "_dn"; 215 216 217 218 /** 219 * The name of the field used to indicate whether authentication and 220 * authorization identities match in the JSON representation of this control. 221 */ 222 @NotNull private static final String JSON_FIELD_IDENTITIES_MATCH = 223 "identities-match"; 224 225 226 227 /** 228 * The name of the field used to indicate whether the client is authenticated 229 * in the JSON representation of this control. 230 */ 231 @NotNull private static final String JSON_FIELD_IS_AUTHENTICATED = 232 "is-authenticated"; 233 234 235 236 /** 237 * The serial version UID for this serializable class. 238 */ 239 private static final long serialVersionUID = -5443107150740697226L; 240 241 242 243 // Indicates whether the authentication and authorization identities are the 244 // same. 245 private final boolean identitiesMatch; 246 247 // Indicates whether the client is authenticated. 248 private final boolean isAuthenticated; 249 250 // The entry for the authentication identity, if available. 251 @Nullable private final ReadOnlyEntry authNEntry; 252 253 // The entry for the authorization identity, if available. 254 @Nullable private final ReadOnlyEntry authZEntry; 255 256 // The authID for the authentication identity, if available. 257 @Nullable private final String authNID; 258 259 // The authID for the authorization identity, if available. 260 @Nullable private final String authZID; 261 262 263 264 /** 265 * Creates a new empty control instance that is intended to be used only for 266 * decoding controls via the {@code DecodeableControl} interface. 267 */ 268 GetAuthorizationEntryResponseControl() 269 { 270 isAuthenticated = false; 271 identitiesMatch = true; 272 authNEntry = null; 273 authNID = null; 274 authZEntry = null; 275 authZID = null; 276 } 277 278 279 280 /** 281 * Creates a new get authorization entry response control with the provided 282 * information. 283 * 284 * @param isAuthenticated Indicates whether the client is authenticated. 285 * @param identitiesMatch Indicates whether the authentication identity is 286 * the same as the authorization identity. 287 * @param authNID The string that may be used to reference the 288 * authentication identity. It may be {@code null} 289 * if information about the authentication identity 290 * is not to be included, or if the identifier should 291 * be derived from the DN. 292 * @param authNEntry The entry for the authentication identity. It may 293 * be {@code null} if the information about the 294 * authentication identity is not to be included. 295 * @param authZID The string that may be used to reference the 296 * authorization identity. It may be {@code null} 297 * if information about the authentication identity 298 * is not to be included, if the identifier should 299 * be derived from the DN, or if the authentication 300 * and authorization identities are the same. 301 * @param authZEntry The entry for the authentication identity. It may 302 * be {@code null} if the information about the 303 * authentication identity is not to be included, or 304 * if the authentication and authorization identities 305 * are the same. 306 */ 307 public GetAuthorizationEntryResponseControl(final boolean isAuthenticated, 308 final boolean identitiesMatch, @Nullable final String authNID, 309 @Nullable final ReadOnlyEntry authNEntry, 310 @Nullable final String authZID, 311 @Nullable final ReadOnlyEntry authZEntry) 312 { 313 super(GET_AUTHORIZATION_ENTRY_RESPONSE_OID, false, 314 encodeValue(isAuthenticated, identitiesMatch, authNID, authNEntry, 315 authZID, authZEntry)); 316 317 this.isAuthenticated = isAuthenticated; 318 this.identitiesMatch = identitiesMatch; 319 this.authNID = authNID; 320 this.authNEntry = authNEntry; 321 this.authZID = authZID; 322 this.authZEntry = authZEntry; 323 } 324 325 326 327 /** 328 * Creates a new get authorization entry response control with the provided 329 * information. 330 * 331 * @param oid The OID for the control. 332 * @param isCritical Indicates whether the control should be marked 333 * critical. 334 * @param value The encoded value for the control. This may be 335 * {@code null} if no value was provided. 336 * 337 * @throws LDAPException If the provided control cannot be decoded as a get 338 * authorization entry response control. 339 */ 340 public GetAuthorizationEntryResponseControl(@NotNull final String oid, 341 final boolean isCritical, 342 @Nullable final ASN1OctetString value) 343 throws LDAPException 344 { 345 super(oid, isCritical, value); 346 347 if (value == null) 348 { 349 throw new LDAPException(ResultCode.DECODING_ERROR, 350 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_NO_VALUE.get()); 351 } 352 353 try 354 { 355 boolean isAuth = false; 356 boolean idsMatch = false; 357 String nID = null; 358 String zID = null; 359 ReadOnlyEntry nEntry = null; 360 ReadOnlyEntry zEntry = null; 361 362 final ASN1Element valElement = ASN1Element.decode(value.getValue()); 363 for (final ASN1Element e : 364 ASN1Sequence.decodeAsSequence(valElement).elements()) 365 { 366 switch (e.getType()) 367 { 368 case TYPE_IS_AUTHENTICATED: 369 isAuth = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 370 break; 371 case TYPE_IDENTITIES_MATCH: 372 idsMatch = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 373 break; 374 case TYPE_AUTHN_ENTRY: 375 final Object[] nObjects = decodeAuthEntry(e); 376 nID = (String) nObjects[0]; 377 nEntry = (ReadOnlyEntry) nObjects[1]; 378 break; 379 case TYPE_AUTHZ_ENTRY: 380 final Object[] zObjects = decodeAuthEntry(e); 381 zID = (String) zObjects[0]; 382 zEntry = (ReadOnlyEntry) zObjects[1]; 383 break; 384 default: 385 throw new LDAPException(ResultCode.DECODING_ERROR, 386 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_VALUE_TYPE.get( 387 StaticUtils.toHex(e.getType()))); 388 } 389 } 390 391 isAuthenticated = isAuth; 392 identitiesMatch = idsMatch; 393 authNID = nID; 394 authNEntry = nEntry; 395 authZID = zID; 396 authZEntry = zEntry; 397 } 398 catch (final Exception e) 399 { 400 Debug.debugException(e); 401 throw new LDAPException(ResultCode.DECODING_ERROR, 402 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_CANNOT_DECODE_VALUE.get( 403 StaticUtils.getExceptionMessage(e)), 404 e); 405 } 406 } 407 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override() 414 @NotNull() 415 public GetAuthorizationEntryResponseControl decodeControl( 416 @NotNull final String oid, 417 final boolean isCritical, 418 @Nullable final ASN1OctetString value) 419 throws LDAPException 420 { 421 return new GetAuthorizationEntryResponseControl(oid, isCritical, value); 422 } 423 424 425 426 /** 427 * Extracts a get authorization entry response control from the provided 428 * result. 429 * 430 * @param result The result from which to retrieve the get authorization 431 * entry response control. 432 * 433 * @return The get authorization entry response control contained in the 434 * provided result, or {@code null} if the result did not contain a 435 * get authorization entry response control. 436 * 437 * @throws LDAPException If a problem is encountered while attempting to 438 * decode the get authorization entry response control 439 * contained in the provided result. 440 */ 441 @Nullable() 442 public static GetAuthorizationEntryResponseControl get( 443 @NotNull final BindResult result) 444 throws LDAPException 445 { 446 final Control c = 447 result.getResponseControl(GET_AUTHORIZATION_ENTRY_RESPONSE_OID); 448 if (c == null) 449 { 450 return null; 451 } 452 453 if (c instanceof GetAuthorizationEntryResponseControl) 454 { 455 return (GetAuthorizationEntryResponseControl) c; 456 } 457 else 458 { 459 return new GetAuthorizationEntryResponseControl(c.getOID(), 460 c.isCritical(), c.getValue()); 461 } 462 } 463 464 465 466 /** 467 * Encodes the provided information appropriately for use as the value of this 468 * control. 469 * 470 * @param isAuthenticated Indicates whether the client is authenticated. 471 * @param identitiesMatch Indicates whether the authentication identity is 472 * the same as the authorization identity. 473 * @param authNID The string that may be used to reference the 474 * authentication identity. It may be {@code null} 475 * if information about the authentication identity 476 * is not to be included, or if the identifier should 477 * be derived from the DN. 478 * @param authNEntry The entry for the authentication identity. It may 479 * be {@code null} if the information about the 480 * authentication identity is not to be included. 481 * @param authZID The string that may be used to reference the 482 * authorization identity. It may be {@code null} 483 * if information about the authentication identity 484 * is not to be included, if the identifier should 485 * be derived from the DN, or if the authentication 486 * and authorization identities are the same. 487 * @param authZEntry The entry for the authentication identity. It may 488 * be {@code null} if the information about the 489 * authentication identity is not to be included, or 490 * if the authentication and authorization identities 491 * are the same. 492 * 493 * @return The ASN.1 octet string suitable for use as the control value. 494 */ 495 @NotNull() 496 private static ASN1OctetString encodeValue(final boolean isAuthenticated, 497 final boolean identitiesMatch, 498 @Nullable final String authNID, 499 @Nullable final ReadOnlyEntry authNEntry, 500 @Nullable final String authZID, 501 @Nullable final ReadOnlyEntry authZEntry) 502 { 503 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 504 elements.add(new ASN1Boolean(TYPE_IS_AUTHENTICATED, isAuthenticated)); 505 elements.add(new ASN1Boolean(TYPE_IDENTITIES_MATCH, identitiesMatch)); 506 507 if (authNEntry != null) 508 { 509 elements.add(encodeAuthEntry(TYPE_AUTHN_ENTRY, authNID, authNEntry)); 510 } 511 512 if (authZEntry != null) 513 { 514 elements.add(encodeAuthEntry(TYPE_AUTHZ_ENTRY, authZID, authZEntry)); 515 } 516 517 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 518 } 519 520 521 522 /** 523 * Encodes the provided information as appropriate for an auth entry. 524 * 525 * @param type The BER type to use for the element. 526 * @param authID The authID to be encoded, if available. 527 * @param authEntry The entry to be encoded. 528 * 529 * @return The ASN.1 sequence containing the encoded auth entry. 530 */ 531 @NotNull() 532 private static ASN1Sequence encodeAuthEntry(final byte type, 533 @Nullable final String authID, 534 @NotNull final ReadOnlyEntry authEntry) 535 { 536 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 537 538 if (authID != null) 539 { 540 elements.add(new ASN1OctetString(TYPE_AUTHID, authID)); 541 } 542 543 elements.add(new ASN1OctetString(TYPE_AUTHDN, authEntry.getDN())); 544 545 final Collection<Attribute> attributes = authEntry.getAttributes(); 546 final ArrayList<ASN1Element> attrElements = 547 new ArrayList<>(attributes.size()); 548 for (final Attribute a : attributes) 549 { 550 attrElements.add(a.encode()); 551 } 552 elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements)); 553 554 return new ASN1Sequence(type, elements); 555 } 556 557 558 559 /** 560 * Decodes the provided ASN.1 element into an array of auth entry elements. 561 * The first element of the array will be the auth ID, and the second element 562 * will be the read-only entry. 563 * 564 * @param element The element to decode. 565 * 566 * @return The decoded array of elements. 567 * 568 * @throws ASN1Exception If a problem occurs while performing ASN.1 parsing. 569 * 570 * @throws LDAPException If a problem occurs while performing LDAP parsing. 571 */ 572 @NotNull() 573 private static Object[] decodeAuthEntry(@NotNull final ASN1Element element) 574 throws ASN1Exception, LDAPException 575 { 576 String authID = null; 577 String authDN = null; 578 final ArrayList<Attribute> attrs = new ArrayList<>(20); 579 580 for (final ASN1Element e : 581 ASN1Sequence.decodeAsSequence(element).elements()) 582 { 583 switch (e.getType()) 584 { 585 case TYPE_AUTHID: 586 authID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 587 break; 588 case TYPE_AUTHDN: 589 authDN = ASN1OctetString.decodeAsOctetString(e).stringValue(); 590 break; 591 case TYPE_ATTRIBUTES: 592 for (final ASN1Element ae : 593 ASN1Sequence.decodeAsSequence(e).elements()) 594 { 595 attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(ae))); 596 } 597 break; 598 default: 599 throw new LDAPException(ResultCode.DECODING_ERROR, 600 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_ENTRY_TYPE.get( 601 StaticUtils.toHex(e.getType()))); 602 } 603 } 604 605 return new Object[] { authID, new ReadOnlyEntry(authDN, attrs) }; 606 } 607 608 609 610 /** 611 * Indicates whether the client is authenticated. 612 * 613 * @return {@code true} if the client is authenticated, or {@code false} if 614 * not. 615 */ 616 public boolean isAuthenticated() 617 { 618 return isAuthenticated; 619 } 620 621 622 623 /** 624 * Indicates whether the authentication identity and the authorization 625 * identity reference the same user. 626 * 627 * @return {@code true} if both the authentication identity and the 628 * authorization identity reference the same user, or {@code false} 629 * if not. 630 */ 631 public boolean identitiesMatch() 632 { 633 return identitiesMatch; 634 } 635 636 637 638 /** 639 * Retrieves the identifier that may be used to reference the authentication 640 * identity in the directory server, if it is available. 641 * 642 * @return The identifier that may be used to reference the authentication 643 * identity in the directory server, or {@code null} if it is not 644 * available. 645 */ 646 @Nullable() 647 public String getAuthNID() 648 { 649 if ((authNID == null) && identitiesMatch) 650 { 651 return authZID; 652 } 653 654 return authNID; 655 } 656 657 658 659 /** 660 * Retrieves the entry for the user specified as the authentication identity, 661 * if it is available. 662 * 663 * @return The entry for the user specified as the authentication identity, 664 * or {@code null} if it is not available. 665 */ 666 @Nullable() 667 public ReadOnlyEntry getAuthNEntry() 668 { 669 if ((authNEntry == null) && identitiesMatch) 670 { 671 return authZEntry; 672 } 673 674 return authNEntry; 675 } 676 677 678 679 /** 680 * Retrieves the identifier that may be used to reference the authorization 681 * identity in the directory server, if it is available. 682 * 683 * @return The identifier that may be used to reference the authorization 684 * identity in the directory server, or {@code null} if it is not 685 * available. 686 */ 687 @Nullable() 688 public String getAuthZID() 689 { 690 if ((authZID == null) && identitiesMatch) 691 { 692 return authNID; 693 } 694 695 return authZID; 696 } 697 698 699 700 /** 701 * Retrieves the entry for the user specified as the authorization identity, 702 * if it is available. 703 * 704 * @return The entry for the user specified as the authorization identity, 705 * or {@code null} if it is not available. 706 */ 707 @Nullable() 708 public ReadOnlyEntry getAuthZEntry() 709 { 710 if ((authZEntry == null) && identitiesMatch) 711 { 712 return authNEntry; 713 } 714 715 return authZEntry; 716 } 717 718 719 720 /** 721 * {@inheritDoc} 722 */ 723 @Override() 724 @NotNull() 725 public String getControlName() 726 { 727 return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get(); 728 } 729 730 731 732 /** 733 * Retrieves a representation of this get authorization entry response control 734 * as a JSON object. The JSON object uses the following fields: 735 * <UL> 736 * <LI> 737 * {@code oid} -- A mandatory string field whose value is the object 738 * identifier for this control. For the get authorization entry response 739 * control, the OID is "1.3.6.1.4.1.30221.2.5.6". 740 * </LI> 741 * <LI> 742 * {@code control-name} -- An optional string field whose value is a 743 * human-readable name for this control. This field is only intended for 744 * descriptive purposes, and when decoding a control, the {@code oid} 745 * field should be used to identify the type of control. 746 * </LI> 747 * <LI> 748 * {@code criticality} -- A mandatory Boolean field used to indicate 749 * whether this control is considered critical. 750 * </LI> 751 * <LI> 752 * {@code value-base64} -- An optional string field whose value is a 753 * base64-encoded representation of the raw value for this get 754 * authorization entry response control. Exactly one of the 755 * {@code value-base64} and {@code value-json} fields must be present. 756 * </LI> 757 * <LI> 758 * {@code value-json} -- An optional JSON object field whose value is a 759 * user-friendly representation of the value for this get authorization 760 * entry response control. Exactly one of the {@code value-base64} and 761 * {@code value-json} fields must be present, and if the 762 * {@code value-json} field is used, then it will use the following 763 * fields: 764 * <UL> 765 * <LI> 766 * {@code is-authenticated} -- A Boolean field that indicates whether 767 * the bind operation resulted in an authenticated or anonymous 768 * authentication state. 769 * </LI> 770 * <LI> 771 * {@code identities-match} -- A Boolean field that indicates whether 772 * the resulting authentication identity matches the resulting 773 * authorization identity. 774 * </LI> 775 * <LI> 776 * {@code authentication-id} -- An optional string field that holds 777 * the resulting authentication identity. 778 * </LI> 779 * <LI> 780 * {@code authentication-entry} -- An optional JSON object field that 781 * contains the content of the entry for the resulting authentication 782 * identity for the operation. If present, the object will include a 783 * "{@code _dn}" field whose value is the DN of the entry, and all 784 * other fields will have a name that is the name of an LDAP attribute 785 * in the entry and a value that is an array containing the string 786 * representations of the values for that attribute. 787 * </LI> 788 * <LI> 789 * {@code authorization-id} -- An optional string field that holds 790 * the resulting authorization identity. 791 * </LI> 792 * <LI> 793 * {@code authorization-entry} -- An optional JSON object field that 794 * contains the content of the entry for the resulting authorization 795 * identity for the operation. If present, the object will include a 796 * "{@code _dn}" field whose value is the DN of the entry, and all 797 * other fields will have a name that is the name of an LDAP attribute 798 * in the entry and a value that is an array containing the string 799 * representations of the values for that attribute. 800 * </LI> 801 * </UL> 802 * </LI> 803 * </UL> 804 * 805 * @return A JSON object that contains a representation of this control. 806 */ 807 @Override() 808 @NotNull() 809 public JSONObject toJSONControl() 810 { 811 final Map<String,JSONValue> valueFields = new LinkedHashMap<>(); 812 valueFields.put(JSON_FIELD_IS_AUTHENTICATED, 813 new JSONBoolean(isAuthenticated)); 814 valueFields.put(JSON_FIELD_IDENTITIES_MATCH, 815 new JSONBoolean(identitiesMatch)); 816 817 if (authNID != null) 818 { 819 valueFields.put(JSON_FIELD_AUTHENTICATION_ID, new JSONString(authNID)); 820 } 821 822 if (authNEntry != null) 823 { 824 valueFields.put(JSON_FIELD_AUTHENTICATION_ENTRY, 825 encodeEntryJSON(authNEntry)); 826 } 827 828 if ((authZEntry != null) && (! identitiesMatch)) 829 { 830 if (authZID != null) 831 { 832 valueFields.put(JSON_FIELD_AUTHORIZATION_ID, new JSONString(authZID)); 833 } 834 835 valueFields.put(JSON_FIELD_AUTHORIZATION_ENTRY, 836 encodeEntryJSON(authZEntry)); 837 } 838 839 840 return new JSONObject( 841 new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID, 842 GET_AUTHORIZATION_ENTRY_RESPONSE_OID), 843 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME, 844 INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get()), 845 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY, 846 isCritical()), 847 new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON, 848 new JSONObject(valueFields))); 849 } 850 851 852 853 /** 854 * Retrieves a JSON object containing an encoded representation of the 855 * provided entry. 856 * 857 * @param entry The entry to be encoded. It must not be {@code null}. 858 * 859 * @return A JSON object containing an encoded representation of the provided 860 * entry. 861 */ 862 @NotNull() 863 private static JSONObject encodeEntryJSON(@NotNull final ReadOnlyEntry entry) 864 { 865 final Map<String,JSONValue> entryFields = new LinkedHashMap<>(); 866 entryFields.put(JSON_FIELD_ENTRY_DN, new JSONString(entry.getDN())); 867 for (final Attribute a : entry.getAttributes()) 868 { 869 final List<JSONValue> attributeValues = new ArrayList<>(a.size()); 870 for (final String v : a.getValues()) 871 { 872 attributeValues.add(new JSONString(v)); 873 } 874 875 entryFields.put(a.getName(), new JSONArray(attributeValues)); 876 } 877 878 return new JSONObject(entryFields); 879 } 880 881 882 883 /** 884 * Attempts to decode the provided object as a JSON representation of a get 885 * authorization entry response control. 886 * 887 * @param controlObject The JSON object to be decoded. It must not be 888 * {@code null}. 889 * @param strict Indicates whether to use strict mode when decoding 890 * the provided JSON object. If this is {@code true}, 891 * then this method will throw an exception if the 892 * provided JSON object contains any unrecognized 893 * fields. If this is {@code false}, then unrecognized 894 * fields will be ignored. 895 * 896 * @return The get authorization entry response control that was decoded from 897 * the provided JSON object. 898 * 899 * @throws LDAPException If the provided JSON object cannot be parsed as a 900 * valid get authorization entry response control. 901 */ 902 @NotNull() 903 public static GetAuthorizationEntryResponseControl decodeJSONControl( 904 @NotNull final JSONObject controlObject, 905 final boolean strict) 906 throws LDAPException 907 { 908 final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper( 909 controlObject, strict, true, true); 910 911 final ASN1OctetString rawValue = jsonControl.getRawValue(); 912 if (rawValue != null) 913 { 914 return new GetAuthorizationEntryResponseControl(jsonControl.getOID(), 915 jsonControl.getCriticality(), rawValue); 916 } 917 918 919 final JSONObject valueObject = jsonControl.getValueObject(); 920 921 final Boolean isAuthenticated = 922 valueObject.getFieldAsBoolean(JSON_FIELD_IS_AUTHENTICATED); 923 if (isAuthenticated == null) 924 { 925 throw new LDAPException(ResultCode.DECODING_ERROR, 926 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_MISSING_FIELD.get( 927 controlObject.toSingleLineString(), 928 JSON_FIELD_IS_AUTHENTICATED)); 929 } 930 931 final Boolean identitiesMatch = 932 valueObject.getFieldAsBoolean(JSON_FIELD_IDENTITIES_MATCH); 933 if (identitiesMatch == null) 934 { 935 throw new LDAPException(ResultCode.DECODING_ERROR, 936 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_MISSING_FIELD.get( 937 controlObject.toSingleLineString(), 938 JSON_FIELD_IDENTITIES_MATCH)); 939 } 940 941 final String authenticationID = 942 valueObject.getFieldAsString(JSON_FIELD_AUTHENTICATION_ID); 943 944 final ReadOnlyEntry authenticationEntry = decodeEntryJSON(controlObject, 945 valueObject, JSON_FIELD_AUTHENTICATION_ENTRY); 946 947 final String authorizationID = 948 valueObject.getFieldAsString(JSON_FIELD_AUTHORIZATION_ID); 949 950 final ReadOnlyEntry authorizationEntry = decodeEntryJSON(controlObject, 951 valueObject, JSON_FIELD_AUTHORIZATION_ENTRY); 952 953 954 if (strict) 955 { 956 final List<String> unrecognizedFields = 957 JSONControlDecodeHelper.getControlObjectUnexpectedFields( 958 valueObject, JSON_FIELD_IS_AUTHENTICATED, 959 JSON_FIELD_IDENTITIES_MATCH, JSON_FIELD_AUTHENTICATION_ID, 960 JSON_FIELD_AUTHENTICATION_ENTRY, JSON_FIELD_AUTHORIZATION_ID, 961 JSON_FIELD_AUTHORIZATION_ENTRY); 962 if (! unrecognizedFields.isEmpty()) 963 { 964 throw new LDAPException(ResultCode.DECODING_ERROR, 965 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_UNRECOGNIZED_FIELD.get( 966 controlObject.toSingleLineString(), 967 unrecognizedFields.get(0))); 968 } 969 } 970 971 972 return new GetAuthorizationEntryResponseControl(isAuthenticated, 973 identitiesMatch, authenticationID, authenticationEntry, 974 authorizationID, authorizationEntry); 975 } 976 977 978 979 /** 980 * Retrieves and decodes the entry contained in the specified field of the 981 * provided value object. 982 * 983 * @param controlObject A JSON object containing an encoded representation 984 * of the entire control being decoded. It must not 985 * be {@code null}. 986 * @param valueObject A JSON object containing an encoded representation 987 * of the control value. It must not be {@code null}. 988 * @param entryField The name of the field containing the entry to be 989 * decoded. It must not be {@code null}. 990 * 991 * @return The entry decoded from the specified field in the value object, or 992 * {@code null} if that field is not present in the value object. 993 * 994 * @throws LDAPException If value of the specified field cannot be parsed as 995 * an entry. 996 */ 997 @Nullable() 998 private static ReadOnlyEntry decodeEntryJSON( 999 @NotNull final JSONObject controlObject, 1000 @NotNull final JSONObject valueObject, 1001 @NotNull final String entryField) 1002 throws LDAPException 1003 { 1004 final JSONObject entryObject = valueObject.getFieldAsObject(entryField); 1005 if (entryObject == null) 1006 { 1007 return null; 1008 } 1009 1010 String dn = null; 1011 final List<Attribute> attributes = 1012 new ArrayList<>(entryObject.getFields().size()); 1013 for (final Map.Entry<String,JSONValue> fieldEntry : 1014 entryObject.getFields().entrySet()) 1015 { 1016 final String fieldName = fieldEntry.getKey(); 1017 final JSONValue fieldValue = fieldEntry.getValue(); 1018 if (fieldName.equals(JSON_FIELD_ENTRY_DN)) 1019 { 1020 if (fieldValue instanceof JSONString) 1021 { 1022 dn = ((JSONString) fieldValue).stringValue(); 1023 } 1024 else 1025 { 1026 throw new LDAPException(ResultCode.DECODING_ERROR, 1027 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_DN_NOT_STRING.get( 1028 controlObject.toSingleLineString(), JSON_FIELD_ENTRY_DN, 1029 entryField)); 1030 } 1031 } 1032 else 1033 { 1034 if (fieldValue instanceof JSONArray) 1035 { 1036 final List<JSONValue> jsonValues = 1037 ((JSONArray) fieldValue).getValues(); 1038 final List<String> stringValues = new ArrayList<>(jsonValues.size()); 1039 for (final JSONValue v : jsonValues) 1040 { 1041 if (v instanceof JSONString) 1042 { 1043 stringValues.add(((JSONString) v).stringValue()); 1044 } 1045 else 1046 { 1047 throw new LDAPException(ResultCode.DECODING_ERROR, 1048 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_VALUE_NOT_STRING. 1049 get(controlObject.toSingleLineString(), entryField, 1050 fieldName)); 1051 } 1052 } 1053 1054 attributes.add(new Attribute(fieldName, stringValues)); 1055 } 1056 else 1057 { 1058 throw new LDAPException(ResultCode.DECODING_ERROR, 1059 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_ATTR_VALUE_NOT_ARRAY. 1060 get(controlObject.toSingleLineString(), entryField, 1061 fieldName)); 1062 } 1063 } 1064 } 1065 1066 if (dn == null) 1067 { 1068 throw new LDAPException(ResultCode.DECODING_ERROR, 1069 ERR_GET_AUTHORIZATION_ENTRY_REQUEST_JSON_ENTRY_MISSING_DN.get( 1070 controlObject.toSingleLineString(), entryField, 1071 JSON_FIELD_ENTRY_DN)); 1072 } 1073 1074 return new ReadOnlyEntry(dn, attributes); 1075 } 1076 1077 1078 1079 /** 1080 * {@inheritDoc} 1081 */ 1082 @Override() 1083 public void toString(@NotNull final StringBuilder buffer) 1084 { 1085 buffer.append("GetAuthorizationEntryResponseControl(identitiesMatch="); 1086 buffer.append(identitiesMatch); 1087 1088 if (authNID != null) 1089 { 1090 buffer.append(", authNID='"); 1091 buffer.append(authNID); 1092 buffer.append('\''); 1093 } 1094 1095 if (authNEntry != null) 1096 { 1097 buffer.append(", authNEntry="); 1098 authNEntry.toString(buffer); 1099 } 1100 1101 if (authZID != null) 1102 { 1103 buffer.append(", authZID='"); 1104 buffer.append(authZID); 1105 buffer.append('\''); 1106 } 1107 1108 if (authZEntry != null) 1109 { 1110 buffer.append(", authZEntry="); 1111 authZEntry.toString(buffer); 1112 } 1113 1114 buffer.append(')'); 1115 } 1116}