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