001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.authorization.dseecompat;
028
029import static org.opends.server.authorization.dseecompat.Aci.*;
030
031import java.util.LinkedList;
032import java.util.List;
033import java.util.Set;
034
035import org.forgerock.opendj.ldap.ByteString;
036import org.opends.server.core.DirectoryServer;
037import org.opends.server.types.Attribute;
038import org.opends.server.types.AttributeType;
039import org.opends.server.types.Attributes;
040import org.opends.server.types.Entry;
041
042/**
043 * This class implements the dseecompat geteffectiverights evaluation.
044 */
045public class AciEffectiveRights {
046
047  /**
048   * Value used when a aclRights attribute was seen in the search operation
049   * attribute set.
050   */
051  private static final int ACL_RIGHTS = 0x001;
052
053  /**
054   * Value used when a aclRightsInfo attribute was seen in the search operation
055   * attribute set.
056   */
057  private static final int ACL_RIGHTS_INFO = 0x002;
058
059  /**
060   * Value used when an ACI has a targattrfilters keyword match and the result
061   * of the access check was a deny.
062   */
063  private static final int ACL_TARGATTR_DENY_MATCH = 0x004;
064
065  /**
066   * Value used when an ACI has a targattrfilters keyword match and the result
067   * of the access check was an allow.
068   */
069  private static final int ACL_TARGATTR_ALLOW_MATCH = 0x008;
070
071  /**
072   * String used to build attribute type name when an aclRights result needs to
073   * be added to the return entry.
074   */
075  private static final String aclRightsAttrStr = "aclRights";
076
077  /**
078   * String used to build attribute type name when an AclRightsInfo result needs
079   * to be added to the return entry.
080   */
081  private static final String aclRightsInfoAttrStr = "aclRightsInfo";
082
083  /**
084   * String used to build attribute type name when an entryLevel rights
085   * attribute type name needs to be added to the return entry.
086   */
087  private static final String entryLevelStr = "entryLevel";
088
089  /**
090   * String used to build attribute type name when an attributeLevel rights
091   * attribute type name needs to be added to the return entry.
092   */
093  private static final String attributeLevelStr = "attributeLevel";
094
095  /**
096   * The string that is used as the attribute type name when an aclRights
097   * entryLevel evaluation needs to be added to the return entry.
098   */
099  private static final String aclRightsEntryLevelStr=
100                                     aclRightsAttrStr + ";" + entryLevelStr;
101
102  /**
103   * The string that is used as the attribute type name when an aclRights
104   * attribute level evaluation needs to be added to the return entry. This
105   * string has the attribute type name used in the evaluation appended to it to
106   * form the final attribute type name.
107   */
108  private static final String aclRightsAttributeLevelStr=
109        aclRightsAttrStr +  ";" + attributeLevelStr;
110
111  /**
112   * The string used to build attribute type name when an attribute level
113   * aclRightsInfo attribute needs to be added to the return entry. This string
114   * has the attribute type name used in the evaluation appended to it to form
115   * the final attribute type name.
116   */
117  private static final String aclRightsInfoAttrLogsStr =
118                      aclRightsInfoAttrStr + ";logs;attributeLevel";
119
120  /**
121   * The string used to build attribute type name when an entryLevel
122   * aclRightsInfo attribute needs to be added to the return entry.
123   */
124  private static final String aclRightsInfoEntryLogsStr =
125                      aclRightsInfoAttrStr + ";logs;entryLevel";
126
127  /**
128   * Attribute type used in access evaluation to see if the geteffectiverights
129   * related to the "aclRights" attribute can be performed.
130   */
131  private static AttributeType aclRights;
132
133  /**
134   * Attribute type used in access evaluation to see if the geteffectiverights
135   * related to the "aclRightsInfo" attribute can be performed.
136   */
137  private static AttributeType aclRightsInfo;
138
139  /** Attribute type used in the geteffectiverights selfwrite evaluation. */
140  private static AttributeType dnAttributeType;
141
142  /**The distinguishedName string. */
143  private static final String dnAttrStr = "distinguishedname";
144
145  /**
146   * String used to fill in the summary status field when access was allowed.
147   */
148  private static String ALLOWED="access allowed";
149
150  /**
151   * String used to fill in the summary status field when access was not
152   * allowed.
153   */
154  private static String NOT_ALLOWED="access not allowed";
155
156  /** Evaluated as anonymous user. Used to fill in summary field. */
157  private static String anonymous="anonymous";
158
159  /** Format used to build the summary string. */
160  private static String summaryFormatStr =
161        "acl_summary(%s): %s(%s) on entry/attr(%s, %s) to (%s)" +
162        " (not proxied) ( reason: %s %s)";
163
164  /**
165   * Strings below represent access denied or allowed evaluation reasons. Used
166   * to fill in the summary status field. Access evaluated an allow ACI.
167   */
168  private static String EVALUATED_ALLOW="evaluated allow";
169
170  /** Access evaluated a deny ACI. */
171  private static String EVALUATED_DENY="evaluated deny";
172
173  /** Access evaluated deny because there were no allow ACIs. */
174  private static String NO_ALLOWS="no acis matched the resource";
175
176  /** Access evaluated deny because no allow or deny ACIs evaluated. */
177  private static String NO_ALLOWS_MATCHED="no acis matched the subject";
178
179  /** Access evaluated allow because the clientDN has bypass-acl privileges. */
180  private static String SKIP_ACI="user has bypass-acl privileges";
181
182  //TODO add support for the modify-acl privilege?
183
184  /**
185   * Attempts to add the geteffectiverights asked for in the search to the entry
186   * being returned. The two geteffectiverights attributes that can be requested
187   * are: aclRights and aclRightsInfo. The aclRightsInfo attribute will return
188   * a summary string describing in human readable form, a summary of each
189   * requested evaluation result. Here is a sample aclRightsInfo summary:
190   *
191   * acl_summary(main): access_not_allowed(proxy) on
192   * entry/attr(uid=proxieduser,ou=acis,dc=example,dc=com, NULL) to
193   * (uid=superuser,ou=acis,dc=example,dc=com) (not proxied)
194   * (reason: no acis matched the resource )
195   *
196   * The aclRights attribute will return a simple
197   * string with the following format:
198   *
199   *        add:0,delete:0,read:1,write:?,proxy:0
200   *
201   * A 0 represents access denied, 1 access allowed and ? that evaluation
202   * depends on a value of an attribute (targattrfilter keyword present in ACI).
203   *
204   * There are two levels of rights information:
205   *
206   *  1. entryLevel - entry level rights information
207   *  2. attributeLevel - attribute level rights information
208   *
209   * The attribute type names are built up using subtypes:
210   *
211   *    aclRights;entryLevel - aclRights entry level presentation
212   *    aclRightsInfo;log;entryLevel;{right} - aclRightsInfo entry level
213   *        presentation for each type of right (proxy, read, write, add,
214   *        delete).
215   *    aclRights;attributeLevel;{attributeType name} - aclRights attribute
216   *        level presentation for each attribute type requested.
217   *    aclRights;attributeLevel;logs;{right};{attributeType name}
218   *        - aclRightsInfo  attribute level presentation for each attribute
219   *          type requested.
220   *
221   * @param handler  The ACI handler to use in the evaluation.
222   * @param searchAttributes  The attributes requested in the search.
223   * @param container  The LDAP operation container to use in the evaluations.
224   * @param e The entry to add the rights attributes to.
225   * @param skipCheck  True if ACI evaluation was skipped because bypass-acl
226   *                   privilege was found.
227   */
228  public static void addRightsToEntry(AciHandler handler,
229      Set<String> searchAttributes,
230      AciLDAPOperationContainer container, final Entry e,
231      boolean skipCheck)
232  {
233    if (aclRights == null)
234    {
235      aclRights = DirectoryServer.getAttributeType(aclRightsAttrStr
236          .toLowerCase());
237    }
238
239    if (aclRightsInfo == null)
240    {
241      aclRightsInfo = DirectoryServer.getAttributeType(aclRightsInfoAttrStr
242          .toLowerCase());
243    }
244
245    if (dnAttributeType == null)
246    {
247      dnAttributeType = DirectoryServer.getAttributeType(dnAttrStr);
248    }
249
250    // Check if the attributes aclRights and aclRightsInfo were requested and
251    // add attributes less those two attributes to a new list of attribute
252    // types.
253    List<AttributeType> nonRightsAttrs = new LinkedList<>();
254    int attrMask = ACI_NULL;
255    for (String a : searchAttributes)
256    {
257      if (aclRightsAttrStr.equalsIgnoreCase(a))
258      {
259        attrMask |= ACL_RIGHTS;
260      }
261      else if (aclRightsInfoAttrStr.equalsIgnoreCase(a))
262      {
263        attrMask |= ACL_RIGHTS_INFO;
264      }
265      else
266      {
267        // Check for shorthands for user attributes "*" or operational "+".
268        if ("*".equals(a))
269        {
270          // Add objectclass.
271          AttributeType ocType = DirectoryServer.getObjectClassAttributeType();
272          nonRightsAttrs.add(ocType);
273          nonRightsAttrs.addAll(e.getUserAttributes().keySet());
274        }
275        else if ("+".equals(a))
276        {
277          nonRightsAttrs.addAll(e.getOperationalAttributes().keySet());
278        }
279        else
280        {
281          nonRightsAttrs.add(DirectoryServer.getAttributeTypeOrDefault(a.toLowerCase()));
282        }
283      }
284    }
285
286    // If the special geteffectiverights attributes were not found or
287    // the user does not have both bypass-acl privs and is not allowed to
288    // perform rights evaluation -- return the entry unchanged.
289    if (attrMask == ACI_NULL
290        || (!skipCheck && !rightsAccessAllowed(container, handler, attrMask)))
291    {
292      return;
293    }
294
295    // From here on out, geteffectiverights evaluation is being performed and
296    // the container will be manipulated. First set the flag that
297    // geteffectiverights evaluation's underway and to use the authZid for
298    // authorizationDN (they might be the same).
299    container.setGetEffectiveRightsEval();
300    container.useAuthzid(true);
301
302    // If no attributes were requested return only entryLevel rights, else
303    // return attributeLevel rights and entryLevel rights. Always try and
304    // return the specific attribute rights if they exist.
305    if (!nonRightsAttrs.isEmpty())
306    {
307      addAttributeLevelRights(container, handler, attrMask, e, nonRightsAttrs,
308          skipCheck, false);
309    }
310    addAttributeLevelRights(container, handler, attrMask, e, container
311        .getSpecificAttributes(), skipCheck, true);
312    addEntryLevelRights(container, handler, attrMask, e, skipCheck);
313  }
314
315
316
317  /**
318   * Perform the attributeLevel rights evaluation on a list of specified
319   * attribute types. Each attribute has an access check done for the following
320   * rights: search, read, compare, add, delete, proxy, selfwrite_add,
321   * selfwrite_delete and write. The special rights, selfwrite_add and
322   * selfwrite_delete, use the authZid as the attribute value to evaluate
323   * against the attribute type being evaluated. The selfwrite_add performs the
324   * access check using the ACI_WRITE_ADD right and selfwrite_delete uses
325   * ACI_WRITE_ADD right. The write right is made complicated by the
326   * targattrfilters keyword, which might depend on an unknown value of an
327   * attribute type. For this case a dummy attribute value is used to try and
328   * determine if a "?" needs to be placed in the rights string. The special
329   * flag ACI_SKIP_PROXY_CHECK is always set, so that proxy evaluation is
330   * bypassed in the Aci Handler's accessAllowed method.
331   *
332   * @param container
333   *          The LDAP operation container to use in the evaluations.
334   * @param handler
335   *          The Aci Handler to use in the access evaluations.
336   * @param mask
337   *          Mask specifying what rights attribute processing to perform
338   *          (aclRights or aclRightsInfo or both).
339   * @param retEntry
340   *          The entry to return.
341   * @param attrList
342   *          The list of attribute types to iterate over.
343   * @param skipCheck
344   *          True if ACI evaluation was skipped because bypass-acl privilege
345   *          was found.
346   * @param specificAttr
347   *          True if this evaluation is result of specific attributes sent in
348   *          the request.
349   */
350  private static void addAttributeLevelRights(
351      AciLDAPOperationContainer container, AciHandler handler, int mask,
352      final Entry retEntry, List<AttributeType> attrList,
353      boolean skipCheck, boolean specificAttr)
354  {
355    if (attrList == null)
356    {
357      return;
358    }
359
360    for(AttributeType a : attrList) {
361      StringBuilder evalInfo=new StringBuilder();
362      container.setCurrentAttributeType(a);
363      container.setCurrentAttributeValue(null);
364      //Perform search check and append results.
365      container.setRights(ACI_SEARCH | ACI_SKIP_PROXY_CHECK);
366      evalInfo.append(rightsString(container, handler, skipCheck, "search"));
367      addAttrLevelRightsInfo(container, mask, a, retEntry, "search");
368      evalInfo.append(',');
369      //Perform read check and append results.
370      container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
371      evalInfo.append(rightsString(container, handler, skipCheck, "read"));
372      addAttrLevelRightsInfo(container, mask, a, retEntry, "read");
373      evalInfo.append(',');
374      //Perform compare and append results.
375      container.setRights(ACI_COMPARE | ACI_SKIP_PROXY_CHECK);
376      evalInfo.append(rightsString(container, handler, skipCheck, "compare"));
377      addAttrLevelRightsInfo(container, mask, a, retEntry, "compare");
378      evalInfo.append(',');
379      //Write right is more complicated. Create a dummy value and set that as
380      //the attribute's value. Call the special writeRightsString method, rather
381      //than rightsString.
382      ByteString val= ByteString.valueOf("dum###Val");
383      container.setCurrentAttributeValue(val);
384      evalInfo.append(attributeLevelWriteRights(container, handler, skipCheck));
385      addAttrLevelRightsInfo(container, mask, a, retEntry, "write");
386      evalInfo.append(',');
387      //Perform both selfwrite_add and selfwrite_delete and append results.
388      ByteString val1 = ByteString.valueOf(container.getClientDN().toString());
389      if(!specificAttr)
390      {
391        container.setCurrentAttributeType(dnAttributeType);
392      }
393      container.setCurrentAttributeValue(val1);
394      container.setRights(ACI_WRITE_ADD | ACI_SKIP_PROXY_CHECK);
395      evalInfo.append(rightsString(container, handler, skipCheck,
396                      "selfwrite_add"));
397      addAttrLevelRightsInfo(container, mask, a, retEntry, "selfwrite_add");
398      evalInfo.append(',');
399      container.setRights(ACI_WRITE_DELETE | ACI_SKIP_PROXY_CHECK);
400      evalInfo.append(rightsString(container, handler, skipCheck,
401                       "selfwrite_delete"));
402      addAttrLevelRightsInfo(container, mask, a, retEntry, "selfwrite_delete");
403      evalInfo.append(',');
404      container.setCurrentAttributeType(a);
405      container.setCurrentAttributeValue(null);
406                container.setRights(ACI_PROXY | ACI_SKIP_PROXY_CHECK);
407      evalInfo.append(rightsString(container, handler, skipCheck, "proxy"));
408      addAttrLevelRightsInfo(container, mask, a, retEntry, "proxy");
409      //It is possible that only the aclRightsInfo attribute type was requested.
410      //Only add the aclRights information if the aclRights attribute type was
411      //seen.
412      if(hasAttrMask(mask, ACL_RIGHTS))  {
413        String typeStr=aclRightsAttributeLevelStr + ";" +
414                a.getNormalizedPrimaryName();
415        AttributeType attributeType = DirectoryServer
416            .getDefaultAttributeType(typeStr);
417        Attribute attr = Attributes.create(attributeType, evalInfo
418            .toString());
419        //It is possible that the user might have specified the same attributes
420        //in both the search and the specific attribute part of the control.
421        //Only try to add the attribute type if it already hasn't been added.
422        if(!retEntry.hasAttribute(attributeType))
423        {
424          retEntry.addAttribute(attr,null);
425        }
426      }
427    }
428    container.setCurrentAttributeValue(null);
429    container.setCurrentAttributeType(null);
430  }
431
432
433
434  /**
435   * Perform the attributeLevel write rights evaluation. The issue here is that
436   * an ACI could contain a targattrfilters keyword that matches the attribute
437   * being evaluated. There is no way of knowing if the filter part of the
438   * targattrfilter would be successful or not. So if the ACI that allowed
439   * access, has an targattrfilter keyword, a "?" is used as the result of the
440   * write (depends on attribute value). If the allow ACI doesn't contain a
441   * targattrfilters keyword than a "1" is added. If the ACI denies then a "0"
442   * is added. If the skipCheck flag is true, then a 1 is used for the write
443   * access, since the client DN has bypass privs.
444   *
445   * @param container
446   *          The LDAP operation container to use in the evaluations.
447   * @param handler
448   *          The Aci Handler to use in the access evaluations.
449   * @param skipCheck
450   *          True if ACI evaluation was skipped because bypass-acl privilege
451   *          was found.
452   * @return A string representing the rights information.
453   */
454  private static String attributeLevelWriteRights(
455      AciLDAPOperationContainer container, AciHandler handler,
456      boolean skipCheck)
457  {
458    StringBuilder resString=new  StringBuilder();
459    //If the user has bypass-acl privs and the authzid is equal to the
460    //authorization dn, create a right string with a '1' and a valid
461    //summary. If the user has bypass-acl privs and is querying for
462    //another authzid or they don't have privs  -- fall through.
463    if(skipCheck && container.isAuthzidAuthorizationDN()) {
464      resString.append("write").append(":1");
465      container.setEvaluationResult(EnumEvalReason.SKIP_ACI, null);
466      container.setEvalSummary(createSummary(container, true));
467    } else {
468      // Reset everything.
469      container.resetEffectiveRightsParams();
470      //Reset name.
471      container.setTargAttrFiltersAciName(null);
472      container.setRights(ACI_WRITE_ADD | ACI_SKIP_PROXY_CHECK);
473      final boolean addRet = handler.accessAllowed(container)
474              && container.getTargAttrFiltersAciName() == null;
475      container.setRights(ACI_WRITE_DELETE | ACI_SKIP_PROXY_CHECK);
476      final boolean delRet = handler.accessAllowed(container)
477              && container.getTargAttrFiltersAciName() == null;
478      //If both booleans are true, then access was allowed by ACIs that did
479      //not contain targattrfilters.
480      if(addRet && delRet) {
481        resString.append("write").append(":1");
482      } else {
483        //If there is an ACI name then an ACI with a targattrfilters allowed,
484        //access. A '?' is needed because that evaluation really depends on an
485        //unknown attribute value, not the dummy value. If there is no ACI
486        //then one of the above access checks failed and a '0' is needed.
487        if(container.getTargAttrFiltersAciName() != null) {
488          resString.append("write").append(":?");
489        } else {
490          resString.append("write").append(":0");
491        }
492      }
493    }
494    return resString.toString();
495  }
496
497
498
499  /**
500   * Perform entryLevel rights evaluation. The rights string is added to the
501   * entry if the aclRights attribute was seen in the search's requested
502   * attribute set.
503   *
504   * @param container
505   *          The LDAP operation container to use in the evaluations.
506   * @param handler
507   *          The Aci Handler to use in the access evaluations.
508   * @param mask
509   *          Mask specifying what rights attribute processing to perform
510   *          (aclRights or aclRightsInfo or both).
511   * @param retEntry
512   *          The entry to return.
513   * @param skipCheck
514   *          True if ACI evaluation was skipped because bypass-acl privilege
515   *          was found.
516   */
517  private static void addEntryLevelRights(AciLDAPOperationContainer container,
518      AciHandler handler, int mask, final Entry retEntry,
519      boolean skipCheck)
520  {
521    //Perform access evaluations for rights: add, delete, read, write, proxy.
522    StringBuilder evalInfo=new StringBuilder();
523    container.setCurrentAttributeType(null);
524    container.setRights(ACI_ADD | ACI_SKIP_PROXY_CHECK);
525    evalInfo.append(rightsString(container, handler, skipCheck, "add"));
526    addEntryLevelRightsInfo(container, mask, retEntry, "add");
527    evalInfo.append(',');
528    container.setCurrentAttributeType(null);
529    container.setRights(ACI_DELETE | ACI_SKIP_PROXY_CHECK);
530    evalInfo.append(rightsString(container, handler, skipCheck, "delete"));
531    addEntryLevelRightsInfo(container, mask, retEntry, "delete");
532    evalInfo.append(',');
533    //The read right needs the entry with the full set of attributes. This was
534    //saved in the Aci Handlers maysend method.
535    container.setCurrentAttributeType(null);
536    container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
537    evalInfo.append(rightsString(container, handler, skipCheck, "read"));
538    addEntryLevelRightsInfo(container, mask, retEntry, "read");
539    evalInfo.append(',');
540    //Switch back to the entry from the Aci Handler's filterentry method.
541    container.setCurrentAttributeType(null);
542    container.setRights(ACI_WRITE| ACI_SKIP_PROXY_CHECK);
543    evalInfo.append(rightsString(container, handler, skipCheck, "write"));
544    addEntryLevelRightsInfo(container, mask, retEntry, "write");
545    evalInfo.append(',');
546    container.setCurrentAttributeType(null);
547    container.setRights(ACI_PROXY| ACI_SKIP_PROXY_CHECK);
548    evalInfo.append(rightsString(container, handler, skipCheck, "proxy"));
549    addEntryLevelRightsInfo(container, mask, retEntry, "proxy");
550    if(hasAttrMask(mask, ACL_RIGHTS)) {
551      AttributeType attributeType=
552              DirectoryServer.getDefaultAttributeType(aclRightsEntryLevelStr);
553      Attribute attr = Attributes.create(attributeType, evalInfo.toString());
554      retEntry.addAttribute(attr,null);
555    }
556  }
557
558  /**
559   * Create the rights for aclRights attributeLevel or entryLevel rights
560   * evaluation. The only right needing special treatment is the read right
561   * with no current attribute type set in the container. For that case the
562   * accessAllowedEntry method is used instead of the accessAllowed method.
563   *
564   * @param container The LDAP operation container to use in the evaluations.
565   * @param handler The Aci Handler to use in the access evaluations.
566   * @param skipCheck True if ACI evaluation was skipped because bypass-acl
567   *                  privilege was found.
568   * @param rightStr String used representation of the right we are evaluating.
569   * @return  A string representing the aclRights for the current right and
570   * attribute type/value combinations.
571   */
572  private static
573  String rightsString(AciLDAPOperationContainer container,
574                                            AciHandler handler,
575                                            boolean skipCheck, String rightStr){
576    StringBuilder resString=new  StringBuilder();
577    container.resetEffectiveRightsParams();
578    //If the user has bypass-acl privs and the authzid is equal to the
579    //authorization dn, create a right string with a '1' and a valid
580    //summary. If the user has bypass-acl privs and is querying for
581    //another authzid or they don't have privs  -- fall through.
582    if(skipCheck && container.isAuthzidAuthorizationDN()) {
583      resString.append(rightStr).append(":1");
584      container.setEvaluationResult(EnumEvalReason.SKIP_ACI, null);
585      container.setEvalSummary(createSummary(container, true));
586    } else {
587      boolean ret;
588      //Check if read right check, if so do accessAllowedEntry.
589      if(container.hasRights(ACI_READ) &&
590         container.getCurrentAttributeType() == null)
591      {
592        ret=handler.accessAllowedEntry(container);
593      }
594      else
595      {
596        ret=handler.accessAllowed(container);
597      }
598
599      resString.append(rightStr).append(ret ? ":1" : ":0");
600    }
601    return resString.toString();
602  }
603
604
605  /**
606   * Check that access is allowed on the aclRights and/or aclRightsInfo
607   * attribute types.
608   *
609   * @param container The LDAP operation container to use in the evaluations.
610   * @param handler   The Aci Handler to use in the access evaluations.
611   * @param mask Mask specifying what rights attribute processing to perform
612   *              (aclRights or aclRightsInfo or both).
613   * @return True if access to the geteffectiverights attribute types are
614   *         allowed.
615   */
616  private static
617  boolean rightsAccessAllowed(AciLDAPOperationContainer container,
618                              AciHandler handler, int mask) {
619    boolean retRight=true, retInfo=true;
620    if(hasAttrMask(mask, ACL_RIGHTS)) {
621        container.setCurrentAttributeType(aclRights);
622        container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
623        retRight=handler.accessAllowed(container);
624    }
625    if(hasAttrMask(mask, ACL_RIGHTS_INFO)) {
626        container.setCurrentAttributeType(aclRightsInfo);
627        container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
628        retInfo=handler.accessAllowed(container);
629    }
630    return retRight && retInfo;
631  }
632
633
634  /**
635   * Add aclRightsInfo attributeLevel information to the entry. This is the
636   * summary string built from the last access check.
637   *
638   * @param container  The LDAP operation container to use in the evaluations.
639   * @param mask  Mask specifying what rights attribute processing to perform
640   *              (aclRights or aclRightsInfo or both).
641   * @param aType The attribute type to use in building the attribute type name.
642   * @param retEntry The entry to add the rights information to.
643   * @param rightStr The string representation of the rights evaluated.
644   */
645  private static
646  void addAttrLevelRightsInfo(AciLDAPOperationContainer container, int mask,
647                     AttributeType aType, Entry retEntry,
648                     String rightStr) {
649
650    //Check if the aclRightsInfo attribute was requested.
651    if(hasAttrMask(mask,ACL_RIGHTS_INFO)) {
652      //Build the attribute type.
653      String typeStr=
654              aclRightsInfoAttrLogsStr + ";" + rightStr + ";" +
655              aType.getPrimaryName();
656      AttributeType attributeType=
657                DirectoryServer.getDefaultAttributeType(typeStr);
658      Attribute attr = Attributes.create(attributeType,
659          container.getEvalSummary());
660      // The attribute type might have already been added, probably
661      // not but it is possible.
662      if(!retEntry.hasAttribute(attributeType))
663      {
664        retEntry.addAttribute(attr,null);
665      }
666    }
667  }
668
669  /**
670   * Add aclRightsInfo entryLevel rights to the entry to be returned. This is
671   * the summary string built from the last access check.
672   *
673   * @param container   The LDAP operation container to use in the evaluations.
674   * @param mask Mask specifying what rights attribute processing to perform
675   *              (aclRights or aclRightsInfo or both).
676   * @param retEntry  The entry to add the rights information to.
677   * @param rightStr The string representation of the rights evaluated.
678   */
679  private static
680   void addEntryLevelRightsInfo(AciLDAPOperationContainer container, int mask,
681                       Entry retEntry,
682                      String rightStr) {
683
684     //Check if the aclRightsInfo attribute was requested.
685     if(hasAttrMask(mask,ACL_RIGHTS_INFO)) {
686      String typeStr = aclRightsInfoEntryLogsStr + ";" + rightStr;
687       AttributeType attributeType=
688                 DirectoryServer.getDefaultAttributeType(typeStr);
689       Attribute attr = Attributes.create(attributeType,
690           container.getEvalSummary());
691       retEntry.addAttribute(attr,null);
692     }
693   }
694
695  /**
696   * Check if the provided mask has a specific rights attr value.
697   *
698   * @param mask The mask with the attribute flags.
699   * @param rightsAttr The rights attr value to check for.
700   * @return True if the mask contains the rights attr value.
701   */
702  private static boolean hasAttrMask(int mask, int rightsAttr) {
703        return (mask & rightsAttr) != 0;
704  }
705
706
707  /**
708   * Create the summary string used in the aclRightsInfo log string.
709   *
710   * @param evalCtx The evaluation context to gather information from.
711   * @param evalRet The value returned from the access evaluation.
712   * @return A summary of the ACI evaluation
713   */
714  public static String createSummary(AciEvalContext evalCtx, boolean evalRet)
715  {
716    String srcStr = "main";
717    String accessStatus = evalRet ? ALLOWED : NOT_ALLOWED;
718
719    //Try and determine what reason string to use.
720    String accessReason = getEvalReason(evalCtx.getEvalReason());
721    StringBuilder decideAci =
722        getDecidingAci(evalCtx.getEvalReason(), evalCtx.getDecidingAciName());
723
724    //Only manipulate the evaluation context's targattrfilters ACI name
725    //if not a selfwrite evaluation and the context's targattrfilter match
726    //hashtable is not empty.
727    if(!evalCtx.isTargAttrFilterMatchAciEmpty() &&
728            !evalCtx.hasRights(ACI_SELF)) {
729      //If the allow list was empty then access is '0'.
730      if(evalCtx.getAllowList().isEmpty()) {
731        evalCtx.setTargAttrFiltersAciName(null);
732      } else if(evalRet) {
733        //The evaluation returned true, clear the evaluation context's
734        //targattrfilters ACI name only if a deny targattrfilters ACI
735        //was not seen. It could remove the allow.
736        if(!evalCtx.hasTargAttrFiltersMatchOp(ACL_TARGATTR_DENY_MATCH))
737        {
738          evalCtx.setTargAttrFiltersAciName(null);
739        }
740      } else {
741        //The evaluation returned false. If the reason was an
742        //explicit deny evaluation by a non-targattrfilters ACI, clear
743        //the evaluation context's targattrfilters ACI name since targattrfilter
744        //evaluation is pretty much ignored during geteffectiverights eval.
745        //Else, it was a non-explicit deny, if there is not a targattrfilters
746        //ACI that might have granted access the deny stands, else there is
747        //a targattrfilters ACI that might grant access.
748        if(evalCtx.getEvalReason() == EnumEvalReason.EVALUATED_DENY_ACI)
749        {
750          evalCtx.setTargAttrFiltersAciName(null);
751        }
752        else if(!evalCtx.hasTargAttrFiltersMatchOp(ACL_TARGATTR_ALLOW_MATCH))
753        {
754          evalCtx.setTargAttrFiltersAciName(null);
755        }
756      }
757    }
758    //Actually build the string.
759    String user=anonymous;
760    if(!evalCtx.getClientDN().isRootDN())
761    {
762      user=evalCtx.getClientDN().toString();
763    }
764    String right=evalCtx.rightToString();
765    AttributeType aType=evalCtx.getCurrentAttributeType();
766    String attrStr="NULL";
767    if(aType != null)
768    {
769      attrStr=aType.getPrimaryName();
770    }
771    if(evalCtx.getTargAttrFiltersAciName() != null)
772    {
773      decideAci.append(", access depends on attr value");
774    }
775    return String.format(summaryFormatStr, srcStr, accessStatus,
776                         right,evalCtx.getResourceDN().toString(),attrStr, user,
777                            accessReason, decideAci.toString());
778  }
779
780  private static String getEvalReason(EnumEvalReason evalReason)
781  {
782    if (evalReason == EnumEvalReason.EVALUATED_ALLOW_ACI)
783    {
784      return EVALUATED_ALLOW;
785    }
786    else if (evalReason == EnumEvalReason.EVALUATED_DENY_ACI)
787    {
788      return EVALUATED_DENY;
789    }
790    else if (evalReason == EnumEvalReason.NO_ALLOW_ACIS)
791    {
792      return NO_ALLOWS;
793    }
794    else if (evalReason == EnumEvalReason.NO_MATCHED_ALLOWS_ACIS)
795    {
796      return NO_ALLOWS_MATCHED;
797    }
798    else if (evalReason == EnumEvalReason.SKIP_ACI)
799    {
800      return SKIP_ACI;
801    }
802    return "";
803  }
804
805  private static StringBuilder getDecidingAci(EnumEvalReason evalReason,
806      String decidingAciName)
807  {
808    StringBuilder decideAci = new StringBuilder();
809    if (evalReason == EnumEvalReason.EVALUATED_ALLOW_ACI)
810    {
811      decideAci.append(", deciding_aci: ").append(decidingAciName);
812    }
813    else if (evalReason == EnumEvalReason.EVALUATED_DENY_ACI)
814    {
815      decideAci.append(", deciding_aci: ").append(decidingAciName);
816    }
817    return decideAci;
818  }
819
820  /**
821   * If the specified ACI is in the targattrfilters hashtable contained in the
822   * evaluation context, set the  evaluation context's targattrfilters match
823   * variable to either ACL_TARGATTR_DENY_MATCH or ACL_TARGATTR_ALLOW_MATCH
824   * depending on the value of the variable denyAci.
825   *
826   * @param evalCtx The evaluation context to evaluate and save information to.
827   * @param aci The ACI to match.
828   * @param denyAci True if the evaluation was a allow, false if the
829   *                evaluation was an deny or the ACI is not in the table.
830   * @return  True if the ACI was found in the hashtable.
831   */
832  public static
833  boolean  setTargAttrAci(AciEvalContext evalCtx, Aci aci, boolean denyAci) {
834    if(evalCtx.hasTargAttrFiltersMatchAci(aci)) {
835      int flag = denyAci ? ACL_TARGATTR_DENY_MATCH : ACL_TARGATTR_ALLOW_MATCH;
836      evalCtx.setTargAttrFiltersMatchOp(flag);
837      return true;
838    }
839    return false;
840  }
841
842  /**
843   * Finalizes static variables on shutdown so that we release the memory
844   * associated with them (for the unit tests) and get fresh copies if we're
845   * doing an in-core restart.
846   */
847  public static void finalizeOnShutdown() {
848    AciEffectiveRights.aclRights = null;
849    AciEffectiveRights.aclRightsInfo = null;
850    AciEffectiveRights.dnAttributeType = null;
851  }
852}