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