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 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 = new HashSet<>(1);
108    options.add("entryLevel");
109
110    List<Attribute> attrList =
111         getAttributesWithOptions(ATTR_ACL_RIGHTS, options);
112    if ((attrList == null) || attrList.isEmpty())
113    {
114      if (Debug.debugEnabled(DebugType.LDAP))
115      {
116        Debug.debug(Level.WARNING, DebugType.LDAP,
117             "No entry-level aclRights information contained in entry " +
118                  entry.getDN());
119      }
120
121      entryRights = null;
122    }
123    else
124    {
125      entryRights = Collections.unmodifiableSet(parseEntryRights(attrList));
126    }
127
128    options.clear();
129    options.add("attributeLevel");
130    attrList = getAttributesWithOptions(ATTR_ACL_RIGHTS, options);
131    if ((attrList == null) || attrList.isEmpty())
132    {
133      if (Debug.debugEnabled(DebugType.LDAP))
134      {
135        Debug.debug(Level.WARNING, DebugType.LDAP,
136             "No attribute-level aclRights information contained in entry " +
137                  entry.getDN());
138      }
139
140      attributeRights = null;
141    }
142    else
143    {
144      final HashMap<String,Set<AttributeRight>> attrRightsMap =
145           new HashMap<>(attrList.size());
146      for (final Attribute a : attrList)
147      {
148        final Set<String> attrOptions = a.getOptions();
149        String attrName = null;
150        for (final String s : attrOptions)
151        {
152          if (! s.equalsIgnoreCase("attributeLevel"))
153          {
154            attrName = s;
155          }
156        }
157
158        if (attrName == null)
159        {
160          if (Debug.debugEnabled(DebugType.LDAP))
161          {
162            Debug.debug(Level.WARNING, DebugType.LDAP,
163                 "Unable to determine the target attribute name from " +
164                      a.getName());
165          }
166        }
167        else
168        {
169          final String lowerName = StaticUtils.toLowerCase(attrName);
170          final Set<AttributeRight> rights = parseAttributeRights(a);
171          attrRightsMap.put(lowerName, rights);
172        }
173      }
174
175      attributeRights = Collections.unmodifiableMap(attrRightsMap);
176    }
177  }
178
179
180
181  /**
182   * Parses the entry rights information from the entry.
183   *
184   * @param  attrList  The list of attributes to be parsed.
185   *
186   * @return  The set of entry rights parsed from the entry.
187   */
188  private static Set<EntryRight> parseEntryRights(
189                                      final List<Attribute> attrList)
190  {
191    final EnumSet<EntryRight> entryRightsSet = EnumSet.noneOf(EntryRight.class);
192    for (final Attribute a : attrList)
193    {
194      for (final String value : a.getValues())
195      {
196        final StringTokenizer tokenizer = new StringTokenizer(value, ", ");
197        while (tokenizer.hasMoreTokens())
198        {
199          final String token = tokenizer.nextToken();
200          if (token.endsWith(":1"))
201          {
202            final String rightName = token.substring(0, token.length()-2);
203            final EntryRight r = EntryRight.forName(rightName);
204            if (r == null)
205            {
206              if (Debug.debugEnabled(DebugType.LDAP))
207              {
208                Debug.debug(Level.WARNING, DebugType.LDAP,
209                     "Unrecognized entry right " + rightName);
210              }
211            }
212            else
213            {
214              entryRightsSet.add(r);
215            }
216          }
217        }
218      }
219    }
220
221    return entryRightsSet;
222  }
223
224
225
226  /**
227   * Parses the attribute rights information from the provided attribute.
228   *
229   * @param  a  The attribute to be parsed.
230   *
231   * @return  The set of attribute rights parsed from the provided attribute.
232   */
233  private static Set<AttributeRight> parseAttributeRights(final Attribute a)
234  {
235    final EnumSet<AttributeRight> rightsSet =
236         EnumSet.noneOf(AttributeRight.class);
237
238    for (final String value : a.getValues())
239    {
240      final StringTokenizer tokenizer = new StringTokenizer(value, ", ");
241      while (tokenizer.hasMoreTokens())
242      {
243        final String token = tokenizer.nextToken();
244        if (token.endsWith(":1"))
245        {
246          final String rightName = token.substring(0, token.length()-2);
247          final AttributeRight r = AttributeRight.forName(rightName);
248          if (r == null)
249          {
250            if (Debug.debugEnabled(DebugType.LDAP))
251            {
252              Debug.debug(Level.WARNING, DebugType.LDAP,
253                   "Unrecognized attribute right " + rightName);
254            }
255          }
256          else
257          {
258            rightsSet.add(r);
259          }
260        }
261      }
262    }
263
264    return rightsSet;
265  }
266
267
268
269  /**
270   * Indicates whether any access control rights information was contained in
271   * the entry.
272   *
273   * @return  {@code true} if access control rights information was contained in
274   *          the entry, or {@code false} if not.
275   */
276  public boolean rightsInformationAvailable()
277  {
278    return ((entryRights != null) || (attributeRights != null));
279  }
280
281
282
283  /**
284   * Retrieves the set of entry-level rights parsed from the entry.
285   *
286   * @return  The set of entry-level rights parsed from the entry, or
287   *          {@code null} if the entry did not have any entry-level rights
288   *          information.
289   */
290  public Set<EntryRight> getEntryRights()
291  {
292    return entryRights;
293  }
294
295
296
297  /**
298   * Indicates whether the specified entry right is granted for this entry.
299   *
300   * @param  entryRight  The entry right for which to make the determination.
301   *                     It must not be {@code null}.
302   *
303   * @return  {@code true} if the entry included entry-level rights information
304   *          and the specified entry right is granted, or {@code false} if not.
305   */
306  public boolean hasEntryRight(final EntryRight entryRight)
307  {
308    Validator.ensureNotNull(entryRight);
309
310    return ((entryRights != null) && entryRights.contains(entryRight));
311  }
312
313
314
315  /**
316   * Retrieves the set of attribute-level rights parsed from the entry, mapped
317   * from attribute name (in all lowercase characters) to the set of
318   * attribute-level rights for that attribute.
319   *
320   * @return  The set of attribute-level rights parsed from the entry, or
321   *          {@code null} if the entry did not have any attribute-level rights
322   *          information.
323   */
324  public Map<String,Set<AttributeRight>> getAttributeRights()
325  {
326    return attributeRights;
327  }
328
329
330
331  /**
332   * Retrieves the set of attribute-level rights parsed from the entry for the
333   * specified attribute.
334   *
335   * @param  attributeName  The name of the attribute for which to retrieve the
336   *                        attribute-level rights.  It must not be
337   *                        {@code null}.
338   *
339   * @return  The set of attribute-level rights for the specified attribute, or
340   *          {@code null} if the entry did not include any attribute-level
341   *          rights information for the specified attribute.
342   */
343  public Set<AttributeRight> getAttributeRights(final String attributeName)
344  {
345    Validator.ensureNotNull(attributeName);
346
347    if (attributeRights == null)
348    {
349      return null;
350    }
351
352    return attributeRights.get(StaticUtils.toLowerCase(attributeName));
353  }
354
355
356
357  /**
358   * Indicates whether the specified attribute right is granted for the
359   * specified attribute in this entry.
360   *
361   * @param  attributeRight  The attribute right for which to make the
362   *                         determination.  It must not be {@code null}.
363   * @param  attributeName   The name of the attribute for which to make the
364   *                         determination.  It must not be {@code null}.
365   *
366   * @return  {@code true} if the entry included attribute-level rights
367   *          information for the specified attribute and the indicated right is
368   *          granted, or {@code false} if not.
369   */
370  public boolean hasAttributeRight(final AttributeRight attributeRight,
371                                   final String attributeName)
372  {
373    Validator.ensureNotNull(attributeName, attributeRight);
374
375    final Set<AttributeRight> attrRights = getAttributeRights(attributeName);
376    return ((attrRights != null) && attrRights.contains(attributeRight));
377  }
378}