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