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