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    }