View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.oscal.lib.profile.resolver.selection;
28  
29  import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem;
30  import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem;
31  import gov.nist.secauto.metaschema.model.common.metapath.item.IRootAssemblyNodeItem;
32  import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
33  import gov.nist.secauto.oscal.lib.model.Catalog;
34  import gov.nist.secauto.oscal.lib.model.CatalogGroup;
35  import gov.nist.secauto.oscal.lib.model.Control;
36  import gov.nist.secauto.oscal.lib.model.ControlPart;
37  import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractIndexingVisitor;
38  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
39  import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer;
40  import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus;
41  
42  import org.apache.logging.log4j.LogManager;
43  import org.apache.logging.log4j.Logger;
44  
45  import edu.umd.cs.findbugs.annotations.NonNull;
46  
47  /**
48   * Walks a {@link Catalog} indexing all nodes that can be referenced.
49   * <p>
50   * For each {@link CatalogGroup}, {@link Control}, and {@link ControlPart},
51   * determine if that object is {@link SelectionStatus#SELECTED} or
52   * {@link SelectionStatus#UNSELECTED}.
53   * <p>
54   * A {@link Control} is {@link SelectionStatus#SELECTED} if it matches the
55   * configured {@link IControlFilter}, otherwise it is
56   * {@link SelectionStatus#UNSELECTED}.
57   * <p>
58   * A {@link CatalogGroup} is {@link SelectionStatus#SELECTED} if it contains a
59   * {@link SelectionStatus#SELECTED} descendant {@link Control}, otherwise it is
60   * {@link SelectionStatus#UNSELECTED}.
61   * <p>
62   * A {@link ControlPart} is {@link SelectionStatus#SELECTED} if its containing
63   * control is {@link SelectionStatus#SELECTED}.
64   * <p>
65   * All other indexed nodes will have the {@link SelectionStatus#UNKNOWN}, since
66   * these nodes require reference counting to determine if they are to be kept or
67   * not.
68   */
69  public class ControlSelectionVisitor
70      extends AbstractIndexingVisitor<IControlSelectionState, Boolean> {
71    private static final Logger LOGGER = LogManager.getLogger(ControlSelectionVisitor.class);
72  
73    private static final ControlSelectionVisitor SINGLETON = new ControlSelectionVisitor();
74  
75    public static ControlSelectionVisitor instance() {
76      return SINGLETON;
77    }
78  
79    @Override
80    protected IIndexer getIndexer(IControlSelectionState state) {
81      return state.getIndex();
82    }
83  
84    @Override
85    protected Boolean newDefaultResult(IControlSelectionState state) {
86      return false;
87    }
88  
89    @Override
90    protected Boolean aggregateResults(Boolean first, Boolean second, IControlSelectionState state) {
91      return first || second;
92    }
93  
94    public void visit(@NonNull IDocumentNodeItem catalogDocument, @NonNull IControlSelectionState state) {
95      visitCatalog(catalogDocument, state);
96    }
97  
98    public void visitProfile(
99        @NonNull IDocumentNodeItem catalogDocument,
100       @NonNull IDocumentNodeItem profileDocument,
101       @NonNull IControlSelectionState state) {
102     visit(catalogDocument, state);
103 
104     IRootAssemblyNodeItem root = profileDocument.getRootAssemblyNodeItem();
105 
106     visitMetadata(root, state);
107     visitBackMatter(root, state);
108   }
109 
110   @Override
111   public Boolean visitCatalog(IDocumentNodeItem catalogDocument, IControlSelectionState state) {
112     getIndexer(state).setSelectionStatus(catalogDocument, SelectionStatus.SELECTED);
113     return super.visitCatalog(catalogDocument, state);
114   }
115 
116   @Override
117   public Boolean visitGroup(IRequiredValueModelNodeItem groupItem, Boolean childSelected,
118       IControlSelectionState state) {
119     super.visitGroup(groupItem, childSelected, state);
120     if (LOGGER.isTraceEnabled()) {
121       CatalogGroup group = (CatalogGroup) groupItem.getValue();
122       LOGGER.atTrace().log("Selecting group '{}'. match={}", group.getId(), childSelected);
123     }
124 
125     // these should agree
126     assert state.isSelected(groupItem) == childSelected;
127 
128     if (childSelected) {
129       getIndexer(state).setSelectionStatus(groupItem, SelectionStatus.SELECTED);
130     } else {
131       getIndexer(state).setSelectionStatus(groupItem, SelectionStatus.UNSELECTED);
132     }
133 
134     handlePartSelection(groupItem, childSelected, state);
135     return childSelected;
136   }
137 
138   private void handlePartSelection(
139       @NonNull IRequiredValueModelNodeItem groupOrControlItem,
140       boolean selected,
141       IControlSelectionState state) {
142     if (isVisitedItemType(IEntityItem.ItemType.PART)) {
143       SelectionStatus selectionStatus = selected ? SelectionStatus.SELECTED : SelectionStatus.UNSELECTED;
144 
145       IIndexer index = getIndexer(state);
146       CHILD_PART_METAPATH.evaluate(groupOrControlItem).asStream()
147           .map(item -> (IRequiredValueModelNodeItem) item)
148           .forEachOrdered(partItem -> {
149             index.setSelectionStatus(ObjectUtils.requireNonNull(partItem), selectionStatus);
150           });
151     }
152   }
153 
154   @Override
155   public Boolean visitControl(
156       IRequiredValueModelNodeItem controlItem,
157       Boolean childResult,
158       IControlSelectionState state) {
159     super.visitControl(controlItem, childResult, state);
160 
161     boolean selected = state.isSelected(controlItem);
162     if (selected) {
163       getIndexer(state).setSelectionStatus(controlItem, SelectionStatus.SELECTED);
164     } else {
165       getIndexer(state).setSelectionStatus(controlItem, SelectionStatus.UNSELECTED);
166     }
167 
168     handlePartSelection(controlItem, selected, state);
169     return selected;
170   }
171 }