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