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