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