001/*
002 * Copyright 2008-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 Ping Identity Corporation
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 */
021package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025import com.unboundid.asn1.ASN1Element;
026import com.unboundid.asn1.ASN1OctetString;
027import com.unboundid.asn1.ASN1Sequence;
028import com.unboundid.ldap.sdk.Control;
029import com.unboundid.ldap.sdk.LDAPException;
030import com.unboundid.ldap.sdk.ResultCode;
031import com.unboundid.util.NotMutable;
032import com.unboundid.util.StaticUtils;
033import com.unboundid.util.ThreadSafety;
034import com.unboundid.util.ThreadSafetyLevel;
035
036import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
037import static com.unboundid.util.Debug.*;
038import static com.unboundid.util.Validator.*;
039
040
041
042/**
043 * This class provides an implementation of the get effective rights request
044 * control, which may be included in a search request to indicate that matching
045 * entries should include information about the rights a given user may have
046 * when interacting with that entry.
047 * <BR>
048 * <BLOCKQUOTE>
049 *   <B>NOTE:</B>  This class, and other classes within the
050 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
051 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
052 *   server products.  These classes provide support for proprietary
053 *   functionality or for external specifications that are not considered stable
054 *   or mature enough to be guaranteed to work in an interoperable way with
055 *   other types of LDAP servers.
056 * </BLOCKQUOTE>
057 * <BR>
058 * When the get effective rights control is included in a search request, then
059 * each entry returned may include information about the rights that the
060 * specified user has for that entry in the {@code aclRights} operational
061 * attribute.  Note that because this is an operational attribute, it must be
062 * explicitly included in the set of attributes to return.
063 * <BR><BR>
064 * If the {@code aclRights} attribute is included in the entry, then it will be
065 * present with multiple sets of options.  In one case, it will have an option
066 * of "entryLevel", which provides information about the rights that the user
067 * has for the entry in general (see the {@link EntryRight} enum for a list of
068 * the entry-level rights that can be held).  In all other cases, it will have
069 * one option of "attributeLevel" and another option that is the name of the
070 * attribute for which the set of rights is granted (see the
071 * {@link AttributeRight} enum for a list of the attribute-level rights that can
072 * be held).  In either case, the value will be a comma-delimited list of
073 * right strings, where each right string is the name of the right followed by
074 * a colon and a one to indicate that the right is granted or zero to indicate
075 * that it is not granted.  The {@link EffectiveRightsEntry} class provides a
076 * simple means of accessing the information encoded in the values of the
077 * {@code aclRights} attribute.
078 * <BR><BR>
079 * This control was designed by Sun Microsystems, and it is not the same as the
080 * get effective rights control referenced in the draft-ietf-ldapext-acl-model
081 * Internet draft.  The value for this control should be encoded as follows:
082 * <BR><BR>
083 * <PRE>
084 * GET_EFFECTIVE_RIGHTS := SEQUENCE {
085 *   authzID     authzID,
086 *   attributes  SEQUENCE OF AttributeType OPTIONAL }
087 * </PRE>
088 * <H2>Example</H2>
089 * The following example demonstrates the use of the get effective rights
090 * control to determine whether user "uid=admin,dc=example,dc=com" has the
091 * ability to change the password for the user with uid "john.doe":
092 * <PRE>
093 * SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
094 *      SearchScope.SUB, Filter.createEqualityFilter("uid", "john.doe"),
095 *      "userPassword", "aclRights");
096 * searchRequest.addControl(new GetEffectiveRightsRequestControl(
097 *      "dn:uid=admin,dc=example,dc=com"));
098 * SearchResult searchResult = connection.search(searchRequest);
099 *
100 * for (SearchResultEntry entry : searchResult.getSearchEntries())
101 * {
102 *   EffectiveRightsEntry effectiveRightsEntry =
103 *        new EffectiveRightsEntry(entry);
104 *   if (effectiveRightsEntry.rightsInformationAvailable())
105 *   {
106 *     if (effectiveRightsEntry.hasAttributeRight(AttributeRight.WRITE,
107 *          "userPassword"))
108 *     {
109 *       // The admin user has permission to change the target user's password.
110 *     }
111 *     else
112 *     {
113 *       // The admin user does not have permission to change the target user's
114 *       // password.
115 *     }
116 *   }
117 *   else
118 *   {
119 *     // No effective rights information was returned.
120 *   }
121 * }
122 * </PRE>
123 */
124@NotMutable()
125@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
126public final class GetEffectiveRightsRequestControl
127       extends Control
128{
129  /**
130   * The OID (1.3.6.1.4.1.42.2.27.9.5.2) for the get effective rights request
131   * control.
132   */
133  public static final String GET_EFFECTIVE_RIGHTS_REQUEST_OID =
134       "1.3.6.1.4.1.42.2.27.9.5.2";
135
136
137
138  /**
139   * The serial version UID for this serializable class.
140   */
141  private static final long serialVersionUID = 354733122036206073L;
142
143
144
145  // The authorization ID of the user for which to calculate the effective
146  // rights.
147  private final String authzID;
148
149  // The names of the attribute types for which to calculate the effective
150  // rights.
151  private final String[] attributes;
152
153
154
155  /**
156   * Creates a new get effective rights request control with the provided
157   * information.  It will not be marked critical.
158   *
159   * @param  authzID     The authorization ID of the user for whom the effective
160   *                     rights should be calculated.  It must not be
161   *                     {@code null}.
162   * @param  attributes  The set of attributes for which to calculate the
163   *                     effective rights.
164   */
165  public GetEffectiveRightsRequestControl(final String authzID,
166                                          final String... attributes)
167  {
168    this(false, authzID, attributes);
169  }
170
171
172
173  /**
174   * Creates a new get effective rights request control with the provided
175   * information.  It will not be marked critical.
176   *
177   * @param  isCritical  Indicates whether this control should be marked
178   *                     critical.
179   * @param  authzID     The authorization ID of the user for whom the effective
180   *                     rights should be calculated.  It must not be
181   *                     {@code null}.
182   * @param  attributes  The set of attributes for which to calculate the
183   *                     effective rights.
184   */
185  public GetEffectiveRightsRequestControl(final boolean isCritical,
186                                          final String authzID,
187                                          final String... attributes)
188  {
189    super(GET_EFFECTIVE_RIGHTS_REQUEST_OID, isCritical,
190          encodeValue(authzID, attributes));
191
192    this.authzID    = authzID;
193    this.attributes = attributes;
194  }
195
196
197
198  /**
199   * Creates a new get effective rights request control which is decoded from
200   * the provided generic control.
201   *
202   * @param  control  The generic control to be decoded as a get effective
203   *                  rights request control.
204   *
205   * @throws  LDAPException  If the provided control cannot be decoded as a get
206   *                         effective rights request control.
207   */
208  public GetEffectiveRightsRequestControl(final Control control)
209         throws LDAPException
210  {
211    super(control);
212
213    final ASN1OctetString value = control.getValue();
214    if (value == null)
215    {
216      throw new LDAPException(ResultCode.DECODING_ERROR,
217                              ERR_GER_REQUEST_NO_VALUE.get());
218    }
219
220    final ASN1Element[] elements;
221    try
222    {
223      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
224      elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
225    }
226    catch (final Exception e)
227    {
228      debugException(e);
229      throw new LDAPException(ResultCode.DECODING_ERROR,
230                              ERR_GER_REQUEST_VALUE_NOT_SEQUENCE.get(e), e);
231    }
232
233    if ((elements.length < 1) || (elements.length > 2))
234    {
235      throw new LDAPException(ResultCode.DECODING_ERROR,
236                              ERR_GER_REQUEST_INVALID_ELEMENT_COUNT.get(
237                                   elements.length));
238    }
239
240    authzID = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
241
242    if (elements.length == 2)
243    {
244      try
245      {
246        final ASN1Element[] attrElements =
247             ASN1Sequence.decodeAsSequence(elements[1]).elements();
248        attributes = new String[attrElements.length];
249        for (int i=0; i < attrElements.length; i++)
250        {
251          attributes[i] = ASN1OctetString.decodeAsOctetString(
252                               attrElements[i]).stringValue();
253        }
254      }
255      catch (final Exception e)
256      {
257        debugException(e);
258        throw new LDAPException(ResultCode.DECODING_ERROR,
259                                ERR_GER_REQUEST_CANNOT_DECODE.get(e), e);
260      }
261    }
262    else
263    {
264      attributes = StaticUtils.NO_STRINGS;
265    }
266  }
267
268
269
270  /**
271   * Encodes the provided information into an ASN.1 octet string suitable for
272   * use as the value of this control.
273   *
274   * @param  authzID     The authorization ID of the user for whom the effective
275   *                     rights should be calculated.  It must not be
276   *                     {@code null}.
277   * @param  attributes  The set of attributes for which to calculate the
278   *                     effective rights.
279   *
280   * @return  An ASN.1 octet string containing the encoded control value.
281   */
282  private static ASN1OctetString encodeValue(final String authzID,
283                                             final String[] attributes)
284  {
285    ensureNotNull(authzID);
286
287    final ASN1Element[] elements;
288    if ((attributes == null) || (attributes.length == 0))
289    {
290      elements = new ASN1Element[]
291      {
292        new ASN1OctetString(authzID),
293        new ASN1Sequence()
294      };
295    }
296    else
297    {
298      final ASN1Element[] attrElements = new ASN1Element[attributes.length];
299      for (int i=0; i < attributes.length; i++)
300      {
301        attrElements[i] = new ASN1OctetString(attributes[i]);
302      }
303
304      elements = new ASN1Element[]
305      {
306        new ASN1OctetString(authzID),
307        new ASN1Sequence(attrElements)
308      };
309    }
310
311    return new ASN1OctetString(new ASN1Sequence(elements).encode());
312  }
313
314
315
316  /**
317   * Retrieves the authorization ID of the user for whom to calculate the
318   * effective rights.
319   *
320   * @return  The authorization ID of the user for whom to calculate the
321   *          effective rights.
322   */
323  public String getAuthzID()
324  {
325    return authzID;
326  }
327
328
329
330  /**
331   * Retrieves the names of the attributes for which to calculate the effective
332   * rights information.
333   *
334   * @return  The names of the attributes for which to calculate the effective
335   *          rights information, or an empty array if no attribute names were
336   *          specified.
337   */
338  public String[] getAttributes()
339  {
340    return attributes;
341  }
342
343
344
345  /**
346   * {@inheritDoc}
347   */
348  @Override()
349  public String getControlName()
350  {
351    return INFO_CONTROL_NAME_GET_EFFECTIVE_RIGHTS_REQUEST.get();
352  }
353
354
355
356  /**
357   * {@inheritDoc}
358   */
359  @Override()
360  public void toString(final StringBuilder buffer)
361  {
362    buffer.append("GetEffectiveRightsRequestControl(authzId='");
363    buffer.append(authzID);
364    buffer.append('\'');
365
366    if (attributes.length > 0)
367    {
368      buffer.append(", attributes={");
369      for (int i=0; i < attributes.length; i++)
370      {
371        if (i > 0)
372        {
373          buffer.append(", ");
374        }
375
376        buffer.append(attributes[i]);
377      }
378      buffer.append('}');
379    }
380
381    buffer.append(", isCritical=");
382    buffer.append(isCritical());
383    buffer.append(')');
384  }
385}