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