001/* 002 * Copyright 2012-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.extensions; 022 023 024 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.Iterator; 030import java.util.List; 031 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1Enumerated; 034import com.unboundid.asn1.ASN1OctetString; 035import com.unboundid.asn1.ASN1Sequence; 036import com.unboundid.ldap.sdk.Control; 037import com.unboundid.ldap.sdk.ExtendedRequest; 038import com.unboundid.ldap.sdk.LDAPException; 039import com.unboundid.ldap.sdk.ResultCode; 040import com.unboundid.util.Debug; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.StaticUtils; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045import com.unboundid.util.Validator; 046 047import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 048 049 050 051/** 052 * This class provides an implementation of an extended request that may be used 053 * to set the accessibility of one or more subtrees in the Ping Identity, 054 * UnboundID, or Alcatel-Lucent 8661 Directory Server. It may be used to 055 * indicate that a specified set of entries and all their subordinates should be 056 * invisible or read-only, or to restore it to full accessibility. 057 * <BR> 058 * <BLOCKQUOTE> 059 * <B>NOTE:</B> This class, and other classes within the 060 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 061 * supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661 062 * server products. These classes provide support for proprietary 063 * functionality or for external specifications that are not considered stable 064 * or mature enough to be guaranteed to work in an interoperable way with 065 * other types of LDAP servers. 066 * </BLOCKQUOTE> 067 * <BR> 068 * The OID for this request is 1.3.6.1.4.1.30221.2.6.19, and the 069 * value must have the encoding specified below. Note that the initial 070 * specification for this extended request only allowed for the specification of 071 * a single subtree, whereas it is now possible to affect the accessibility of 072 * multiple subtrees in a single request. In order to preserve compatibility 073 * with the original encoding, if there is more than one target subtree, then 074 * the first subtree must be specified as the first element in the value 075 * sequence and the remaining subtrees must be specified in the 076 * additionalSubtreeBaseDNs element. 077 * <BR><BR> 078 * <PRE> 079 * SetSubtreeAccessibilityRequestValue ::= SEQUENCE { 080 * subtreeBaseDN LDAPDN, 081 * subtreeAccessibility ENUMERATED { 082 * accessible (0), 083 * read-only-bind-allowed (1), 084 * read-only-bind-denied (2), 085 * hidden (3), 086 * ... }, 087 * bypassUserDN [0] LDAPDN OPTIONAL, 088 * additionalSubtreeBaseDNs [1] SEQUENCE OF LDAPDN OPTIONAL, 089 * ... } 090 * </PRE> 091 */ 092@NotMutable() 093@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 094public final class SetSubtreeAccessibilityExtendedRequest 095 extends ExtendedRequest 096{ 097 /** 098 * The OID (1.3.6.1.4.1.30221.2.6.19) for the set subtree accessibility 099 * extended request. 100 */ 101 public static final String SET_SUBTREE_ACCESSIBILITY_REQUEST_OID = 102 "1.3.6.1.4.1.30221.2.6.19"; 103 104 105 106 /** 107 * The BER type for the bypass user DN element of the request. 108 */ 109 private static final byte TYPE_BYPASS_USER_DN = (byte) 0x80; 110 111 112 113 /** 114 * The BER type for the set of additional subtree base DNs. 115 */ 116 private static final byte TYPE_ADDITIONAL_SUBTREE_BASE_DNS = (byte) 0xA1; 117 118 119 120 /** 121 * The serial version UID for this serializable class. 122 */ 123 private static final long serialVersionUID = -3003738735546060245L; 124 125 126 127 // The set of subtree base DNs included in the request. 128 private final List<String> subtreeBaseDNs; 129 130 // The DN of a user who will be exempted from the restrictions. This is not 131 // applicable for a subtree accessibility of ACCESSIBLE. 132 private final String bypassUserDN; 133 134 // The accessibility state to use for the target subtrees. 135 private final SubtreeAccessibilityState accessibilityState; 136 137 138 139 /** 140 * Creates a new set subtree accessibility extended request with the provided 141 * information. 142 * 143 * @param subtreeBaseDNs The set of base DNs for the target subtree. 144 * It must not be {@code null} or empty. 145 * @param accessibilityState The accessibility state to use for the target 146 * subtrees. 147 * @param bypassUserDN The DN of a user that will be allowed to bypass 148 * restrictions on the target subtrees. 149 * @param controls The set of controls to include in the request. 150 */ 151 private SetSubtreeAccessibilityExtendedRequest( 152 final Collection<String> subtreeBaseDNs, 153 final SubtreeAccessibilityState accessibilityState, 154 final String bypassUserDN, 155 final Control... controls) 156 { 157 super(SET_SUBTREE_ACCESSIBILITY_REQUEST_OID, 158 encodeValue(subtreeBaseDNs, accessibilityState, bypassUserDN), 159 controls); 160 161 this.subtreeBaseDNs = Collections.unmodifiableList( 162 new ArrayList<String>(subtreeBaseDNs)); 163 this.accessibilityState = accessibilityState; 164 this.bypassUserDN = bypassUserDN; 165 } 166 167 168 169 /** 170 * Encodes the provided information for use as the extended request value. 171 * 172 * @param subtreeBaseDNs The set of base DNs for the target subtrees. 173 * It must not be {@code null} or empty. 174 * @param accessibilityState The accessibility state to use for the target 175 * subtrees. 176 * @param bypassUserDN The DN of a user that will be allowed to bypass 177 * restrictions on the target subtrees. 178 * 179 * @return An ASN.1 octet string containing the encoded value. 180 */ 181 private static ASN1OctetString encodeValue( 182 final Collection<String> subtreeBaseDNs, 183 final SubtreeAccessibilityState accessibilityState, 184 final String bypassUserDN) 185 { 186 final Iterator<String> dnIterator = subtreeBaseDNs.iterator(); 187 final String subtreeBaseDN = dnIterator.next(); 188 Validator.ensureNotNull(subtreeBaseDN); 189 190 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4); 191 elements.add(new ASN1OctetString(subtreeBaseDN)); 192 elements.add(new ASN1Enumerated(accessibilityState.intValue())); 193 194 if (bypassUserDN != null) 195 { 196 elements.add(new ASN1OctetString(TYPE_BYPASS_USER_DN, bypassUserDN)); 197 } 198 199 if (dnIterator.hasNext()) 200 { 201 final ArrayList<ASN1Element> additionalDNElements = 202 new ArrayList<ASN1Element>(subtreeBaseDNs.size()-1); 203 while (dnIterator.hasNext()) 204 { 205 final String additionalDN = dnIterator.next(); 206 Validator.ensureNotNull(additionalDN); 207 additionalDNElements.add(new ASN1OctetString(additionalDN)); 208 } 209 elements.add(new ASN1Sequence(TYPE_ADDITIONAL_SUBTREE_BASE_DNS, 210 additionalDNElements)); 211 } 212 213 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 214 } 215 216 217 218 /** 219 * Creates a new set subtree accessibility extended request from the provided 220 * generic extended request. 221 * 222 * @param extendedRequest The generic extended request to use to create this 223 * set subtree accessibility extended request. 224 * 225 * @throws LDAPException If a problem occurs while decoding the request. 226 */ 227 public SetSubtreeAccessibilityExtendedRequest( 228 final ExtendedRequest extendedRequest) 229 throws LDAPException 230 { 231 super(extendedRequest); 232 233 final ASN1OctetString value = extendedRequest.getValue(); 234 if (value == null) 235 { 236 throw new LDAPException(ResultCode.DECODING_ERROR, 237 ERR_SET_SUBTREE_ACCESSIBILITY_NO_VALUE.get()); 238 } 239 240 try 241 { 242 final ASN1Element[] elements = 243 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 244 245 final List<String> baseDNs = new ArrayList<String>(10); 246 baseDNs.add(ASN1OctetString.decodeAsOctetString( 247 elements[0]).stringValue()); 248 249 final int accessibilityStateValue = 250 ASN1Enumerated.decodeAsEnumerated(elements[1]).intValue(); 251 accessibilityState = 252 SubtreeAccessibilityState.valueOf(accessibilityStateValue); 253 if (accessibilityState == null) 254 { 255 throw new LDAPException(ResultCode.DECODING_ERROR, 256 ERR_SET_SUBTREE_ACCESSIBILITY_INVALID_ACCESSIBILITY_STATE.get( 257 accessibilityStateValue)); 258 } 259 260 String bypassDN = null; 261 for (int i=2; i < elements.length; i++) 262 { 263 switch (elements[i].getType()) 264 { 265 case TYPE_BYPASS_USER_DN: 266 bypassDN = 267 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue(); 268 break; 269 270 case TYPE_ADDITIONAL_SUBTREE_BASE_DNS: 271 for (final ASN1Element e : 272 ASN1Sequence.decodeAsSequence(elements[i]).elements()) 273 { 274 baseDNs.add(ASN1OctetString.decodeAsOctetString(e).stringValue()); 275 } 276 break; 277 278 default: 279 throw new LDAPException(ResultCode.DECODING_ERROR, 280 ERR_SET_SUBTREE_ACCESSIBILITY_INVALID_ELEMENT_TYPE.get( 281 StaticUtils.toHex(elements[i].getType()))); 282 } 283 } 284 bypassUserDN = bypassDN; 285 subtreeBaseDNs = Collections.unmodifiableList(baseDNs); 286 } 287 catch (final LDAPException le) 288 { 289 Debug.debugException(le); 290 throw le; 291 } 292 catch (final Exception e) 293 { 294 Debug.debugException(e); 295 throw new LDAPException(ResultCode.DECODING_ERROR, 296 ERR_SET_SUBTREE_ACCESSIBILITY_CANNOT_DECODE.get( 297 StaticUtils.getExceptionMessage(e)), 298 e); 299 } 300 301 302 if ((accessibilityState == SubtreeAccessibilityState.ACCESSIBLE) && 303 (bypassUserDN != null)) 304 { 305 throw new LDAPException(ResultCode.DECODING_ERROR, 306 ERR_SET_SUBTREE_ACCESSIBILITY_UNEXPECTED_BYPASS_DN.get( 307 accessibilityState.getStateName())); 308 } 309 } 310 311 312 313 /** 314 * Creates a new set subtree accessibility extended request that will make the 315 * specified subtree accessible. 316 * 317 * @param subtreeBaseDN The base DN for the subtree to make accessible. It 318 * must not be {@code null}. 319 * @param controls The set of controls to include in the request. It 320 * may be {@code null} or empty if no controls are 321 * needed. 322 * 323 * @return The set subtree accessibility extended request that was created. 324 */ 325 public static SetSubtreeAccessibilityExtendedRequest 326 createSetAccessibleRequest(final String subtreeBaseDN, 327 final Control... controls) 328 { 329 Validator.ensureNotNull(subtreeBaseDN); 330 331 return new SetSubtreeAccessibilityExtendedRequest( 332 Arrays.asList(subtreeBaseDN), SubtreeAccessibilityState.ACCESSIBLE, 333 null, controls); 334 } 335 336 337 338 /** 339 * Creates a new set subtree accessibility extended request that will make the 340 * specified subtrees accessible. 341 * 342 * @param subtreeBaseDNs The base DNs for the subtrees to make accessible. 343 * It must not be {@code null} or empty. If multiple 344 * base DNs are specified, then all must reside below 345 * the same backend base DN. 346 * @param controls The set of controls to include in the request. It 347 * may be {@code null} or empty if no controls are 348 * needed. 349 * 350 * @return The set subtree accessibility extended request that was created. 351 */ 352 public static SetSubtreeAccessibilityExtendedRequest 353 createSetAccessibleRequest( 354 final Collection<String> subtreeBaseDNs, 355 final Control... controls) 356 { 357 Validator.ensureNotNull(subtreeBaseDNs); 358 Validator.ensureFalse(subtreeBaseDNs.isEmpty()); 359 360 return new SetSubtreeAccessibilityExtendedRequest(subtreeBaseDNs, 361 SubtreeAccessibilityState.ACCESSIBLE, null, controls); 362 } 363 364 365 366 /** 367 * Creates a new set subtree accessibility extended request that will make the 368 * specified subtree read-only. 369 * 370 * @param subtreeBaseDN The base DN for the subtree to make read-only. It 371 * must not be {@code null}. 372 * @param allowBind Indicates whether users within the specified subtree 373 * will be allowed to bind. 374 * @param bypassUserDN The DN of a user that will be allowed to perform 375 * write (add, delete, modify, and modify DN) 376 * operations in the specified subtree. It may be 377 * {@code null} if no bypass user is needed. 378 * @param controls The set of controls to include in the request. It 379 * may be {@code null} or empty if no controls are 380 * needed. 381 * 382 * @return The set subtree accessibility extended request that was created. 383 */ 384 public static SetSubtreeAccessibilityExtendedRequest 385 createSetReadOnlyRequest(final String subtreeBaseDN, 386 final boolean allowBind, 387 final String bypassUserDN, 388 final Control... controls) 389 { 390 Validator.ensureNotNull(subtreeBaseDN); 391 392 if (allowBind) 393 { 394 return new SetSubtreeAccessibilityExtendedRequest( 395 Arrays.asList(subtreeBaseDN), 396 SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED, bypassUserDN, 397 controls); 398 } 399 else 400 { 401 return new SetSubtreeAccessibilityExtendedRequest( 402 Arrays.asList(subtreeBaseDN), 403 SubtreeAccessibilityState.READ_ONLY_BIND_DENIED, bypassUserDN, 404 controls); 405 } 406 } 407 408 409 410 /** 411 * Creates a new set subtree accessibility extended request that will make the 412 * specified subtrees read-only. 413 * 414 * @param subtreeBaseDNs The base DNs for the subtrees to make read-only. 415 * It must not be {@code null} or empty. If multiple 416 * base DNs are specified, then all must reside below 417 * the same backend base DN. 418 * @param allowBind Indicates whether users within the specified 419 * subtrees will be allowed to bind. 420 * @param bypassUserDN The DN of a user that will be allowed to perform 421 * write (add, delete, modify, and modify DN) 422 * operations in the specified subtrees. It may be 423 * {@code null} if no bypass user is needed. 424 * @param controls The set of controls to include in the request. It 425 * may be {@code null} or empty if no controls are 426 * needed. 427 * 428 * @return The set subtree accessibility extended request that was created. 429 */ 430 public static SetSubtreeAccessibilityExtendedRequest 431 createSetReadOnlyRequest(final Collection<String> subtreeBaseDNs, 432 final boolean allowBind, 433 final String bypassUserDN, 434 final Control... controls) 435 { 436 Validator.ensureNotNull(subtreeBaseDNs); 437 Validator.ensureFalse(subtreeBaseDNs.isEmpty()); 438 439 if (allowBind) 440 { 441 return new SetSubtreeAccessibilityExtendedRequest(subtreeBaseDNs, 442 SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED, bypassUserDN, 443 controls); 444 } 445 else 446 { 447 return new SetSubtreeAccessibilityExtendedRequest(subtreeBaseDNs, 448 SubtreeAccessibilityState.READ_ONLY_BIND_DENIED, bypassUserDN, 449 controls); 450 } 451 } 452 453 454 455 /** 456 * Creates a new set subtree accessibility extended request that will make the 457 * specified subtree hidden. 458 * 459 * @param subtreeBaseDN The base DN for the subtree to make hidden. It must 460 * not be {@code null}. 461 * @param bypassUserDN The DN of a user that will be allowed to perform 462 * write (add, delete, modify, and modify DN) 463 * operations in the specified subtree. It may be 464 * {@code null} if no bypass user is needed. 465 * @param controls The set of controls to include in the request. It 466 * may be {@code null} or empty if no controls are 467 * needed. 468 * 469 * @return The set subtree accessibility extended request that was created. 470 */ 471 public static SetSubtreeAccessibilityExtendedRequest 472 createSetHiddenRequest(final String subtreeBaseDN, 473 final String bypassUserDN, 474 final Control... controls) 475 { 476 Validator.ensureNotNull(subtreeBaseDN); 477 478 return new SetSubtreeAccessibilityExtendedRequest( 479 Arrays.asList(subtreeBaseDN), SubtreeAccessibilityState.HIDDEN, 480 bypassUserDN, controls); 481 } 482 483 484 485 /** 486 * Creates a new set subtree accessibility extended request that will make the 487 * specified subtrees hidden. 488 * 489 * @param subtreeBaseDNs The base DNs for the subtrees to make hidden. It 490 * must not be {@code null} or empty. If multiple 491 * base DNs are specified, then all must reside below 492 * the same backend base DN. 493 * @param bypassUserDN The DN of a user that will be allowed to perform 494 * write (add, delete, modify, and modify DN) 495 * operations in the specified subtrees. It may be 496 * {@code null} if no bypass user is needed. 497 * @param controls The set of controls to include in the request. It 498 * may be {@code null} or empty if no controls are 499 * needed. 500 * 501 * @return The set subtree accessibility extended request that was created. 502 */ 503 public static SetSubtreeAccessibilityExtendedRequest 504 createSetHiddenRequest(final Collection<String> subtreeBaseDNs, 505 final String bypassUserDN, 506 final Control... controls) 507 { 508 Validator.ensureNotNull(subtreeBaseDNs); 509 Validator.ensureFalse(subtreeBaseDNs.isEmpty()); 510 511 return new SetSubtreeAccessibilityExtendedRequest(subtreeBaseDNs, 512 SubtreeAccessibilityState.HIDDEN, bypassUserDN, controls); 513 } 514 515 516 517 /** 518 * Retrieves the base DN for the target subtree. Note that if multiple 519 * base DNs are defined, this will only retrieve the first. The 520 * {@link #getSubtreeBaseDNs()} method should be used to get the complete set 521 * of target subtree base DNs. 522 * 523 * @return The base DN for the target subtree. 524 */ 525 public String getSubtreeBaseDN() 526 { 527 return subtreeBaseDNs.get(0); 528 } 529 530 531 532 /** 533 * Retrieves the base DNs for all target subtrees. 534 * 535 * @return The base DNs for all target subtrees. 536 */ 537 public List<String> getSubtreeBaseDNs() 538 { 539 return subtreeBaseDNs; 540 } 541 542 543 544 /** 545 * Retrieves the accessibility state to apply to the target subtrees. 546 * 547 * @return The accessibility state to apply to the target subtrees. 548 */ 549 public SubtreeAccessibilityState getAccessibilityState() 550 { 551 return accessibilityState; 552 } 553 554 555 556 /** 557 * Retrieves the DN of the user that will be allowed to bypass the 558 * restrictions imposed on the target subtrees for all other users. 559 * 560 * @return The DN of the user that will be allowed to bypass the restrictions 561 * imposed on the target subtrees for all other users, or 562 * {@code null} if there are no restrictions to be imposed on the 563 * target subtrees or if no bypass user is defined for those 564 * subtrees. 565 */ 566 public String getBypassUserDN() 567 { 568 return bypassUserDN; 569 } 570 571 572 573 /** 574 * {@inheritDoc} 575 */ 576 @Override() 577 public SetSubtreeAccessibilityExtendedRequest duplicate() 578 { 579 return duplicate(getControls()); 580 } 581 582 583 584 /** 585 * {@inheritDoc} 586 */ 587 @Override() 588 public SetSubtreeAccessibilityExtendedRequest duplicate( 589 final Control[] controls) 590 { 591 return new SetSubtreeAccessibilityExtendedRequest(subtreeBaseDNs, 592 accessibilityState, bypassUserDN, controls); 593 } 594 595 596 597 /** 598 * {@inheritDoc} 599 */ 600 @Override() 601 public String getExtendedRequestName() 602 { 603 return INFO_EXTENDED_REQUEST_NAME_SET_SUBTREE_ACCESSIBILITY.get(); 604 } 605 606 607 608 /** 609 * {@inheritDoc} 610 */ 611 @Override() 612 public void toString(final StringBuilder buffer) 613 { 614 buffer.append("SetSubtreeAccessibilityExtendedRequest(baseDNs={"); 615 616 final Iterator<String> dnIterator = subtreeBaseDNs.iterator(); 617 while (dnIterator.hasNext()) 618 { 619 buffer.append('"'); 620 buffer.append(dnIterator.next()); 621 buffer.append('"'); 622 623 if (dnIterator.hasNext()) 624 { 625 buffer.append(", "); 626 } 627 } 628 629 buffer.append("}, accessibilityType=\""); 630 buffer.append(accessibilityState.getStateName()); 631 buffer.append('"'); 632 633 if (bypassUserDN != null) 634 { 635 buffer.append(", bypassUserDN=\""); 636 buffer.append(bypassUserDN); 637 buffer.append('"'); 638 } 639 640 final Control[] controls = getControls(); 641 if (controls.length > 0) 642 { 643 buffer.append(", controls={"); 644 for (int i=0; i < controls.length; i++) 645 { 646 if (i > 0) 647 { 648 buffer.append(", "); 649 } 650 651 buffer.append(controls[i]); 652 } 653 buffer.append('}'); 654 } 655 656 buffer.append(')'); 657 } 658}