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