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 }