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.*;
020import static org.opends.server.authorization.dseecompat.AciHandler.*;
021import static org.opends.server.util.ServerConstants.*;
022
023import java.net.InetAddress;
024import java.security.cert.Certificate;
025import java.util.Collection;
026import java.util.HashMap;
027import java.util.List;
028
029import org.forgerock.opendj.ldap.ByteString;
030import org.forgerock.opendj.ldap.DN;
031import org.opends.server.api.ClientConnection;
032import org.opends.server.api.Group;
033import org.opends.server.controls.GetEffectiveRightsRequestControl;
034import org.opends.server.core.AddOperation;
035import org.opends.server.core.SearchOperation;
036import org.opends.server.protocols.ldap.LDAPClientConnection;
037import org.forgerock.opendj.ldap.schema.AttributeType;
038import org.opends.server.types.*;
039
040/**
041 *  The AciContainer class contains all of the needed information to perform
042 *  both target match and evaluate an ACI. Target matching is the process
043 *  of testing if an ACI is applicable to an operation, and evaluation is
044 *  the actual access evaluation of the ACI.
045 */
046public abstract class AciContainer
047implements AciTargetMatchContext, AciEvalContext {
048
049    /**
050     * The allow and deny lists.
051     */
052    private List<Aci> denyList, allowList;
053
054    /**
055     * The attribute type in the resource entry currently being evaluated.
056     */
057    private AttributeType attributeType;
058
059    /**
060     * The attribute type value in the resource entry currently being
061     * evaluated.
062     */
063    private ByteString attributeValue;
064
065    /**
066     * True if this is the first attribute type in the resource entry being
067     * evaluated.
068     */
069    private boolean isFirst;
070
071    /**
072     * True if an entry test rule was seen during target matching of an ACI
073     * entry. A entry test rule is an ACI with targetattrs target keyword.
074     */
075    private boolean isEntryTestRule;
076
077    /**
078     * The right mask to use in the evaluation of the LDAP operation.
079     */
080    private int rightsMask;
081
082    /**
083     * The entry being evaluated (resource entry).
084     */
085    private Entry resourceEntry;
086
087    /**
088     * The client connection information.
089     */
090    private final ClientConnection clientConnection;
091
092    /**
093     * The operation being evaluated.
094     */
095    private final Operation operation;
096
097    /**
098     * True if a targattrfilters match was found.
099     */
100    private boolean targAttrFiltersMatch;
101
102    /**
103     * The authorization entry currently being evaluated. If proxied
104     * authorization is being used and the handler is doing a proxy access
105     * check, then this entry will switched to the original authorization entry
106     * rather than the proxy ID entry. If the check succeeds, it will be
107     * switched back for non-proxy access checking. If proxied authentication
108     * is not being used then this entry never changes.
109     */
110    private Entry authorizationEntry;
111
112    /**
113     * True if proxied authorization is being used.
114     */
115    private boolean proxiedAuthorization;
116
117    /**
118     * Used by proxied authorization processing. True if the entry has already
119     * been processed by an access proxy check. Some operations might perform
120     * several access checks on the same entry (modify DN), this
121     * flag is used to bypass the proxy check after the initial evaluation.
122     */
123    private boolean seenEntry;
124
125    /**
126     *  True if geteffectiverights evaluation is in progress.
127     */
128    private boolean isGetEffectiveRightsEval;
129
130    /**
131     *  True if the operation has a geteffectiverights control.
132     */
133    private boolean hasGetEffectiveRightsControl;
134
135    /**
136     * The geteffectiverights authzID in DN format.
137     */
138    private DN authzid;
139
140    /**
141     * True if the authZid should be used as the client DN, only used in
142     * geteffectiverights evaluation.
143     */
144    private boolean useAuthzid;
145
146    /**
147     * The list of specific attributes to get rights for, in addition to
148     * any attributes requested in the search.
149     */
150    private List<AttributeType> specificAttrs;
151
152    /**
153     * Table of ACIs that have targattrfilter keywords that matched. Used
154     * in geteffectiverights attributeLevel write evaluation.
155     */
156    private final HashMap<Aci,Aci> targAttrFilterAcis = new HashMap<>();
157
158    /**
159     * The name of a ACI that decided an evaluation and contained a
160     * targattrfilter keyword. Used in geteffectiverights attributeLevel
161     * write evaluation.
162     */
163    private String targAttrFiltersAciName;
164
165    /**
166     * Value that is used to store the allow/deny result of a deciding ACI
167     * containing a targattrfilter keyword.  Used in geteffectiverights
168     * attributeLevel write evaluation.
169     */
170    private int targAttrMatch;
171
172    /**
173     * The ACI that decided the last evaluation. Used in geteffectiverights
174     * loginfo processing.
175     */
176    private Aci decidingAci;
177
178    /**
179     * The reason the last evaluation decision was made. Used both
180     * in geteffectiverights loginfo processing and attributeLevel write
181     * evaluation.
182     */
183    private EnumEvalReason evalReason;
184
185    /**
186     * A summary string holding the last evaluation information in textual
187     * format. Used in geteffectiverights loginfo processing.
188     */
189    private String summaryString;
190
191   /**
192    * Flag used to determine if ACI all attributes target matched.
193    */
194    private int evalAllAttributes;
195
196   /**
197    * String used to hold a control OID string.
198    */
199    private String controlOID;
200
201   /**
202    * String used to hold an extended operation OID string.
203    */
204    private String extOpOID;
205
206    /**
207     * AuthenticationInfo class to use.
208     */
209    private AuthenticationInfo authInfo;
210
211  /**
212     * This constructor is used by all currently supported LDAP operations
213     * except the generic access control check that can be used by
214     * plugins.
215     *
216     * @param operation The Operation object being evaluated and target
217     * matching.
218     *
219     * @param rights The rights array to use in evaluation and target matching.
220     *
221     * @param entry The current entry being evaluated and target matched.
222     */
223    protected AciContainer(Operation operation, int rights, Entry entry) {
224      this.resourceEntry=entry;
225      this.operation=operation;
226      this.clientConnection=operation.getClientConnection();
227      this.authInfo = clientConnection.getAuthenticationInfo();
228
229      //If the proxied authorization control was processed, then the operation
230      //will contain an attachment containing the original authorization entry.
231      final Entry origAuthorizationEntry = (Entry) operation.getAttachment(ORIG_AUTH_ENTRY);
232      this.proxiedAuthorization = origAuthorizationEntry != null;
233      this.authorizationEntry=operation.getAuthorizationEntry();
234
235      //The ACI_READ right at constructor time can only be the result of the
236      //AciHandler.filterEntry method. This method processes the
237      //geteffectiverights control, so it needs to check for it.  There are
238      //two other checks done, because the resource entry passed to that method
239      //is filtered (it may not contain enough attribute information
240      //to evaluate correctly). See the the comments below.
241      if (rights == ACI_READ) {
242        //Checks if a geteffectiverights control was sent and
243        //sets up the structures needed.
244        GetEffectiveRightsRequestControl getEffectiveRightsControl =
245              (GetEffectiveRightsRequestControl)
246                      operation.getAttachment(OID_GET_EFFECTIVE_RIGHTS);
247        if (getEffectiveRightsControl != null
248            && operation instanceof SearchOperation)
249        {
250          hasGetEffectiveRightsControl = true;
251          if (getEffectiveRightsControl.getAuthzDN() == null) {
252            this.authzid = getClientDN();
253          } else {
254            this.authzid = getEffectiveRightsControl.getAuthzDN();
255          }
256          this.specificAttrs = getEffectiveRightsControl.getAttributes();
257        }
258
259        //If an ACI evaluated because of an Targetattr="*", then the
260        //AciHandler.maySend method signaled this via adding this attachment
261        //string.
262        String allUserAttrs=
263                  (String)operation.getAttachment(ALL_USER_ATTRS_MATCHED);
264        if(allUserAttrs != null)
265        {
266          evalAllAttributes |= ACI_USER_ATTR_STAR_MATCHED;
267        }
268        //If an ACI evaluated because of an Targetattr="+", then the
269        //AciHandler.maySend method signaled this via adding this attachment
270        //string.
271        String allOpAttrs=(String)operation.getAttachment(ALL_OP_ATTRS_MATCHED);
272        if(allOpAttrs != null)
273        {
274          evalAllAttributes |= ACI_OP_ATTR_PLUS_MATCHED;
275        }
276      }
277
278      //Reference the current authorization entry, so it can be put back
279      //if an access proxy check was performed.
280        this.rightsMask = rights;
281    }
282
283    /**
284     * This constructor is used by the generic access control check.
285     *
286     * @param operation The operation to use in the access evaluation.
287     * @param e The entry to check access for.
288     * @param authInfo The authentication information to use in the evaluation.
289     * @param rights The rights to check access of.
290     */
291    protected AciContainer(Operation operation, Entry e,
292                            AuthenticationInfo authInfo,
293                            int rights) {
294        this.resourceEntry=e;
295        this.operation=operation;
296        this.clientConnection=operation.getClientConnection();
297        this.authInfo = authInfo;
298        this.authorizationEntry = authInfo.getAuthorizationEntry();
299        this.rightsMask = rights;
300    }
301  /**
302   * Returns true if an entry has already been processed by an access proxy
303   * check.
304   *
305   * @return True if an entry has already been processed by an access proxy
306   * check.
307   */
308   public boolean hasSeenEntry() {
309      return this.seenEntry;
310    }
311
312  /**
313   * Set to true if an entry has already been processed by an access proxy
314   * check.
315   *
316   * @param val The value to set the seenEntry boolean to.
317   */
318    public void setSeenEntry(boolean val) {
319     this.seenEntry=val;
320    }
321
322    /** {@inheritDoc} */
323    @Override
324    public boolean isProxiedAuthorization() {
325         return this.proxiedAuthorization;
326    }
327
328    /** {@inheritDoc} */
329    @Override
330    public boolean isGetEffectiveRightsEval() {
331        return this.isGetEffectiveRightsEval;
332    }
333
334  /**
335   * The container is going to be used in a geteffectiverights evaluation, set
336   * the flag isGetEffectiveRightsEval to true.
337   */
338  public void setGetEffectiveRightsEval() {
339       this.isGetEffectiveRightsEval=true;
340    }
341
342  /**
343   * Return true if the container is being used in a geteffectiverights
344   * evaluation.
345   *
346   * @return True if the container is being used in a geteffectiverights
347   * evaluation.
348   */
349    public boolean hasGetEffectiveRightsControl() {
350      return this.hasGetEffectiveRightsControl;
351    }
352
353  /**
354   * Use the DN from the geteffectiverights control's authzId as the
355   * client DN, rather than the authorization entry's DN.
356   *
357   * @param v The valued to set the useAuthzid to.
358   */
359    public void useAuthzid(boolean v) {
360       this.useAuthzid=v;
361    }
362
363  /**
364   * Return the list of additional attributes specified in the
365   * geteffectiverights control.
366   *
367   * @return The list of attributes to return rights information about in the
368   * entry.
369   */
370    public List<AttributeType> getSpecificAttributes() {
371       return this.specificAttrs;
372    }
373
374    /** {@inheritDoc} */
375    @Override
376    public void addTargAttrFiltersMatchAci(Aci aci) {
377      this.targAttrFilterAcis.put(aci, aci);
378    }
379
380    /** {@inheritDoc} */
381    @Override
382    public boolean hasTargAttrFiltersMatchAci(Aci aci) {
383      return this.targAttrFilterAcis.containsKey(aci);
384    }
385
386    /** {@inheritDoc} */
387    @Override
388    public boolean isTargAttrFilterMatchAciEmpty() {
389       return this.targAttrFilterAcis.isEmpty();
390    }
391
392  /**
393   * Reset the values used by the geteffectiverights evaluation to
394   * original values. The geteffectiverights evaluation uses the same container
395   * repeatedly for different rights evaluations (read, write, proxy,...) and
396   * this method resets variables that are specific to a single evaluation.
397   */
398    public void resetEffectiveRightsParams() {
399      this.targAttrFilterAcis.clear();
400      this.decidingAci=null;
401      this.evalReason=null;
402      this.targAttrFiltersMatch=false;
403      this.summaryString=null;
404      this.targAttrMatch=0;
405    }
406
407    /** {@inheritDoc} */
408    @Override
409    public void setTargAttrFiltersAciName(String name) {
410      this.targAttrFiltersAciName=name;
411    }
412
413    /** {@inheritDoc} */
414    @Override
415    public String getTargAttrFiltersAciName() {
416      return this.targAttrFiltersAciName;
417    }
418
419    /** {@inheritDoc} */
420    @Override
421    public void setTargAttrFiltersMatchOp(int flag) {
422      this.targAttrMatch |= flag;
423    }
424
425    /** {@inheritDoc} */
426    @Override
427    public boolean hasTargAttrFiltersMatchOp(int flag) {
428       return (this.targAttrMatch & flag) != 0;
429    }
430
431    /** {@inheritDoc} */
432    @Override
433    public String getDecidingAciName() {
434      if(this.decidingAci != null) {
435        return this.decidingAci.getName();
436      }
437      return null;
438    }
439
440  /** {@inheritDoc} */
441  @Override
442  public void setEvaluationResult(EnumEvalReason reason, Aci decidingAci)
443  {
444    this.evalReason = reason;
445    this.decidingAci = decidingAci;
446  }
447
448    /** {@inheritDoc} */
449    @Override
450    public EnumEvalReason getEvalReason() {
451      return this.evalReason;
452    }
453
454    /** {@inheritDoc} */
455    @Override
456    public void setEvalSummary(String summary) {
457      this.summaryString=summary;
458    }
459
460    /** {@inheritDoc} */
461    @Override
462    public String getEvalSummary() {
463      return this.summaryString;
464    }
465
466  /**
467   * Returns true if the geteffectiverights control's authZid DN is equal to the
468   * authorization entry's DN.
469   *
470   * @return True if the authZid is equal to the authorization entry's DN.
471   */
472    public boolean isAuthzidAuthorizationDN() {
473     return this.authzid.equals(this.authorizationEntry.getName());
474    }
475
476    /** {@inheritDoc} */
477    @Override
478    public void setDenyList(List<Aci> denys) {
479        denyList=denys;
480    }
481
482    /** {@inheritDoc} */
483    @Override
484    public void setAllowList(List<Aci> allows) {
485        allowList=allows;
486    }
487
488    /** {@inheritDoc} */
489    @Override
490    public AttributeType getCurrentAttributeType() {
491        return attributeType;
492    }
493
494    /** {@inheritDoc} */
495    @Override
496    public ByteString getCurrentAttributeValue() {
497        return attributeValue;
498    }
499
500    /** {@inheritDoc} */
501    @Override
502    public void setCurrentAttributeType(AttributeType type) {
503        attributeType=type;
504    }
505
506    /** {@inheritDoc} */
507    @Override
508    public void setCurrentAttributeValue(ByteString value) {
509        attributeValue=value;
510    }
511
512    /** {@inheritDoc} */
513    @Override
514    public boolean isFirstAttribute() {
515        return isFirst;
516    }
517
518    /** {@inheritDoc} */
519    @Override
520    public void setIsFirstAttribute(boolean val) {
521        isFirst=val;
522    }
523
524    /** {@inheritDoc} */
525    @Override
526    public boolean hasEntryTestRule() {
527        return isEntryTestRule;
528    }
529
530    /** {@inheritDoc} */
531    @Override
532    public void setEntryTestRule(boolean val) {
533        isEntryTestRule=val;
534    }
535
536    /** {@inheritDoc} */
537    @Override
538    public Entry getResourceEntry() {
539        return resourceEntry;
540    }
541
542    /** {@inheritDoc} */
543    @Override
544    public Entry getClientEntry() {
545      return this.authorizationEntry;
546    }
547
548    /** {@inheritDoc} */
549    @Override
550    public List<Aci> getDenyList() {
551        return denyList;
552    }
553
554    /** {@inheritDoc} */
555    @Override
556    public List<Aci> getAllowList() {
557       return allowList;
558    }
559
560    /** {@inheritDoc} */
561    @Override
562    public boolean isDenyEval() {
563        return EnumEvalReason.NO_ALLOW_ACIS.equals(evalReason)
564            || EnumEvalReason.EVALUATED_DENY_ACI.equals(evalReason);
565    }
566
567    /** {@inheritDoc} */
568    @Override
569    public boolean isAnonymousUser() {
570        return !authInfo.isAuthenticated();
571    }
572
573    /** {@inheritDoc} */
574    @Override
575    public DN getClientDN() {
576      if(this.useAuthzid)
577      {
578        return this.authzid;
579      }
580      else if (this.authorizationEntry != null)
581      {
582        return this.authorizationEntry.getName();
583      }
584      return DN.rootDN();
585    }
586
587    /** {@inheritDoc} */
588    @Override
589    public DN getResourceDN() {
590        return resourceEntry.getName();
591    }
592
593   /**
594    * {@inheritDoc}
595    * <p>
596    * JNR: I find the implementation in this method dubious.
597    *
598    * @see EnumRight#hasRights(int, int)
599    */
600    @Override
601    public boolean hasRights(int rights) {
602       return (this.rightsMask & rights) != 0;
603    }
604
605    /** {@inheritDoc} */
606    @Override
607    public int getRights() {
608        return this.rightsMask;
609    }
610
611    /** {@inheritDoc} */
612    @Override
613    public void setRights(int rights) {
614         this.rightsMask=rights;
615    }
616
617    /** {@inheritDoc} */
618    @Override
619    public String getHostName() {
620        return clientConnection.getRemoteAddress().getCanonicalHostName();
621    }
622
623    /** {@inheritDoc} */
624    @Override
625    public InetAddress getRemoteAddress() {
626        return clientConnection.getRemoteAddress();
627    }
628
629    /** {@inheritDoc} */
630    @Override
631    public boolean isAddOperation() {
632        return operation instanceof AddOperation;
633    }
634
635    /** {@inheritDoc} */
636    @Override
637    public void setTargAttrFiltersMatch(boolean v) {
638        this.targAttrFiltersMatch=v;
639    }
640
641    /** {@inheritDoc} */
642    @Override
643    public boolean getTargAttrFiltersMatch() {
644        return targAttrFiltersMatch;
645    }
646
647    /** {@inheritDoc} */
648    @Override
649    public String getControlOID() {
650      return controlOID;
651    }
652
653    /** {@inheritDoc} */
654    @Override
655    public String getExtOpOID() {
656      return extOpOID;
657    }
658
659    /**
660     * Set the the controlOID value to the specified oid string.
661     *
662     * @param oid  The control oid string.
663     */
664    protected void setControlOID(String oid) {
665      this.controlOID=oid;
666    }
667
668
669    /**
670     * Set the extended operation OID value to the specified oid string.
671     *
672     * @param oid  The extended operation oid string.
673     */
674    protected void setExtOpOID(String oid) {
675      this.extOpOID=oid;
676    }
677
678    /** {@inheritDoc} */
679    @Override
680    public EnumEvalResult hasAuthenticationMethod(EnumAuthMethod authMethod,
681                                                  String saslMech) {
682      EnumEvalResult matched=EnumEvalResult.FALSE;
683
684      if(authMethod==EnumAuthMethod.AUTHMETHOD_NONE) {
685        /*
686         * None actually means any, in that we don't care what method was used.
687         * This doesn't seem very intuitive or useful, but that's the way it is.
688         */
689        matched = EnumEvalResult.TRUE;
690      } else {
691        // Some kind of authentication is required.
692        if(authInfo.isAuthenticated()) {
693          if(authMethod==EnumAuthMethod.AUTHMETHOD_SIMPLE) {
694            if(authInfo.hasAuthenticationType(AuthenticationType.SIMPLE)) {
695              matched = EnumEvalResult.TRUE;
696            }
697          } else if(authMethod == EnumAuthMethod.AUTHMETHOD_SSL) {
698            /*
699             * This means authentication using a certificate over TLS.
700             *
701             * We check the following:
702             * - SASL EXTERNAL has been used, and
703             * - TLS is the security provider, and
704             * - The client provided a certificate.
705             */
706            if (authInfo.hasAuthenticationType(AuthenticationType.SASL)
707                && authInfo.hasSASLMechanism(saslMech)
708                && clientConnection instanceof LDAPClientConnection) {
709                LDAPClientConnection lc = (LDAPClientConnection) clientConnection;
710                Certificate[] certChain = lc.getClientCertificateChain();
711                if (certChain.length != 0) {
712                  matched = EnumEvalResult.TRUE;
713                }
714            }
715          } else {
716            // A particular SASL mechanism.
717            if (authInfo.hasAuthenticationType(AuthenticationType.SASL) &&
718                 authInfo.hasSASLMechanism(saslMech)) {
719              matched = EnumEvalResult.TRUE;
720            }
721          }
722        }
723      }
724      return matched;
725    }
726
727    /** {@inheritDoc} */
728    @Override
729    public boolean isMemberOf(Group<?> group) {
730        try {
731            if(useAuthzid) {
732                return group.isMember(this.authzid);
733            }
734            Entry e = getClientEntry();
735            if (e != null) {
736                return group.isMember(e);
737            }
738            return group.isMember(getClientDN());
739        } catch (DirectoryException ex) {
740            return false;
741        }
742    }
743
744  /**
745   * {@inheritDoc}
746   * <p>
747   * JNR: I find the implementation in this method dubious.
748   *
749   * @see EnumRight#getEnumRight(int)
750   */
751    @Override
752    public String rightToString() {
753      if(hasRights(ACI_SEARCH))
754      {
755        return "search";
756      }
757      else if(hasRights(ACI_COMPARE))
758      {
759        return "compare";
760      }
761      else if(hasRights(ACI_READ))
762      {
763        return "read";
764      }
765      else if(hasRights(ACI_DELETE))
766      {
767        return "delete";
768      }
769      else if(hasRights(ACI_ADD))
770      {
771        return "add";
772      }
773      else if(hasRights(ACI_WRITE))
774      {
775        return "write";
776      }
777      else if(hasRights(ACI_PROXY))
778      {
779        return "proxy";
780      }
781      else if(hasRights(ACI_IMPORT))
782      {
783        return "import";
784      }
785      else if(hasRights(ACI_EXPORT))
786      {
787        return "export";
788      }
789      else if(hasRights(ACI_WRITE) &&
790              hasRights(ACI_SELF))
791      {
792        return "selfwrite";
793      }
794      return null;
795  }
796
797  /** {@inheritDoc} */
798  @Override
799  public  void setEvalUserAttributes(int v) {
800    if(rightsMask == ACI_READ) {
801      if(v == ACI_FOUND_USER_ATTR_RULE) {
802        evalAllAttributes |= ACI_FOUND_USER_ATTR_RULE;
803        evalAllAttributes &= ~ACI_USER_ATTR_STAR_MATCHED;
804      }
805      else
806      {
807        evalAllAttributes |= ACI_USER_ATTR_STAR_MATCHED;
808      }
809    }
810  }
811
812  /** {@inheritDoc} */
813  @Override
814  public  void setEvalOpAttributes(int v) {
815    if(rightsMask == ACI_READ) {
816      if(v == ACI_FOUND_OP_ATTR_RULE) {
817        evalAllAttributes |= ACI_FOUND_OP_ATTR_RULE;
818        evalAllAttributes &= ~ACI_OP_ATTR_PLUS_MATCHED;
819      }
820      else
821      {
822        evalAllAttributes |= ACI_OP_ATTR_PLUS_MATCHED;
823      }
824    }
825  }
826
827  /** {@inheritDoc} */
828  @Override
829  public boolean hasEvalUserAttributes() {
830    return hasAttribute(ACI_FOUND_USER_ATTR_RULE);
831  }
832
833  /** {@inheritDoc} */
834  @Override
835  public boolean hasEvalOpAttributes() {
836    return hasAttribute(ACI_FOUND_OP_ATTR_RULE);
837  }
838
839  /**
840   * Return true if the evaluating ACI contained a targetattr all
841   * user attributes rule match.
842   *
843   * @return  True if the above condition was seen.
844   */
845  public boolean hasAllUserAttributes() {
846    return hasAttribute(ACI_USER_ATTR_STAR_MATCHED);
847  }
848
849  /**
850   * Return true if the evaluating ACI contained a targetattr all
851   * operational attributes rule match.
852   *
853   * @return  True if the above condition was seen.
854   */
855  public boolean hasAllOpAttributes() {
856    return hasAttribute(ACI_OP_ATTR_PLUS_MATCHED);
857  }
858
859  private boolean hasAttribute(int aciAttribute)
860  {
861    return (evalAllAttributes & aciAttribute) == aciAttribute;
862  }
863
864  /** {@inheritDoc} */
865  @Override
866  public void clearEvalAttributes(int v) {
867    if(v == 0)
868    {
869      evalAllAttributes=0;
870    }
871    else
872    {
873      evalAllAttributes &= ~v;
874    }
875  }
876
877  /** {@inheritDoc} */
878  @Override
879  public int getCurrentSSF() {
880      return clientConnection.getSSF();
881  }
882
883  /** {@inheritDoc} */
884  @Override
885  public String toString()
886  {
887    final StringBuilder sb = new StringBuilder();
888    if (attributeType != null)
889    {
890      appendSeparatorIfNeeded(sb);
891      sb.append("attributeType: ").append(attributeType.getNameOrOID());
892      if (attributeValue != null)
893      {
894        sb.append(":").append(attributeValue);
895      }
896    }
897    appendSeparatorIfNeeded(sb);
898    sb.append(size(allowList)).append(" allow ACIs");
899    appendSeparatorIfNeeded(sb);
900    sb.append(size(denyList)).append(" deny ACIs");
901    if (evalReason != null)
902    {
903      appendSeparatorIfNeeded(sb);
904      sb.append("evaluationResult: ").append(evalReason);
905      if (decidingAci != null)
906      {
907        sb.append(",").append(decidingAci);
908      }
909    }
910    return sb.toString();
911  }
912
913  private void appendSeparatorIfNeeded(StringBuilder sb)
914  {
915    if (sb.length() > 0)
916    {
917      sb.append(", ");
918    }
919  }
920
921  private int size(Collection<?> col)
922  {
923    if (col != null)
924    {
925      return col.size();
926    }
927    return 0;
928  }
929}