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