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