001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.oscal.lib.profile.resolver.selection; 007 008import com.fasterxml.jackson.core.Version; 009import com.fasterxml.jackson.core.util.VersionUtil; 010 011import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; 012import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; 013import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 014import gov.nist.secauto.metaschema.core.metapath.item.node.IRootAssemblyNodeItem; 015import gov.nist.secauto.metaschema.core.util.CollectionUtil; 016import gov.nist.secauto.metaschema.core.util.ObjectUtils; 017import gov.nist.secauto.oscal.lib.model.BackMatter; 018import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; 019import gov.nist.secauto.oscal.lib.model.Catalog; 020import gov.nist.secauto.oscal.lib.model.CatalogGroup; 021import gov.nist.secauto.oscal.lib.model.Control; 022import gov.nist.secauto.oscal.lib.model.Metadata; 023import gov.nist.secauto.oscal.lib.model.Metadata.Location; 024import gov.nist.secauto.oscal.lib.model.Metadata.Party; 025import gov.nist.secauto.oscal.lib.model.Metadata.Role; 026import gov.nist.secauto.oscal.lib.model.Parameter; 027import gov.nist.secauto.oscal.lib.model.ProfileImport; 028import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; 029import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException; 030import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver.UriResolver; 031import gov.nist.secauto.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor; 032import gov.nist.secauto.oscal.lib.profile.resolver.support.BasicIndexer; 033import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 034import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer; 035import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus; 036 037import java.net.URI; 038import java.util.LinkedList; 039import java.util.List; 040import java.util.stream.Collectors; 041 042import edu.umd.cs.findbugs.annotations.NonNull; 043 044public class Import { 045 046 @NonNull 047 private final IRootAssemblyNodeItem profile; 048 @NonNull 049 private final IAssemblyNodeItem profileImportItem; 050 051 public Import( 052 @NonNull IRootAssemblyNodeItem profile, 053 @NonNull IAssemblyNodeItem profileImportItem) { 054 055 this.profile = profile; 056 this.profileImportItem = profileImportItem; 057 } 058 059 protected IRootAssemblyNodeItem getProfileItem() { 060 return profile; 061 } 062 063 protected IAssemblyNodeItem getProfileImportItem() { 064 return profileImportItem; 065 } 066 067 @NonNull 068 protected ProfileImport getProfileImport() { 069 return ObjectUtils.requireNonNull((ProfileImport) profileImportItem.getValue()); 070 } 071 072 private static Catalog toCatalog(@NonNull IDocumentNodeItem catalogDocument) { 073 return (Catalog) INodeItem.toValue(catalogDocument); 074 } 075 076 @NonNull 077 protected IControlFilter newControlFilter() { 078 return IControlFilter.newInstance(getProfileImport()); 079 } 080 081 @NonNull 082 protected IIndexer newIndexer() { 083 // TODO: add support for reassignment 084 // IIdentifierMapper mapper = IIdentifierMapper.IDENTITY; 085 // IIndexer indexer = new ReassignmentIndexer(mapper); 086 return new BasicIndexer(); 087 } 088 089 @NonNull 090 public IIndexer resolve( 091 @NonNull IDocumentNodeItem importedCatalogDocument, 092 @NonNull Catalog resolvedCatalog, 093 @NonNull UriResolver uriResolver) 094 throws ProfileResolutionException { 095 ProfileImport profileImport = getProfileImport(); 096 URI uri = ObjectUtils.requireNonNull(profileImport.getHref(), "profile import href is null"); 097 098 // determine which controls and groups to keep 099 IControlFilter filter = newControlFilter(); 100 IIndexer indexer = newIndexer(); 101 // the catalog is always selected. This ensures that controls defined at the 102 // catalog level are kept and not duplicated 103 indexer.setSelectionStatus(importedCatalogDocument.getRootAssemblyNodeItem(), SelectionStatus.SELECTED); 104 IControlSelectionState state = new ControlSelectionState(indexer, filter); 105 106 try { 107 ControlSelectionVisitor.instance().visitCatalog(importedCatalogDocument, state); 108 109 // process references 110 ReferenceCountingVisitor.instance().visitCatalog(importedCatalogDocument, indexer, uriResolver); 111 112 // filter based on selections 113 FilterNonSelectedVisitor.instance().visitCatalog(importedCatalogDocument, indexer); 114 } catch (ProfileResolutionEvaluationException ex) { 115 throw new ProfileResolutionException( 116 String.format("Import: Unable to resolve profile import '%s'. %s", uri.toString(), ex.getMessage()), ex); 117 } 118 119 Catalog importedCatalog = toCatalog(importedCatalogDocument); 120 for (Parameter param : CollectionUtil.listOrEmpty(importedCatalog.getParams())) { 121 if (param != null) { 122 resolvedCatalog.addParam(param); 123 } 124 } 125 for (Control control : CollectionUtil.listOrEmpty(importedCatalog.getControls())) { 126 if (control != null) { 127 resolvedCatalog.addControl(control); 128 } 129 } 130 for (CatalogGroup group : CollectionUtil.listOrEmpty(importedCatalog.getGroups())) { 131 if (group != null) { 132 resolvedCatalog.addGroup(group); 133 } 134 } 135 136 generateMetadata(importedCatalogDocument, resolvedCatalog, indexer); 137 generateBackMatter(importedCatalogDocument, resolvedCatalog, indexer); 138 return indexer; 139 } 140 141 private static void generateMetadata( 142 @NonNull IDocumentNodeItem importedCatalogDocument, 143 @NonNull Catalog resolvedCatalog, 144 @NonNull IIndexer indexer) { 145 Metadata importedMetadata = toCatalog(importedCatalogDocument).getMetadata(); 146 147 if (importedMetadata != null) { 148 Metadata resolvedMetadata = resolvedCatalog.getMetadata(); 149 if (resolvedMetadata == null) { 150 resolvedMetadata = new Metadata(); 151 resolvedCatalog.setMetadata(resolvedMetadata); 152 } 153 resolveMetadata(importedMetadata, resolvedMetadata, indexer); 154 } 155 } 156 157 private static void resolveMetadata( 158 @NonNull Metadata imported, 159 @NonNull Metadata resolved, 160 @NonNull IIndexer indexer) { 161 String importedVersion = imported.getOscalVersion(); 162 if (importedVersion != null) { 163 Version importOscalVersion = VersionUtil.parseVersion(importedVersion, null, null); 164 165 Version resolvedCatalogVersion 166 = VersionUtil.parseVersion(resolved.getOscalVersion(), null, null); 167 168 if (importOscalVersion.compareTo(resolvedCatalogVersion) > 0) { 169 resolved.setOscalVersion(importOscalVersion.toString()); 170 } 171 } 172 173 // copy roles, parties, and locations with prop name:keep and any referenced 174 resolved.setRoles( 175 IIndexer.filterDistinct( 176 ObjectUtils.notNull(CollectionUtil.listOrEmpty(resolved.getRoles()).stream()), 177 indexer.getEntitiesByItemType(IEntityItem.ItemType.ROLE), 178 Role::getId) 179 .collect(Collectors.toCollection(LinkedList::new))); 180 resolved.setParties( 181 IIndexer.filterDistinct( 182 ObjectUtils.notNull(CollectionUtil.listOrEmpty(resolved.getParties()).stream()), 183 indexer.getEntitiesByItemType(IEntityItem.ItemType.PARTY), 184 Party::getUuid) 185 .collect(Collectors.toCollection(LinkedList::new))); 186 resolved.setLocations( 187 IIndexer.filterDistinct( 188 ObjectUtils.notNull(CollectionUtil.listOrEmpty(resolved.getLocations()).stream()), 189 indexer.getEntitiesByItemType(IEntityItem.ItemType.LOCATION), 190 Location::getUuid) 191 .collect(Collectors.toCollection(LinkedList::new))); 192 } 193 194 @SuppressWarnings("PMD.AvoidDeeplyNestedIfStmts") // not worth a function call 195 private static void generateBackMatter( 196 @NonNull IDocumentNodeItem importedCatalogDocument, 197 @NonNull Catalog resolvedCatalog, 198 @NonNull IIndexer indexer) { 199 BackMatter importedBackMatter = toCatalog(importedCatalogDocument).getBackMatter(); 200 201 if (importedBackMatter != null) { 202 BackMatter resolvedBackMatter = resolvedCatalog.getBackMatter(); 203 204 List<Resource> resolvedResources = resolvedBackMatter == null ? CollectionUtil.emptyList() 205 : CollectionUtil.listOrEmpty(resolvedBackMatter.getResources()); 206 207 List<Resource> resources = IIndexer.filterDistinct( 208 ObjectUtils.notNull(resolvedResources.stream()), 209 indexer.getEntitiesByItemType(IEntityItem.ItemType.RESOURCE), 210 Resource::getUuid) 211 .collect(Collectors.toCollection(LinkedList::new)); 212 213 if (!resources.isEmpty()) { 214 if (resolvedBackMatter == null) { 215 resolvedBackMatter = new BackMatter(); 216 resolvedCatalog.setBackMatter(resolvedBackMatter); 217 } 218 219 resolvedBackMatter.setResources(resources); 220 } 221 } 222 } 223}