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.merge;
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.BackMatter.Resource;
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.model.Metadata.Location;
38  import gov.nist.secauto.oscal.lib.model.Metadata.Party;
39  import gov.nist.secauto.oscal.lib.model.Metadata.Role;
40  import gov.nist.secauto.oscal.lib.model.Parameter;
41  import gov.nist.secauto.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor;
42  import gov.nist.secauto.oscal.lib.profile.resolver.selection.DefaultResult;
43  import gov.nist.secauto.oscal.lib.profile.resolver.selection.FilterNonSelectedVisitor;
44  import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractCatalogEntityVisitor;
45  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
46  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem.ItemType;
47  import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer;
48  import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus;
49  
50  import java.util.EnumSet;
51  import java.util.UUID;
52  
53  import edu.umd.cs.findbugs.annotations.NonNull;
54  
55  public class FlatteningStructuringVisitor
56      extends AbstractCatalogEntityVisitor<IIndexer, Void> {
57    private static final FlatteningStructuringVisitor SINGLETON = new FlatteningStructuringVisitor();
58  
59    public static FlatteningStructuringVisitor instance() {
60      return SINGLETON;
61    }
62  
63    public FlatteningStructuringVisitor() {
64      super(ObjectUtils.notNull(EnumSet.of(ItemType.GROUP, ItemType.CONTROL)));
65    }
66  
67    @Override
68    protected Void newDefaultResult(IIndexer state) {
69      // do nothing
70      return null;
71    }
72  
73    @Override
74    protected Void aggregateResults(Void first, Void second, IIndexer state) {
75      // do nothing
76      return null;
77    }
78  
79    @Override
80    public Void visitCatalog(@NonNull IDocumentNodeItem catalogItem, IIndexer index) {
81      index.resetSelectionStatus();
82  
83      index.setSelectionStatus(catalogItem, SelectionStatus.SELECTED);
84      super.visitCatalog(catalogItem, index);
85  
86      for (ItemType itemType : ItemType.values()) {
87        assert itemType != null;
88        for (IEntityItem item : index.getEntitiesByItemType(itemType)) {
89          item.resetReferenceCount();
90        }
91      }
92  
93      // process references, looking for orphaned links to groups
94      ReferenceCountingVisitor.instance().visitCatalog(catalogItem, index, catalogItem.getDocumentUri());
95  
96      FlatteningFilterNonSelectedVisitor.instance().visitCatalog(catalogItem, index);
97      return null;
98    }
99  
100   @Override
101   public Void visitGroup(IRequiredValueModelNodeItem item, Void childResult, IIndexer index) {
102     CatalogGroup group = (CatalogGroup) item.getValue();
103     String id = group.getId();
104     if (id != null) {
105       IEntityItem entity = index.getEntity(ItemType.GROUP, id);
106       assert entity != null;
107       // refresh the instance
108       entity.setInstance(item);
109     }
110 
111     index.setSelectionStatus(item, SelectionStatus.UNSELECTED);
112     handlePartSelection(item, index, SelectionStatus.UNSELECTED);
113     return super.visitGroup(item, childResult, index);
114   }
115 
116   @Override
117   public Void visitControl(IRequiredValueModelNodeItem item, Void childResult, IIndexer index) {
118     Control control = (Control) item.getValue();
119     String id = ObjectUtils.requireNonNull(control.getId());
120     IEntityItem entity = index.getEntity(ItemType.CONTROL, id);
121     assert entity != null;
122     // refresh the instance
123     entity.setInstance(item);
124 
125     index.setSelectionStatus(item, SelectionStatus.SELECTED);
126     handlePartSelection(item, index, SelectionStatus.SELECTED);
127     return null;
128   }
129 
130   @Override
131   protected Void visitParameter(IRequiredValueModelNodeItem item,
132       IRequiredValueModelNodeItem catalogOrGroupOrControl, IIndexer index) {
133     Parameter parameter = (Parameter) item.getValue();
134     String id = ObjectUtils.requireNonNull(parameter.getId());
135     IEntityItem entity = index.getEntity(ItemType.PARAMETER, id);
136     assert entity != null;
137     // refresh the instance
138     entity.setInstance(item);
139 
140     return null;
141   }
142 
143   @Override
144   protected void visitRole(IRequiredValueModelNodeItem item, IRequiredValueModelNodeItem metadataItem,
145       IIndexer index) {
146     Role role = (Role) item.getValue();
147     String id = ObjectUtils.requireNonNull(role.getId());
148     IEntityItem entity = index.getEntity(ItemType.ROLE, id);
149     assert entity != null;
150     // refresh the instance
151     entity.setInstance(item);
152   }
153 
154   @Override
155   protected void visitLocation(IRequiredValueModelNodeItem item, IRequiredValueModelNodeItem metadataItem,
156       IIndexer index) {
157     Location location = (Location) item.getValue();
158     UUID uuid = ObjectUtils.requireNonNull(location.getUuid());
159     IEntityItem entity = index.getEntity(ItemType.LOCATION, uuid);
160     assert entity != null;
161     // refresh the instance
162     entity.setInstance(item);
163   }
164 
165   @Override
166   protected void visitParty(IRequiredValueModelNodeItem item, IRequiredValueModelNodeItem metadataItem,
167       IIndexer index) {
168     Party location = (Party) item.getValue();
169     UUID uuid = ObjectUtils.requireNonNull(location.getUuid());
170     IEntityItem entity = index.getEntity(ItemType.PARTY, uuid);
171     assert entity != null;
172     // refresh the instance
173     entity.setInstance(item);
174   }
175 
176   @Override
177   protected void visitResource(IRequiredValueModelNodeItem item, IRootAssemblyNodeItem rootItem,
178       IIndexer index) {
179     Resource location = (Resource) item.getValue();
180     UUID uuid = ObjectUtils.requireNonNull(location.getUuid());
181     IEntityItem entity = index.getEntity(ItemType.RESOURCE, uuid);
182     assert entity != null;
183     // refresh the instance
184     entity.setInstance(item);
185   }
186 
187   private static void handlePartSelection(
188       @NonNull IRequiredValueModelNodeItem groupOrControlItem,
189       @NonNull IIndexer index,
190       @NonNull SelectionStatus selectionStatus) {
191     CHILD_PART_METAPATH.evaluate(groupOrControlItem).asStream()
192         .map(item -> (IRequiredValueModelNodeItem) item)
193         .forEachOrdered(partItem -> {
194           index.setSelectionStatus(ObjectUtils.requireNonNull(partItem), selectionStatus);
195 
196           ControlPart part = (ControlPart) partItem.getValue();
197           String id = part.getId();
198           if (id != null) {
199             IEntityItem entity = index.getEntity(ItemType.PART, id);
200             assert entity != null;
201             // refresh the instance
202             entity.setInstance(partItem);
203           }
204         });
205   }
206 
207   private static final class FlatteningFilterNonSelectedVisitor
208       extends FilterNonSelectedVisitor {
209     private static final FlatteningFilterNonSelectedVisitor SINGLETON = new FlatteningFilterNonSelectedVisitor();
210 
211     public static FlatteningFilterNonSelectedVisitor instance() {
212       return SINGLETON;
213     }
214 
215     @Override
216     public DefaultResult visitControl(IRequiredValueModelNodeItem item, DefaultResult childResult,
217         Context context) {
218       assert childResult != null;
219 
220       Control control = (Control) item.getValue();
221       IIndexer index = context.getIndexer();
222       // this control should always be found in the index
223       IEntityItem entity = ObjectUtils.requireNonNull(
224           index.getEntity(ItemType.CONTROL, ObjectUtils.requireNonNull(control.getId()), false));
225 
226       IRequiredValueModelNodeItem parent = ObjectUtils.notNull(item.getParentContentNodeItem());
227       DefaultResult retval = new DefaultResult();
228       if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
229         // keep this control
230 
231         // always promote the control and any children
232         retval.promoteControl(control);
233 
234         retval.appendPromoted(childResult);
235         childResult.applyRemovesTo(control);
236 
237         if (parent.getValue() instanceof Control && SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
238           retval.removeControl(control);
239         }
240       } else {
241         // remove this control and promote any needed children
242 
243         if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
244           retval.removeControl(control);
245         }
246         retval.appendPromoted(ObjectUtils.notNull(childResult));
247         index.removeItem(entity);
248 
249         // remove any associated parts from the index
250         removePartsFromIndex(item, index);
251       }
252       return retval;
253     }
254   }
255 }