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 }