1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.oscal.lib.profile.resolver.selection;
7   
8   import gov.nist.secauto.metaschema.core.util.ObjectUtils;
9   import gov.nist.secauto.oscal.lib.model.IncludeAll;
10  import gov.nist.secauto.oscal.lib.model.ProfileImport;
11  import gov.nist.secauto.oscal.lib.model.control.catalog.IControl;
12  import gov.nist.secauto.oscal.lib.model.control.profile.IProfileSelectControlById;
13  
14  import org.apache.commons.lang3.tuple.Pair;
15  
16  import java.util.List;
17  
18  import edu.umd.cs.findbugs.annotations.NonNull;
19  
20  public interface IControlFilter {
21    @NonNull
22    IControlFilter ALWAYS_MATCH = new IControlFilter() {
23      @Override
24      public @NonNull
25      Pair<Boolean, Boolean> match(@NonNull IControl control, boolean defaultMatch) {
26        return IControlSelectionFilter.MATCH;
27      }
28  
29      @Override
30      public @NonNull
31      IControlSelectionFilter getInclusionFilter() {
32        return IControlSelectionFilter.ALL_MATCH;
33      }
34  
35      @Override
36      public @NonNull
37      IControlSelectionFilter getExclusionFilter() {
38        return IControlSelectionFilter.NONE_MATCH;
39      }
40    };
41  
42    @NonNull
43    IControlFilter NONE_MATCH = new IControlFilter() {
44  
45      @Override
46      public @NonNull
47      Pair<Boolean, Boolean> match(@NonNull IControl control, boolean defaultMatch) {
48        return IControlSelectionFilter.NON_MATCH;
49      }
50  
51      @Override
52      public @NonNull
53      IControlSelectionFilter getInclusionFilter() {
54        return IControlSelectionFilter.NONE_MATCH;
55      }
56  
57      @Override
58      public @NonNull
59      IControlSelectionFilter getExclusionFilter() {
60        return IControlSelectionFilter.NONE_MATCH;
61      }
62    };
63  
64    /**
65     * Construct a new filter instance based on the provided profile import
66     * statement.
67     *
68     * @param profileImport
69     *          an OSCAL profile import statement
70     * @return a new control filter
71     */
72    @NonNull
73    static IControlFilter newInstance(@NonNull ProfileImport profileImport) {
74      return new Filter(profileImport);
75    }
76  
77    @NonNull
78    static IControlFilter newInstance(@NonNull IControlSelectionFilter includes,
79        @NonNull IControlSelectionFilter excludes) {
80      return new Filter(includes, excludes);
81    }
82  
83    /**
84     * Determines if the control is matched by this filter. This method returns a
85     * {@link Pair} where the first member of the pair indicates if the control
86     * matches, and the second indicates if the match applies to child controls as
87     * well.
88     *
89     * @param control
90     *          the control to check for a match
91     * @return a pair indicating the status of the match ({@code true} for a match
92     *         or {@code false} otherwise), and if a match applies to child controls
93     */
94    @NonNull
95    default Pair<Boolean, Boolean> match(@NonNull IControl control) {
96      return match(control, false);
97    }
98  
99    /**
100    * Determines if the control is matched by this filter. This method returns a
101    * {@link Pair} where the first member of the pair indicates if the control
102    * matches, and the second indicates if the match applies to child controls as
103    * well.
104    *
105    * @param control
106    *          the control to check for a match
107    * @param defaultMatch
108    *          the match status to use if the filter doesn't have an explicit hit
109    * @return a pair indicating the status of the match ({@code true} for a match
110    *         or {@code false} otherwise), and if a match applies to child controls
111    */
112   @NonNull
113   Pair<Boolean, Boolean> match(@NonNull IControl control, boolean defaultMatch);
114 
115   @NonNull
116   IControlSelectionFilter getInclusionFilter();
117 
118   @NonNull
119   IControlSelectionFilter getExclusionFilter();
120 
121   class Filter implements IControlFilter {
122     @NonNull
123     private final IControlSelectionFilter inclusionFilter;
124     @NonNull
125     private final IControlSelectionFilter exclusionFilter;
126 
127     public Filter(@NonNull ProfileImport profileImport) {
128       IncludeAll includeAll = profileImport.getIncludeAll();
129 
130       if (includeAll == null) {
131         List<? extends IProfileSelectControlById> selections = profileImport.getIncludeControls();
132         if (selections == null) {
133           this.inclusionFilter = IControlSelectionFilter.NONE_MATCH;
134         } else {
135           this.inclusionFilter = new DefaultControlSelectionFilter(selections);
136         }
137       } else {
138         this.inclusionFilter = IControlSelectionFilter.ALL_MATCH;
139       }
140 
141       List<? extends IProfileSelectControlById> selections = profileImport.getExcludeControls();
142       if (selections == null) {
143         this.exclusionFilter = IControlSelectionFilter.NONE_MATCH;
144       } else {
145         this.exclusionFilter = new DefaultControlSelectionFilter(selections);
146       }
147 
148     }
149 
150     public Filter(@NonNull IControlSelectionFilter includes, @NonNull IControlSelectionFilter excludes) {
151       this.inclusionFilter = includes;
152       this.exclusionFilter = excludes;
153     }
154 
155     @Override
156     @NonNull
157     public IControlSelectionFilter getInclusionFilter() {
158       return inclusionFilter;
159     }
160 
161     @Override
162     @NonNull
163     public IControlSelectionFilter getExclusionFilter() {
164       return exclusionFilter;
165     }
166 
167     @Override
168     public Pair<Boolean, Boolean> match(@NonNull IControl control, boolean defaultMatch) {
169       @NonNull
170       Pair<Boolean, Boolean> result = getInclusionFilter().apply(control);
171       boolean left = ObjectUtils.notNull(result.getLeft());
172       if (left) {
173         // this is a positive include match. Is it excluded?
174         Pair<Boolean, Boolean> excluded = getExclusionFilter().apply(control);
175         if (ObjectUtils.notNull(excluded.getLeft())) {
176           // the effective result is a non-match
177           result = IControlSelectionFilter.NON_MATCH;
178         }
179       } else {
180         result = defaultMatch ? IControlSelectionFilter.MATCH : IControlSelectionFilter.NON_MATCH;
181       }
182       return result;
183     }
184 
185   }
186 
187 }