001/*
002 * Copyright 2008-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2024 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) 2008-2024 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.Date;
044import java.util.Iterator;
045import java.util.List;
046
047import com.unboundid.asn1.ASN1Element;
048import com.unboundid.asn1.ASN1Enumerated;
049import com.unboundid.asn1.ASN1OctetString;
050import com.unboundid.asn1.ASN1Sequence;
051import com.unboundid.ldap.sdk.Control;
052import com.unboundid.ldap.sdk.ExtendedResult;
053import com.unboundid.ldap.sdk.LDAPException;
054import com.unboundid.ldap.sdk.ResultCode;
055import com.unboundid.util.Debug;
056import com.unboundid.util.NotMutable;
057import com.unboundid.util.NotNull;
058import com.unboundid.util.Nullable;
059import com.unboundid.util.StaticUtils;
060import com.unboundid.util.ThreadSafety;
061import com.unboundid.util.ThreadSafetyLevel;
062
063import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
064
065
066
067/**
068 * This class provides an implementation of an extended result that holds
069 * information about the response returned from a
070 * {@link GetSubtreeAccessibilityExtendedRequest}.
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 * It has an OID of 1.3.6.1.4.1.30221.1.6.21, and successful responses will have
083 * a value with the following encoding:
084 * <BR><BR>
085 * <PRE>
086 *   GetSubtreeAccessibilityResultValue ::= SEQUENCE OF SEQUENCE {
087 *        subtreeBaseDN            [0] LDAPDN,
088 *        subtreeAccessibility     [1] ENUMERATED {
089 *             accessible                 (0),
090 *             read-only-bind-allowed     (1),
091 *             read-only-bind-denied      (2),
092 *             hidden                     (3),
093 *             to-be-deleted              (4),
094 *             ... },
095 *        bypassUserDN             [2] LDAPDN OPTIONAL,
096 *        effectiveTime            [3] OCTET STRING,
097 *        ... }
098 * </PRE>
099 */
100@NotMutable()
101@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
102public final class GetSubtreeAccessibilityExtendedResult
103       extends ExtendedResult
104{
105  /**
106   * The OID (1.3.6.1.4.1.30221.1.6.21) for the get subtree accessibility
107   * extended result.
108   */
109  @NotNull public static final String GET_SUBTREE_ACCESSIBILITY_RESULT_OID =
110       "1.3.6.1.4.1.30221.1.6.21";
111
112
113
114  /**
115   * The BER type for the element that holds the base DN for a subtree
116   * accessibility restriction.
117   */
118  private static final byte TYPE_BASE_DN = (byte) 0x80;
119
120
121
122  /**
123   * The BER type for the element that holds the accessibility state for a
124   * subtree accessibility restriction.
125   */
126  private static final byte TYPE_STATE = (byte) 0x81;
127
128
129
130  /**
131   * The BER type for the element that holds the bypass user DN for a subtree
132   * accessibility restriction.
133   */
134  private static final byte TYPE_BYPASS_USER = (byte) 0x82;
135
136
137
138  /**
139   * The BER type for the element that holds the effective time for a subtree
140   * accessibility restriction.
141   */
142  private static final byte TYPE_EFFECTIVE_TIME = (byte) 0x83;
143
144
145
146  /**
147   * The serial version UID for this serializable class.
148   */
149  private static final long serialVersionUID = -3163306122775326749L;
150
151
152
153  // A list of the subtree accessibility restrictions defined in the server.
154  @Nullable private final List<SubtreeAccessibilityRestriction>
155       accessibilityRestrictions;
156
157
158
159  /**
160   * Creates a new get subtree accessibility extended result from the provided
161   * generic extended result.
162   *
163   * @param  extendedResult  The generic extended result to be decoded.
164   *
165   * @throws  LDAPException  If a problem occurs while attempting to decode the
166   *                         provided extended result as a get connection ID
167   *                         result.
168   */
169  public GetSubtreeAccessibilityExtendedResult(
170              @NotNull final ExtendedResult extendedResult)
171         throws LDAPException
172  {
173    super(extendedResult);
174
175    final ASN1OctetString value = extendedResult.getValue();
176    if (value == null)
177    {
178      accessibilityRestrictions = null;
179      return;
180    }
181
182    try
183    {
184      final ASN1Element[] restrictionElements =
185           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
186      final ArrayList<SubtreeAccessibilityRestriction> restrictionList =
187           new ArrayList<>(restrictionElements.length);
188
189      for (final ASN1Element e : restrictionElements)
190      {
191        String baseDN = null;
192        SubtreeAccessibilityState state = null;
193        String bypassDN = null;
194        Date effectiveTime = null;
195
196        for (final ASN1Element re : ASN1Sequence.decodeAsSequence(e).elements())
197        {
198          switch (re.getType())
199          {
200            case TYPE_BASE_DN:
201              baseDN = ASN1OctetString.decodeAsOctetString(re).stringValue();
202              break;
203            case TYPE_STATE:
204              state = SubtreeAccessibilityState.valueOf(
205                   ASN1Enumerated.decodeAsEnumerated(re).intValue());
206              if (state == null)
207              {
208                throw new LDAPException(ResultCode.DECODING_ERROR,
209                     ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_STATE.get(
210                          ASN1Enumerated.decodeAsEnumerated(re).intValue()));
211              }
212              break;
213            case TYPE_BYPASS_USER:
214              bypassDN = ASN1OctetString.decodeAsOctetString(re).stringValue();
215              break;
216            case TYPE_EFFECTIVE_TIME:
217              effectiveTime = StaticUtils.decodeGeneralizedTime(
218                   ASN1OctetString.decodeAsOctetString(re).stringValue());
219              break;
220            default:
221              throw new LDAPException(ResultCode.DECODING_ERROR,
222                   ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_TYPE.get(
223                        StaticUtils.toHex(re.getType())));
224          }
225        }
226
227        if (baseDN == null)
228        {
229          throw new LDAPException(ResultCode.DECODING_ERROR,
230               ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_BASE.get());
231        }
232
233        if (state == null)
234        {
235          throw new LDAPException(ResultCode.DECODING_ERROR,
236               ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_STATE.get());
237        }
238
239        if (effectiveTime == null)
240        {
241          throw new LDAPException(ResultCode.DECODING_ERROR,
242               ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_TIME.get());
243        }
244
245        restrictionList.add(new SubtreeAccessibilityRestriction(baseDN, state,
246             bypassDN, effectiveTime));
247      }
248
249      accessibilityRestrictions = Collections.unmodifiableList(restrictionList);
250    }
251    catch (final LDAPException le)
252    {
253      Debug.debugException(le);
254      throw le;
255    }
256    catch (final Exception e)
257    {
258      Debug.debugException(e);
259      throw new LDAPException(ResultCode.DECODING_ERROR,
260           ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_DECODE_ERROR.get(
261                StaticUtils.getExceptionMessage(e)),
262           e);
263    }
264  }
265
266
267
268  /**
269   * Creates a new get subtree accessibility extended result with the provided
270   * information.
271   *
272   * @param  messageID          The message ID for the LDAP message that is
273   *                            associated with this LDAP result.
274   * @param  resultCode         The result code from the response.
275   * @param  diagnosticMessage  The diagnostic message from the response, if
276   *                            available.
277   * @param  matchedDN          The matched DN from the response, if available.
278   * @param  referralURLs       The set of referral URLs from the response, if
279   *                            available.
280   * @param  restrictions       The set of subtree accessibility restrictions
281   *                            to include in the response.  It may be
282   *                            {@code null} if this represents an error
283   *                            response, or it may be empty if there are no
284   *                            subtree accessibility restrictions defined in
285   *                            the server.
286   * @param  responseControls   The set of controls from the response, if
287   *                            available.
288   */
289  public GetSubtreeAccessibilityExtendedResult(final int messageID,
290       @NotNull final ResultCode resultCode,
291       @Nullable final String diagnosticMessage,
292       @Nullable final String matchedDN,
293       @Nullable final String[] referralURLs,
294       @Nullable final Collection<SubtreeAccessibilityRestriction> restrictions,
295       @Nullable final Control... responseControls)
296  {
297    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
298          null, encodeValue(restrictions), responseControls);
299
300    if (restrictions == null)
301    {
302      accessibilityRestrictions = null;
303    }
304    else
305    {
306      accessibilityRestrictions = Collections.unmodifiableList(
307           new ArrayList<>(restrictions));
308    }
309  }
310
311
312
313  /**
314   * Encodes the value for this extended result using the provided information.
315   *
316   * @param  restrictions  The set of subtree accessibility restrictions to
317   *                       include in the response.  It may be {@code null} if
318   *                       this represents an error response, or it may be empty
319   *                       if there are no subtree accessibility restrictions
320   *                       defined in the server.
321   *
322   * @return  An ASN.1 octet string containing the properly-encoded value, or
323   *          {@code null} if there should be no value.
324   */
325  @Nullable()
326  private static ASN1OctetString encodeValue(
327       @Nullable final Collection<SubtreeAccessibilityRestriction> restrictions)
328  {
329    if (restrictions == null)
330    {
331      return null;
332    }
333
334    final ArrayList<ASN1Element> elements =
335         new ArrayList<>(restrictions.size());
336    for (final SubtreeAccessibilityRestriction r : restrictions)
337    {
338      final ArrayList<ASN1Element> restrictionElements = new ArrayList<>(4);
339      restrictionElements.add(new ASN1OctetString(TYPE_BASE_DN,
340           r.getSubtreeBaseDN()));
341      restrictionElements.add(new ASN1Enumerated(TYPE_STATE,
342           r.getAccessibilityState().intValue()));
343
344      if (r.getBypassUserDN() != null)
345      {
346        restrictionElements.add(new ASN1OctetString(TYPE_BYPASS_USER,
347             r.getBypassUserDN()));
348      }
349
350      restrictionElements.add(new ASN1OctetString(TYPE_EFFECTIVE_TIME,
351           StaticUtils.encodeGeneralizedTime(r.getEffectiveTime())));
352
353      elements.add(new ASN1Sequence(restrictionElements));
354    }
355
356    return new ASN1OctetString(new ASN1Sequence(elements).encode());
357  }
358
359
360
361  /**
362   * Retrieves a list of the subtree accessibility restrictions defined in the
363   * server.
364   *
365   * @return  A list of the subtree accessibility restrictions defined in the
366   *          server, an empty list if there are no restrictions defined, or
367   *          {@code null} if no restriction data was included in the response
368   *          from the server (e.g., because it was an error response).
369   */
370  @Nullable()
371  public List<SubtreeAccessibilityRestriction> getAccessibilityRestrictions()
372  {
373    return accessibilityRestrictions;
374  }
375
376
377
378  /**
379   * {@inheritDoc}
380   */
381  @Override()
382  @NotNull()
383  public String getExtendedResultName()
384  {
385    return INFO_EXTENDED_RESULT_NAME_GET_SUBTREE_ACCESSIBILITY.get();
386  }
387
388
389
390  /**
391   * {@inheritDoc}
392   */
393  @Override()
394  public void toString(@NotNull final StringBuilder buffer)
395  {
396    buffer.append("GetSubtreeAccessibilityExtendedResult(resultCode=");
397    buffer.append(getResultCode());
398
399    final int messageID = getMessageID();
400    if (messageID >= 0)
401    {
402      buffer.append(", messageID=");
403      buffer.append(messageID);
404    }
405
406    final String diagnosticMessage = getDiagnosticMessage();
407    if (diagnosticMessage != null)
408    {
409      buffer.append(", diagnosticMessage='");
410      buffer.append(diagnosticMessage);
411      buffer.append('\'');
412    }
413
414    final String matchedDN = getMatchedDN();
415    if (matchedDN != null)
416    {
417      buffer.append(", matchedDN='");
418      buffer.append(matchedDN);
419      buffer.append('\'');
420    }
421
422    final String[] referralURLs = getReferralURLs();
423    if ((referralURLs != null) && (referralURLs.length > 0))
424    {
425      buffer.append(", referralURLs={ '");
426      for (int i=0; i < referralURLs.length; i++)
427      {
428        if (i > 0)
429        {
430          buffer.append("', '");
431        }
432        buffer.append(referralURLs[i]);
433      }
434
435      buffer.append("' }");
436    }
437
438    if (accessibilityRestrictions != null)
439    {
440      buffer.append(", accessibilityRestrictions={");
441
442      final Iterator<SubtreeAccessibilityRestriction> iterator =
443           accessibilityRestrictions.iterator();
444      while (iterator.hasNext())
445      {
446        iterator.next().toString(buffer);
447        if (iterator.hasNext())
448        {
449          buffer.append(", ");
450        }
451      }
452
453      buffer.append('}');
454    }
455
456    final Control[] controls = getResponseControls();
457    if (controls.length > 0)
458    {
459      buffer.append(", controls={");
460      for (int i=0; i < controls.length; i++)
461      {
462        if (i > 0)
463        {
464          buffer.append(", ");
465        }
466
467        buffer.append(controls[i]);
468      }
469      buffer.append('}');
470    }
471
472    buffer.append(')');
473  }
474}