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