001/*
002 * Copyright 2008-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 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 java.util.Collections;
026import java.util.EnumSet;
027import java.util.HashMap;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032import java.util.StringTokenizer;
033import java.util.logging.Level;
034
035import com.unboundid.ldap.sdk.Attribute;
036import com.unboundid.ldap.sdk.Entry;
037import com.unboundid.ldap.sdk.ReadOnlyEntry;
038import com.unboundid.util.Debug;
039import com.unboundid.util.DebugType;
040import com.unboundid.util.NotMutable;
041import com.unboundid.util.StaticUtils;
042import com.unboundid.util.ThreadSafety;
043import com.unboundid.util.ThreadSafetyLevel;
044import com.unboundid.util.Validator;
045
046
047
048/**
049 * This class provides a mechanism for extracting the effective rights
050 * information from an entry returned for a search request that included the
051 * get effective rights request control.  In particular, it provides the ability
052 * to parse the values of the aclRights attributes in order to determine what
053 * rights the specified user may have when interacting with the entry.
054 * <BR>
055 * <BLOCKQUOTE>
056 *   <B>NOTE:</B>  This class, and other classes within the
057 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
058 *   supported for use against Ping Identity, UnboundID, and
059 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
060 *   for proprietary functionality or for external specifications that are not
061 *   considered stable or mature enough to be guaranteed to work in an
062 *   interoperable way with other types of LDAP servers.
063 * </BLOCKQUOTE>
064 * <BR>
065 * See the {@link GetEffectiveRightsRequestControl} for an example that
066 * demonstrates the use of the get effective rights request control and this
067 * entry.
068 */
069@NotMutable()
070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
071public final class EffectiveRightsEntry
072       extends ReadOnlyEntry
073{
074  /**
075   * The name of the attribute that includes the rights information.
076   */
077  private static final String ATTR_ACL_RIGHTS = "aclRights";
078
079
080
081  /**
082   * The serial version UID for this serializable class.
083   */
084  private static final long serialVersionUID = -3203127456449579174L;
085
086
087
088  // The set of entry-level rights parsed from the entry.
089  private final Set<EntryRight> entryRights;
090
091  // The set of attribute-level rights parsed from the entry, mapped from the
092  // name of the attribute to the set of the corresponding attribute rights.
093  private final Map<String,Set<AttributeRight>> attributeRights;
094
095
096
097  /**
098   * Creates a new get effective rights entry from the provided entry.
099   *
100   * @param  entry  The entry to use to create this get effective rights entry.
101   *                It must not be {@code null}.
102   */
103  public EffectiveRightsEntry(final Entry entry)
104  {
105    super(entry);
106
107    final HashSet<String> options = StaticUtils.hashSetOf("entryLevel");
108    List<Attribute> attrList =
109         getAttributesWithOptions(ATTR_ACL_RIGHTS, options);
110    if ((attrList == null) || attrList.isEmpty())
111    {
112      if (Debug.debugEnabled(DebugType.LDAP))
113      {
114        Debug.debug(Level.WARNING, DebugType.LDAP,
115             "No entry-level aclRights information contained in entry " +
116                  entry.getDN());
117      }
118
119      entryRights = null;
120    }
121    else
122    {
123      entryRights = Collections.unmodifiableSet(parseEntryRights(attrList));
124    }
125
126    options.clear();
127    options.add("attributeLevel");
128    attrList = getAttributesWithOptions(ATTR_ACL_RIGHTS, options);
129    if ((attrList == null) || attrList.isEmpty())
130    {
131      if (Debug.debugEnabled(DebugType.LDAP))
132      {
133        Debug.debug(Level.WARNING, DebugType.LDAP,
134             "No attribute-level aclRights information contained in entry " +
135                  entry.getDN());
136      }
137
138      attributeRights = null;
139    }
140    else
141    {
142      final HashMap<String,Set<AttributeRight>> attrRightsMap =
143           new HashMap<>(StaticUtils.computeMapCapacity(attrList.size()));
144      for (final Attribute a : attrList)
145      {
146        final Set<String> attrOptions = a.getOptions();
147        String attrName = null;
148        for (final String s : attrOptions)
149        {
150          if (! s.equalsIgnoreCase("attributeLevel"))
151          {
152            attrName = s;
153          }
154        }
155
156        if (attrName == null)
157        {
158          if (Debug.debugEnabled(DebugType.LDAP))
159          {
160            Debug.debug(Level.WARNING, DebugType.LDAP,
161                 "Unable to determine the target attribute name from " +
162                      a.getName());
163          }
164        }
165        else
166        {
167          final String lowerName = StaticUtils.toLowerCase(attrName);
168          final Set<AttributeRight> rights = parseAttributeRights(a);
169          attrRightsMap.put(lowerName, rights);
170        }
171      }
172
173      attributeRights = Collections.unmodifiableMap(attrRightsMap);
174    }
175  }
176
177
178
179  /**
180   * Parses the entry rights information from the entry.
181   *
182   * @param  attrList  The list of attributes to be parsed.
183   *
184   * @return  The set of entry rights parsed from the entry.
185   */
186  private static Set<EntryRight> parseEntryRights(
187                                      final List<Attribute> attrList)
188  {
189    final EnumSet<EntryRight> entryRightsSet = EnumSet.noneOf(EntryRight.class);
190    for (final Attribute a : attrList)
191    {
192      for (final String value : a.getValues())
193      {
194        final StringTokenizer tokenizer = new StringTokenizer(value, ", ");
195        while (tokenizer.hasMoreTokens())
196        {
197          final String token = tokenizer.nextToken();
198          if (token.endsWith(":1"))
199          {
200            final String rightName = token.substring(0, token.length()-2);
201            final EntryRight r = EntryRight.forName(rightName);
202            if (r == null)
203            {
204              if (Debug.debugEnabled(DebugType.LDAP))
205              {
206                Debug.debug(Level.WARNING, DebugType.LDAP,
207                     "Unrecognized entry right " + rightName);
208              }
209            }
210            else
211            {
212              entryRightsSet.add(r);
213            }
214          }
215        }
216      }
217    }
218
219    return entryRightsSet;
220  }
221
222
223
224  /**
225   * Parses the attribute rights information from the provided attribute.
226   *
227   * @param  a  The attribute to be parsed.
228   *
229   * @return  The set of attribute rights parsed from the provided attribute.
230   */
231  private static Set<AttributeRight> parseAttributeRights(final Attribute a)
232  {
233    final EnumSet<AttributeRight> rightsSet =
234         EnumSet.noneOf(AttributeRight.class);
235
236    for (final String value : a.getValues())
237    {
238      final StringTokenizer tokenizer = new StringTokenizer(value, ", ");
239      while (tokenizer.hasMoreTokens())
240      {
241        final String token = tokenizer.nextToken();
242        if (token.endsWith(":1"))
243        {
244          final String rightName = token.substring(0, token.length()-2);
245          final AttributeRight r = AttributeRight.forName(rightName);
246          if (r == null)
247          {
248            if (Debug.debugEnabled(DebugType.LDAP))
249            {
250              Debug.debug(Level.WARNING, DebugType.LDAP,
251                   "Unrecognized attribute right " + rightName);
252            }
253          }
254          else
255          {
256            rightsSet.add(r);
257          }
258        }
259      }
260    }
261
262    return rightsSet;
263  }
264
265
266
267  /**
268   * Indicates whether any access control rights information was contained in
269   * the entry.
270   *
271   * @return  {@code true} if access control rights information was contained in
272   *          the entry, or {@code false} if not.
273   */
274  public boolean rightsInformationAvailable()
275  {
276    return ((entryRights != null) || (attributeRights != null));
277  }
278
279
280
281  /**
282   * Retrieves the set of entry-level rights parsed from the entry.
283   *
284   * @return  The set of entry-level rights parsed from the entry, or
285   *          {@code null} if the entry did not have any entry-level rights
286   *          information.
287   */
288  public Set<EntryRight> getEntryRights()
289  {
290    return entryRights;
291  }
292
293
294
295  /**
296   * Indicates whether the specified entry right is granted for this entry.
297   *
298   * @param  entryRight  The entry right for which to make the determination.
299   *                     It must not be {@code null}.
300   *
301   * @return  {@code true} if the entry included entry-level rights information
302   *          and the specified entry right is granted, or {@code false} if not.
303   */
304  public boolean hasEntryRight(final EntryRight entryRight)
305  {
306    Validator.ensureNotNull(entryRight);
307
308    return ((entryRights != null) && entryRights.contains(entryRight));
309  }
310
311
312
313  /**
314   * Retrieves the set of attribute-level rights parsed from the entry, mapped
315   * from attribute name (in all lowercase characters) to the set of
316   * attribute-level rights for that attribute.
317   *
318   * @return  The set of attribute-level rights parsed from the entry, or
319   *          {@code null} if the entry did not have any attribute-level rights
320   *          information.
321   */
322  public Map<String,Set<AttributeRight>> getAttributeRights()
323  {
324    return attributeRights;
325  }
326
327
328
329  /**
330   * Retrieves the set of attribute-level rights parsed from the entry for the
331   * specified attribute.
332   *
333   * @param  attributeName  The name of the attribute for which to retrieve the
334   *                        attribute-level rights.  It must not be
335   *                        {@code null}.
336   *
337   * @return  The set of attribute-level rights for the specified attribute, or
338   *          {@code null} if the entry did not include any attribute-level
339   *          rights information for the specified attribute.
340   */
341  public Set<AttributeRight> getAttributeRights(final String attributeName)
342  {
343    Validator.ensureNotNull(attributeName);
344
345    if (attributeRights == null)
346    {
347      return null;
348    }
349
350    return attributeRights.get(StaticUtils.toLowerCase(attributeName));
351  }
352
353
354
355  /**
356   * Indicates whether the specified attribute right is granted for the
357   * specified attribute in this entry.
358   *
359   * @param  attributeRight  The attribute right for which to make the
360   *                         determination.  It must not be {@code null}.
361   * @param  attributeName   The name of the attribute for which to make the
362   *                         determination.  It must not be {@code null}.
363   *
364   * @return  {@code true} if the entry included attribute-level rights
365   *          information for the specified attribute and the indicated right is
366   *          granted, or {@code false} if not.
367   */
368  public boolean hasAttributeRight(final AttributeRight attributeRight,
369                                   final String attributeName)
370  {
371    Validator.ensureNotNull(attributeName, attributeRight);
372
373    final Set<AttributeRight> attrRights = getAttributeRights(attributeName);
374    return ((attrRights != null) && attrRights.contains(attributeRight));
375  }
376}