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