001/*
002 * Copyright 2007-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2020 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-2020 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.controls;
037
038
039
040import com.unboundid.asn1.ASN1Element;
041import com.unboundid.asn1.ASN1OctetString;
042import com.unboundid.asn1.ASN1Sequence;
043import com.unboundid.ldap.sdk.Control;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.util.Debug;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.StaticUtils;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
053
054
055
056/**
057 * This class provides an implementation of the LDAP pre-read request control
058 * as defined in <A HREF="http://www.ietf.org/rfc/rfc4527.txt">RFC 4527</A>.  It
059 * may be used to request that the server retrieve a copy of the target entry as
060 * it appeared immediately before processing a delete, modify, or modify DN
061 * operation.
062 * <BR><BR>
063 * If this control is included in a delete, modify, or modify DN request, then
064 * the corresponding response may include a {@link PreReadResponseControl}
065 * containing a version of the entry as it before after applying that change.
066 * Note that this response control will only be included if the operation was
067 * successful, so it will not be provided if the operation failed for some
068 * reason (e.g., if the change would have violated the server schema, or if the
069 * requester did not have sufficient permission to perform that operation).
070 * <BR><BR>
071 * The value of this control should contain a set of requested attributes to
072 * include in the entry that is returned.  The server should treat this set of
073 * requested attributes exactly as it treats the requested attributes from a
074 * {@link com.unboundid.ldap.sdk.SearchRequest}.  As is the case with a search
075 * request, if no attributes are specified, then all user attributes will be
076 * included.
077 * <BR><BR>
078 * The use of the LDAP pre-read request control is virtually identical to the
079 * use of the LDAP post-read request control.  See the documentation for the
080 * {@link PostReadRequestControl} for an example that illustrates its use.
081 */
082@NotMutable()
083@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
084public final class PreReadRequestControl
085       extends Control
086{
087  /**
088   * The OID (1.3.6.1.1.13.1) for the pre-read request control.
089   */
090  public static final String PRE_READ_REQUEST_OID = "1.3.6.1.1.13.1";
091
092
093
094  /**
095   * The set of requested attributes that will be used if none are provided.
096   */
097  private static final String[] NO_ATTRIBUTES = StaticUtils.NO_STRINGS;
098
099
100
101  /**
102   * The serial version UID for this serializable class.
103   */
104  private static final long serialVersionUID = 1205235290978028739L;
105
106
107
108  // The set of requested attributes to retrieve from the target entry.
109  private final String[] attributes;
110
111
112
113  /**
114   * Creates a new pre-read request control that will retrieve the specified set
115   * of attributes from the target entry.  It will be marked critical.
116   *
117   * @param  attributes  The set of attributes to retrieve from the target
118   *                     entry.  It behaves in the same way as the set of
119   *                     requested attributes for a search operation.  If this
120   *                     is empty or {@code null}, then all user attributes
121   *                     will be returned.
122   */
123  public PreReadRequestControl(final String... attributes)
124  {
125    this(true, attributes);
126  }
127
128
129
130  /**
131   * Creates a new pre-read request control that will retrieve the specified set
132   * of attributes from the target entry.
133   *
134   * @param  isCritical  Indicates whether this control should be marked
135   *                     critical.
136   * @param  attributes  The set of attributes to retrieve from the target
137   *                     entry.  It behaves in the same way as the set of
138   *                     requested attributes for a search operation.  If this
139   *                     is empty or {@code null}, then all user attributes
140   *                     will be returned.
141   */
142  public PreReadRequestControl(final boolean isCritical,
143                               final String... attributes)
144  {
145    super(PRE_READ_REQUEST_OID, isCritical, encodeValue(attributes));
146
147    if (attributes == null)
148    {
149      this.attributes = NO_ATTRIBUTES;
150    }
151    else
152    {
153      this.attributes = attributes;
154    }
155  }
156
157
158
159  /**
160   * Creates a new pre-read request control which is decoded from the provided
161   * generic control.
162   *
163   * @param  control  The generic control to be decoded as a pre-read request
164   *                  control.
165   *
166   * @throws  LDAPException  If the provided control cannot be decoded as a
167   *                         pre-read request control.
168   */
169  public PreReadRequestControl(final Control control)
170         throws LDAPException
171  {
172    super(control);
173
174    final ASN1OctetString value = control.getValue();
175    if (value == null)
176    {
177      throw new LDAPException(ResultCode.DECODING_ERROR,
178                              ERR_PRE_READ_REQUEST_NO_VALUE.get());
179    }
180
181    try
182    {
183      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
184      final ASN1Element[] attrElements =
185           ASN1Sequence.decodeAsSequence(valueElement).elements();
186      attributes = new String[attrElements.length];
187      for (int i=0; i < attrElements.length; i++)
188      {
189        attributes[i] =
190             ASN1OctetString.decodeAsOctetString(attrElements[i]).stringValue();
191      }
192    }
193    catch (final Exception e)
194    {
195      Debug.debugException(e);
196      throw new LDAPException(ResultCode.DECODING_ERROR,
197                              ERR_PRE_READ_REQUEST_CANNOT_DECODE.get(e), e);
198    }
199  }
200
201
202
203  /**
204   * Encodes the provided information into an octet string that can be used as
205   * the value for this control.
206   *
207   * @param  attributes  The set of attributes to retrieve from the target
208   *                     entry.  It behaves in the same way as the set of
209   *                     requested attributes for a search operation.  If this
210   *                     is empty or {@code null}, then all user attributes
211   *                     will be returned.
212   *
213   * @return  An ASN.1 octet string that can be used as the value for this
214   *          control.
215   */
216  private static ASN1OctetString encodeValue(final String[] attributes)
217  {
218    if ((attributes == null) || (attributes.length == 0))
219    {
220      return new ASN1OctetString(new ASN1Sequence().encode());
221    }
222
223    final ASN1OctetString[] elements = new ASN1OctetString[attributes.length];
224    for (int i=0; i < attributes.length; i++)
225    {
226      elements[i] = new ASN1OctetString(attributes[i]);
227    }
228
229    return new ASN1OctetString(new ASN1Sequence(elements).encode());
230  }
231
232
233
234  /**
235   * Retrieves the set of attributes that will be requested for inclusion in the
236   * entry returned in the response control.
237   *
238   * @return  The set of attributes that will be requested for inclusion in the
239   *          entry returned in the response control, or an empty array if all
240   *          user attributes should be returned.
241   */
242  public String[] getAttributes()
243  {
244    return attributes;
245  }
246
247
248
249  /**
250   * {@inheritDoc}
251   */
252  @Override()
253  public String getControlName()
254  {
255    return INFO_CONTROL_NAME_PRE_READ_REQUEST.get();
256  }
257
258
259
260  /**
261   * {@inheritDoc}
262   */
263  @Override()
264  public void toString(final StringBuilder buffer)
265  {
266    buffer.append("PreReadRequestControl(attributes={");
267    for (int i=0; i < attributes.length; i++)
268    {
269      if (i > 0)
270      {
271        buffer.append(", ");
272      }
273      buffer.append('\'');
274      buffer.append(attributes[i]);
275      buffer.append('\'');
276    }
277    buffer.append("}, isCritical=");
278    buffer.append(isCritical());
279    buffer.append(')');
280  }
281}