From 01cff320246f6b12450f1d7a6499da1418e57b53 Mon Sep 17 00:00:00 2001 From: Ray Gauss II Date: Thu, 12 May 2022 13:50:36 -0400 Subject: [PATCH 1/7] Ability to add and update SSP Impl Req - Created an `OscalSspImplReqMarshallerImpl` - Updated `IterableAssemblyClassBinding` to define a list of `secondaryRootObjects` that will 'trick' the deserializer to allow as root objects - Updated `BaseOscalObjectMarshallerLiboscalImpl` to be able to create deserializer with `IterableAssemblyClassBinding` - Updated `SspController` and tests --- .../IterableAssemblyClassBinding.java | 30 +++ ...BaseOscalObjectMarshallerLiboscalImpl.java | 4 +- .../impl/OscalSspImplReqMarshallerImpl.java | 15 ++ .../oscalrestservice/ServiceConfig.java | 7 + .../api/BaseOscalController.java | 16 +- .../api/OscalControllerExceptionAdvice.java | 2 +- .../oscalrestservice/api/SspController.java | 136 ++++++++++- .../api/BaseOscalControllerTests.java | 22 +- .../api/SspControllerTests.java | 224 ++++++++++++++++++ 9 files changed, 433 insertions(+), 23 deletions(-) create mode 100644 oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/OscalSspImplReqMarshallerImpl.java diff --git a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java index 537c791..290be2c 100644 --- a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java +++ b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java @@ -4,13 +4,29 @@ import gov.nist.secauto.metaschema.binding.IBindingContext; import gov.nist.secauto.metaschema.binding.io.json.IJsonWritingContext; import gov.nist.secauto.metaschema.binding.model.DefaultAssemblyClassBinding; +import gov.nist.secauto.oscal.lib.model.ImplementedRequirement; import java.io.IOException; +import java.util.Map; +import javax.xml.namespace.QName; +import org.apache.commons.collections4.map.HashedMap; /** * Extends DefaultAssemblyClassBinding to add writing of root items as an array. */ public class IterableAssemblyClassBinding extends DefaultAssemblyClassBinding { + /** + * Map of objects that are not declared as root in liboscal-java, + * but we want to treat them as such. + */ + private static final Map, QName> secondaryRootObjects; + + static { + secondaryRootObjects = new HashedMap<>(); + secondaryRootObjects.put(ImplementedRequirement.class, + new QName("implemented-requirement")); + } + protected IterableAssemblyClassBinding(Class clazz, IBindingContext bindingContext) { super(clazz, bindingContext); } @@ -48,4 +64,18 @@ public void writeRootItems( writer.writeEndArray(); } + + @Override + public boolean isRoot() { + return (super.isRoot() || secondaryRootObjects.containsKey(getBoundClass())); + } + + @Override + public QName getRootXmlQName() { + if (secondaryRootObjects.containsKey(getBoundClass())) { + return secondaryRootObjects.get(getBoundClass()); + } + return super.getRootXmlQName(); + } + } diff --git a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/BaseOscalObjectMarshallerLiboscalImpl.java b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/BaseOscalObjectMarshallerLiboscalImpl.java index 78bdd41..affb94e 100644 --- a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/BaseOscalObjectMarshallerLiboscalImpl.java +++ b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/BaseOscalObjectMarshallerLiboscalImpl.java @@ -7,8 +7,8 @@ import gov.nist.secauto.metaschema.binding.IBindingContext; import gov.nist.secauto.metaschema.binding.io.BindingException; import gov.nist.secauto.metaschema.binding.io.Feature; -import gov.nist.secauto.metaschema.binding.io.Format; import gov.nist.secauto.metaschema.binding.io.IDeserializer; +import gov.nist.secauto.metaschema.binding.io.json.DefaultJsonDeserializer; import gov.nist.secauto.metaschema.binding.model.IAssemblyClassBinding; import java.io.InputStream; import java.io.OutputStream; @@ -33,7 +33,7 @@ public BaseOscalObjectMarshallerLiboscalImpl(Class clazz) { IterableAssemblyClassBinding.createInstance(clazz, context); this.serializer = new IterableJsonSerializer(context, classBinding); this.serializer.enableFeature(Feature.SERIALIZE_ROOT); - this.deserializer = context.newDeserializer(Format.JSON, clazz); + this.deserializer = new DefaultJsonDeserializer(context, classBinding); this.deserializer.enableFeature(Feature.DESERIALIZE_ROOT); } diff --git a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/OscalSspImplReqMarshallerImpl.java b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/OscalSspImplReqMarshallerImpl.java new file mode 100644 index 0000000..6d0bf6a --- /dev/null +++ b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/impl/OscalSspImplReqMarshallerImpl.java @@ -0,0 +1,15 @@ +package com.easydynamics.oscal.data.marshalling.impl; + +import gov.nist.secauto.oscal.lib.model.ImplementedRequirement; + +/** + * OSCAL SSP Implemented Requirement marshaller implementation. + */ +public class OscalSspImplReqMarshallerImpl + extends BaseOscalObjectMarshallerLiboscalImpl { + + public OscalSspImplReqMarshallerImpl() { + super(ImplementedRequirement.class); + } + +} diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/ServiceConfig.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/ServiceConfig.java index 06f2bbf..f7b1823 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/ServiceConfig.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/ServiceConfig.java @@ -4,9 +4,11 @@ import com.easydynamics.oscal.data.marshalling.impl.OscalCatalogMarshallerImpl; import com.easydynamics.oscal.data.marshalling.impl.OscalComponentMarshallerImpl; import com.easydynamics.oscal.data.marshalling.impl.OscalProfileMarshallerImpl; +import com.easydynamics.oscal.data.marshalling.impl.OscalSspImplReqMarshallerImpl; import com.easydynamics.oscal.data.marshalling.impl.OscalSspMarshallerImpl; import gov.nist.secauto.oscal.lib.model.Catalog; import gov.nist.secauto.oscal.lib.model.ComponentDefinition; +import gov.nist.secauto.oscal.lib.model.ImplementedRequirement; import gov.nist.secauto.oscal.lib.model.Profile; import gov.nist.secauto.oscal.lib.model.SystemSecurityPlan; import org.springframework.context.annotation.Bean; @@ -39,4 +41,9 @@ public OscalObjectMarshaller profileMarshaller() { public OscalObjectMarshaller sspMarshaller() { return new OscalSspMarshallerImpl(); } + + @Bean + public OscalObjectMarshaller sspImplReqMarshaller() { + return new OscalSspImplReqMarshallerImpl(); + } } diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java index 1d7edc5..94bcae1 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java @@ -21,8 +21,8 @@ public abstract class BaseOscalController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final BaseOscalObjectService oscalObjectService; - private final OscalObjectMarshaller oscalObjectMarshaller; + protected final BaseOscalObjectService oscalObjectService; + protected final OscalObjectMarshaller oscalObjectMarshaller; protected BaseOscalController( BaseOscalObjectService oscalObjectService, @@ -114,21 +114,21 @@ public ResponseEntity put(String id, String json) { return makeObjectResponse(oscalObjectService.save(incomingOscalObject)); } - private ResponseEntity makeIterableResponse( + protected ResponseEntity makeIterableResponse( Iterable oscalObjectCollection) { return makeResponse( (outputStream) -> oscalObjectMarshaller.toJson(oscalObjectCollection, outputStream), - oscalObjectCollection.getClass()); + oscalObjectCollection.getClass()); } - private ResponseEntity makeObjectResponse(T oscalObject) { + protected ResponseEntity makeObjectResponse(T oscalObject) { return makeResponse( (outputStream) -> oscalObjectMarshaller.toJson(oscalObject, outputStream), - oscalObject.getClass()); + oscalObject.getClass()); } - private ResponseEntity makeResponse( - Consumer marshallingTask, + protected ResponseEntity makeResponse( + Consumer marshallingTask, Class clazz) { StreamingResponseBody responseBody = outputStream -> { diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/OscalControllerExceptionAdvice.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/OscalControllerExceptionAdvice.java index 9d740c1..553b1d0 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/OscalControllerExceptionAdvice.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/OscalControllerExceptionAdvice.java @@ -22,7 +22,7 @@ String oscalObjectNotFoundHandler(OscalObjectNotFoundException exception) { @ResponseBody @ExceptionHandler(OscalObjectConflictException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseStatus(HttpStatus.CONFLICT) String oscalObjectConflictHandler(OscalObjectConflictException exception) { return exception.getMessage(); } diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java index ad69c13..d2290ba 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java @@ -2,14 +2,26 @@ import com.easydynamics.oscal.data.marshalling.OscalObjectMarshaller; import com.easydynamics.oscal.service.OscalSspService; +import com.easydynamics.oscal.service.impl.OscalDeepCopyUtils; +import gov.nist.secauto.oscal.lib.model.ControlImplementation; +import gov.nist.secauto.oscal.lib.model.ImplementedRequirement; import gov.nist.secauto.oscal.lib.model.SystemSecurityPlan; import io.swagger.v3.oas.annotations.Parameter; +import java.io.ByteArrayInputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -23,13 +35,17 @@ @RequestMapping(path = "/oscal/v1") @RestController public class SspController extends BaseOscalController { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final OscalObjectMarshaller oscalSspImplReqtMarshaller; @Autowired(required = true) public SspController( OscalSspService sspService, - OscalObjectMarshaller marshaller + OscalObjectMarshaller marshaller, + OscalObjectMarshaller oscalSspImplReqtMarshaller ) { super(sspService, marshaller); + this.oscalSspImplReqtMarshaller = oscalSspImplReqtMarshaller; } @GetMapping("/system-security-plans") @@ -75,4 +91,122 @@ public ResponseEntity put( @RequestBody String json) { return super.put(id, json); } + + /** + * Similar to unmarshallAndValidateId, checks that the given id + * matches the UUID in the given json. + * + * @param id the request path id + * @param json the request body json + * @return the unmarshalled object + * @throws OscalObjectConflictException when the path ID does not match the body ID + */ + protected ImplementedRequirement unmarshallImplReqAndValidateId(String id, String json) { + ImplementedRequirement incomingOscalObject = oscalSspImplReqtMarshaller.toObject( + new ByteArrayInputStream(json.getBytes())); + + UUID incomingUuid = incomingOscalObject.getUuid(); + if (incomingUuid != null && !id.equals(incomingUuid.toString())) { + throw new OscalObjectConflictException(incomingUuid.toString(), id); + } + + return incomingOscalObject; + } + + /** + * Does the work of finding an existing SSP and updating it with the + * given Implemented Requirement. + * + * @param id the SSP UUID + * @param implementedRequirementId the impl req UUID + * @param json the Implemented Requirement JSON + * @param isCreateOnly requires no impl req with the same UUID exist when true + * @return the response + */ + private ResponseEntity updateImplementedRequirement( + String id, + String implementedRequirementId, + String json, + boolean isCreateOnly) { + SystemSecurityPlan existingSsp = oscalObjectService.findById(id) + .orElseThrow(() -> new OscalObjectNotFoundException(id)); + + ImplementedRequirement incomingImplReq = + unmarshallImplReqAndValidateId(implementedRequirementId, json); + + // Find existing ImplementedRequirement if exists and merge or add + ImplementedRequirement existingImplReq = null; + if (existingSsp.getControlImplementation() != null + && existingSsp.getControlImplementation().getImplementedRequirements() != null) { + existingImplReq = existingSsp.getControlImplementation().getImplementedRequirements().stream() + .filter(implReq -> incomingImplReq.getUuid().equals(implReq.getUuid())) + .findAny() + .orElse(null); + } + if (existingImplReq != null && isCreateOnly) { + throw new OscalObjectConflictException("Implented Requirement already exists"); + } + if (existingImplReq == null) { + ControlImplementation controlImplementation = existingSsp.getControlImplementation(); + if (controlImplementation == null) { + controlImplementation = new ControlImplementation(); + existingSsp.setControlImplementation(controlImplementation); + } + List implReqs = controlImplementation.getImplementedRequirements(); + if (implReqs == null) { + implReqs = new ArrayList<>(); + controlImplementation.setImplementedRequirements(implReqs); + } + implReqs.add(incomingImplReq); + } else { + try { + OscalDeepCopyUtils.deepCopyProperties(existingImplReq, incomingImplReq); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new InvalidDataAccessResourceUsageException( + "could not deep copy object", e); + } + } + + logger.debug("SSP ImplementedRequiremnt updated, saving via service"); + + return makeObjectResponse(oscalObjectService.save(existingSsp)); + } + + /** + * Defines a POST request for updating SSPs control implementation + * implemented requirements. + * + * @param id the SSP uuid + * @param implementedRequirementId the Implemented Requirement uuid + * @param json the SSP contents + */ + @PostMapping(value = "/system-security-plans/{id}/control-implementation/" + + "implemented-requirements/{implementedRequirementId}", + consumes = { MediaType.APPLICATION_JSON_VALUE }, + produces = { MediaType.APPLICATION_JSON_VALUE }) + public ResponseEntity updateImplementedRequirementPost( + @Parameter @PathVariable String id, + @Parameter @PathVariable String implementedRequirementId, + @RequestBody String json) { + return updateImplementedRequirement(id, implementedRequirementId, json, true); + } + + /** + * Defines a PUT request for updating SSPs control implementation + * implemented requirements. + * + * @param id the SSP uuid + * @param implementedRequirementId the Implemented Requirement uuid + * @param json the SSP contents + */ + @PutMapping(value = "/system-security-plans/{id}/control-implementation/" + + "implemented-requirements/{implementedRequirementId}", + consumes = { MediaType.APPLICATION_JSON_VALUE }, + produces = { MediaType.APPLICATION_JSON_VALUE }) + public ResponseEntity updateImplementedRequirementPut( + @Parameter @PathVariable String id, + @Parameter @PathVariable String implementedRequirementId, + @RequestBody String json) { + return updateImplementedRequirement(id, implementedRequirementId, json, false); + } } \ No newline at end of file diff --git a/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/BaseOscalControllerTests.java b/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/BaseOscalControllerTests.java index d4bff64..2c21b50 100644 --- a/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/BaseOscalControllerTests.java +++ b/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/BaseOscalControllerTests.java @@ -38,7 +38,7 @@ public abstract class BaseOscalControllerTests { @Autowired - private MockMvc mockMvc; + protected MockMvc mockMvc; protected OscalObjectType oscalObjectType; protected ExampleContent exampleContent; @@ -50,7 +50,7 @@ public void setup() throws IOException { originalFileContents = Files.readString(Paths.get( "target/oscal-demo-content-main", oscalObjectType.jsonField + "s", - exampleContent.fileName), + exampleContent.fileName), Charset.forName("UTF-8")); } @@ -59,9 +59,9 @@ public void reset() throws IOException { Files.writeString(Paths.get( "target/oscal-demo-content-main", oscalObjectType.jsonField + "s", - exampleContent.fileName), + exampleContent.fileName), originalFileContents, Charset.forName("UTF-8")); - } + } /** * Tests if the GET Request to //{id} will retrieve a valid default object. @@ -242,7 +242,7 @@ public void testFindAllOscalObjects() throws Exception { .andExpect(jsonPath("$[0].*.uuid").value(exampleContent.uuid)); } - @Test + @Test public void testPutOscalObjectNotFound() throws Exception { String id = "0300753c-563b-4956-b6f6-3a68950a5279"; this.mockMvc.perform(put("/oscal/v1/" + oscalObjectType.restPath + "/{id}", id) @@ -253,7 +253,7 @@ public void testPutOscalObjectNotFound() throws Exception { .andExpect(status().isNotFound()); } - @Test + @Test public void testPutOscalObjectUnsupportedMediaType() throws Exception { this.mockMvc.perform(put("/oscal/v1/" + oscalObjectType.restPath + "/{id}", exampleContent.uuid) .contentType(MediaType.APPLICATION_XML_VALUE) @@ -263,7 +263,7 @@ public void testPutOscalObjectUnsupportedMediaType() throws Exception { .andExpect(status().isUnsupportedMediaType()); } - @Test + @Test public void testPutMismatchOscalObject() throws Exception { String badId = "8A7D0A6D-024E-416A-956A-FDEB8816EEA1"; this.mockMvc.perform(put("/oscal/v1/" + oscalObjectType.restPath + "/{id}", exampleContent.uuid) @@ -271,12 +271,12 @@ public void testPutMismatchOscalObject() throws Exception { .accept(MediaType.APPLICATION_JSON) .characterEncoding("UTF-8") .content(generateOscalObject(oscalObjectType.jsonField, badId))) - .andExpect(status().isBadRequest()) + .andExpect(status().isConflict()) .andExpect(content().string(containsStringIgnoringCase(badId))) .andExpect(content().string(containsStringIgnoringCase(exampleContent.uuid))); } - @Test + @Test public void testPutNullIdMismatchOscalObject() throws Exception { String badId = null; this.mockMvc.perform(put("/oscal/v1/" + oscalObjectType.restPath + "/{id}", exampleContent.uuid) @@ -288,7 +288,7 @@ public void testPutNullIdMismatchOscalObject() throws Exception { } - @Test + @Test public void testPutOscalObjectSuccess() throws Exception { this.mockMvc.perform(put("/oscal/v1/" + oscalObjectType.restPath + "/{id}", exampleContent.uuid) .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -310,7 +310,7 @@ private static String generateOscalObject(String objectType, String id) { + " }\n" + "}"; } - + class DateGreaterMatcher extends BaseMatcher { private ZonedDateTime floor; DateGreaterMatcher(ZonedDateTime floor) { diff --git a/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java b/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java index f123a95..5dad9ee 100644 --- a/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java +++ b/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java @@ -1,5 +1,20 @@ package com.easydynamics.oscalrestservice.api; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; + import com.easydynamics.oscal.data.example.ExampleContent; /** @@ -7,9 +22,218 @@ */ public class SspControllerTests extends BaseOscalControllerTests { + private static final String UPDATE_SSP_IMPL_REQ_URL = + "/oscal/v1/system-security-plans/{id}/control-implementation/implemented-requirements/{implementedRequirementId}"; + private static final String EXISTING_IMPL_REQ_UUID = "aaadb3ff-6ae8-4332-92db-211468c52af2"; + private static final String EXISTING_CONTROL_ID = "au-1"; + private static final String EXISTING_STATEMENT_ID = "au-1_smt.a"; + private static final String EXISTING_COMPONENT_UUID = "795533ab-9427-4abe-820f-0b571bacfe6d"; + private static final String EXISTING_PARAM_ID = "au-1_prm_1"; + private static final String NEW_IMPL_REQ_UUID = UUID.randomUUID().toString(); + private static final String NEW_CONTROL_ID = "some-control"; + private static final String NEW_STATEMENT_ID = NEW_CONTROL_ID + "_smt"; + private static final String NEW_COMPONENT_UUID = UUID.randomUUID().toString(); + private static final String NEW_PARAM_ID = NEW_CONTROL_ID + "_prm_1"; + private static final String EXPECTED_PARAM_VALUE = "Some Param Value"; + + private SspControllerTests() { this.oscalObjectType = OscalObjectType.SSP; this.exampleContent = ExampleContent.SSP_EXAMPLE; } + /** + * Builds an SSP Implemented Requirement JSON string with the given + * values. + * + * @param implReqUuid + * @param controlId + * @param statementId + * @param componentUuid + * @param paramId + * @param paramValue + * @return the SSP Implemented Requirement JSON string + */ + private String buildImplementedRequirementJson( + String implReqUuid, + String controlId, + String statementId, + String componentUuid, + String paramId, + String paramValue + ) { + return "{\n" + + " \"implemented-requirement\": {\n" + + " \"uuid\": \"" + implReqUuid + "\",\n" + + " \"control-id\": \"" + controlId + "\",\n" + + " \"statements\": [\n" + + " {\n" + + " \"statement-id\": \"" + statementId + "\",\n" + + " \"by-components\": [\n" + + " {\n" + + " \"component-uuid\": \"" + componentUuid + "\",\n" + + " \"set-parameters\": [\n" + + " {\n" + + " \"param-id\": \"" + paramId + "\",\n" + + " \"values\": [ \"" + paramValue + "\" ]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + } + + /** + * Does the work of actually performing the mock MVC request for + * updating an SSP Impl Req. + * + * @param httpMethod + * @param implReqUuid + * @param controlId + * @param statementId + * @param componentUuid + * @param paramId + * @param expectedParamValue + * @param expectedStatus + * @param isUpdateExpected + * @throws Exception + */ + private void testSspImplReq( + HttpMethod httpMethod, + String implReqUuid, + String controlId, + String statementId, + String componentUuid, + String paramId, + String expectedParamValue, + ResultMatcher expectedStatus, + boolean isUpdateExpected + ) throws Exception { + + String implReq = buildImplementedRequirementJson( + implReqUuid, controlId, statementId, componentUuid, paramId, expectedParamValue); + + if (httpMethod.equals(HttpMethod.PUT)) { + this.mockMvc.perform( + put(UPDATE_SSP_IMPL_REQ_URL, + exampleContent.uuid, + implReqUuid) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(implReq)) + .andExpect(expectedStatus); + } else if (httpMethod.equals(HttpMethod.POST)){ + this.mockMvc.perform( + post(UPDATE_SSP_IMPL_REQ_URL, + exampleContent.uuid, + implReqUuid) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(implReq)) + .andExpect(expectedStatus); + } else { + throw new UnsupportedOperationException(); + } + + // With StreamingResponseBody this is async so we start the request here + MvcResult asyncResult = this.mockMvc.perform(get("/oscal/v1/" + + oscalObjectType.restPath + "/{id}", exampleContent.uuid)) + .andExpect(request().asyncStarted()) + .andReturn(); + + if (isUpdateExpected) { + // On async result check the content and title + this.mockMvc.perform(asyncDispatch(asyncResult)) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/json")) + .andExpect(jsonPath("$.*.control-implementation" + + ".implemented-requirements[?(@.uuid == \"" + implReqUuid + "\")]" + + ".statements[?(@.statement-id == \"" + statementId + "\")]" + + ".by-components[?(@.component-uuid == \"" + componentUuid + "\")]" + + ".set-parameters[?(@.param-id == \"" + paramId + "\")]" + + ".values[0]").value(expectedParamValue)); + } + } + + /** + * Tests if the request to add a new SSP Implemented Requirement updates the SSP via POST. + * + * @throws Exception + */ + @Test + public void testCreateSspImplReqViaPost() throws Exception { + testSspImplReq( + HttpMethod.POST, + NEW_IMPL_REQ_UUID, + NEW_CONTROL_ID, + NEW_STATEMENT_ID, + NEW_COMPONENT_UUID, + NEW_PARAM_ID, + EXPECTED_PARAM_VALUE, + status().isOk(), + true); + } + + /** + * Tests a conflicting request to add a new SSP Implemented Requirement via POST. + * + * @throws Exception + */ + @Test + public void testImplAlreadyExistsViaPost() throws Exception { + + testSspImplReq( + HttpMethod.POST, + EXISTING_IMPL_REQ_UUID, + EXISTING_CONTROL_ID, + EXISTING_STATEMENT_ID, + EXISTING_COMPONENT_UUID, + EXISTING_PARAM_ID, + EXPECTED_PARAM_VALUE, + status().isConflict(), + false); + } + + /** + * Tests if the request to add a new SSP Implemented Requirement updates the SSP via PUT. + * + * @throws Exception + */ + @Test + public void testCreateSspImplReqViaPut() throws Exception { + testSspImplReq( + HttpMethod.PUT, + NEW_IMPL_REQ_UUID, + NEW_CONTROL_ID, + NEW_STATEMENT_ID, + NEW_COMPONENT_UUID, + NEW_PARAM_ID, + EXPECTED_PARAM_VALUE, + status().isOk(), + true); + } + + /** + * Tests if the request to update an existing SSP Implemented Requirement updates the SSP via PUT. + * + * @throws Exception + */ + @Test + public void testUpdateExistingSspImplReq() throws Exception { + testSspImplReq( + HttpMethod.PUT, + EXISTING_IMPL_REQ_UUID, + EXISTING_CONTROL_ID, + EXISTING_STATEMENT_ID, + EXISTING_COMPONENT_UUID, + EXISTING_PARAM_ID, + EXPECTED_PARAM_VALUE, + status().isOk(), + true); + } } \ No newline at end of file From 8a66b1703510fd81ffd5c8c02c43faf131c83787 Mon Sep 17 00:00:00 2001 From: Ray Gauss II Date: Fri, 13 May 2022 08:03:50 -0400 Subject: [PATCH 2/7] Improved secondaryRootObjects handling --- .../IterableAssemblyClassBinding.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java index 290be2c..e51fac6 100644 --- a/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java +++ b/oscal-data-repository-commons/src/main/java/com/easydynamics/oscal/data/marshalling/IterableAssemblyClassBinding.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.util.Map; import javax.xml.namespace.QName; -import org.apache.commons.collections4.map.HashedMap; /** * Extends DefaultAssemblyClassBinding to add writing of root items as an array. @@ -19,13 +18,9 @@ public class IterableAssemblyClassBinding extends DefaultAssemblyClassBinding { * Map of objects that are not declared as root in liboscal-java, * but we want to treat them as such. */ - private static final Map, QName> secondaryRootObjects; - - static { - secondaryRootObjects = new HashedMap<>(); - secondaryRootObjects.put(ImplementedRequirement.class, - new QName("implemented-requirement")); - } + private static final Map, QName> secondaryRootObjects = Map.ofEntries( + Map.entry(ImplementedRequirement.class, new QName("implemented-requirement")) + ); protected IterableAssemblyClassBinding(Class clazz, IBindingContext bindingContext) { super(clazz, bindingContext); @@ -72,10 +67,7 @@ public boolean isRoot() { @Override public QName getRootXmlQName() { - if (secondaryRootObjects.containsKey(getBoundClass())) { - return secondaryRootObjects.get(getBoundClass()); - } - return super.getRootXmlQName(); + return secondaryRootObjects.getOrDefault(getBoundClass(), super.getRootXmlQName()); } } From 1983896b668e836824f66e6431f69780ebb22a0e Mon Sep 17 00:00:00 2001 From: hreineck Date: Sat, 14 May 2022 12:34:53 -0400 Subject: [PATCH 3/7] Separate POST and PUT Endpoint Implementations for SSP Impl Req Separate the implementation of the POST endpoint (that adds a new Impl Req) and the PUT endpoint (that updates existing Impl Reqs) into two functions. This is done to match the API spec which specifies different parameters for the two endpoints; the POST endpoint does not need the Impl Req ID. --- .../oscalrestservice/api/SspController.java | 85 ++++++++++++++----- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java index d2290ba..f280918 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java @@ -113,6 +113,32 @@ protected ImplementedRequirement unmarshallImplReqAndValidateId(String id, Strin return incomingOscalObject; } + /** + * + * Helper function to add a new Implemented Requirement to the list + * of Implemented Requirements in a given SSP. + * + * @param existingSsp the SSP object to update + * @param incomingImplReq the new Implemented Requirement to add + * + */ + private void addImplReqToList( + SystemSecurityPlan existingSsp, + ImplementedRequirement incomingImplReq + ){ + ControlImplementation controlImplementation = existingSsp.getControlImplementation(); + if (controlImplementation == null) { + controlImplementation = new ControlImplementation(); + existingSsp.setControlImplementation(controlImplementation); + } + List implReqs = controlImplementation.getImplementedRequirements(); + if (implReqs == null) { + implReqs = new ArrayList<>(); + controlImplementation.setImplementedRequirements(implReqs); + } + implReqs.add(incomingImplReq); + } + /** * Does the work of finding an existing SSP and updating it with the * given Implemented Requirement. @@ -126,8 +152,7 @@ protected ImplementedRequirement unmarshallImplReqAndValidateId(String id, Strin private ResponseEntity updateImplementedRequirement( String id, String implementedRequirementId, - String json, - boolean isCreateOnly) { + String json) { SystemSecurityPlan existingSsp = oscalObjectService.findById(id) .orElseThrow(() -> new OscalObjectNotFoundException(id)); @@ -143,21 +168,8 @@ private ResponseEntity updateImplementedRequirement( .findAny() .orElse(null); } - if (existingImplReq != null && isCreateOnly) { - throw new OscalObjectConflictException("Implented Requirement already exists"); - } if (existingImplReq == null) { - ControlImplementation controlImplementation = existingSsp.getControlImplementation(); - if (controlImplementation == null) { - controlImplementation = new ControlImplementation(); - existingSsp.setControlImplementation(controlImplementation); - } - List implReqs = controlImplementation.getImplementedRequirements(); - if (implReqs == null) { - implReqs = new ArrayList<>(); - controlImplementation.setImplementedRequirements(implReqs); - } - implReqs.add(incomingImplReq); + addImplReqToList(existingSsp, incomingImplReq); } else { try { OscalDeepCopyUtils.deepCopyProperties(existingImplReq, incomingImplReq); @@ -173,7 +185,39 @@ private ResponseEntity updateImplementedRequirement( } /** - * Defines a POST request for updating SSPs control implementation + * Does the work of finding an existing SSP and adding the + * new Implemented Requirement. + * + * @param id the SSP UUID + * @param json the Implemented Requirement JSON + * @return the response + */ + private ResponseEntity addImplementedRequirement( + String id, + String json) { + SystemSecurityPlan existingSsp = oscalObjectService.findById(id) + .orElseThrow(() -> new OscalObjectNotFoundException(id)); + + ImplementedRequirement incomingImplReq = oscalSspImplReqtMarshaller.toObject( + new ByteArrayInputStream(json.getBytes())); + + // Throw an exception if the Implemented Requirement alrady exists + if (existingSsp.getControlImplementation() != null + && existingSsp.getControlImplementation().getImplementedRequirements() != null + && existingSsp.getControlImplementation().getImplementedRequirements().stream() + .anyMatch(implReq -> incomingImplReq.getUuid().equals(implReq.getUuid()))){ + throw new OscalObjectConflictException("Implented Requirement already exists"); + } + + addImplReqToList(existingSsp, incomingImplReq); + + logger.debug("SSP ImplementedRequiremnt updated, saving via service"); + + return makeObjectResponse(oscalObjectService.save(existingSsp)); + } + + /** + * Defines a POST request for adding SSPs control implementation * implemented requirements. * * @param id the SSP uuid @@ -181,14 +225,13 @@ private ResponseEntity updateImplementedRequirement( * @param json the SSP contents */ @PostMapping(value = "/system-security-plans/{id}/control-implementation/" - + "implemented-requirements/{implementedRequirementId}", + + "implemented-requirements", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE }) public ResponseEntity updateImplementedRequirementPost( @Parameter @PathVariable String id, - @Parameter @PathVariable String implementedRequirementId, @RequestBody String json) { - return updateImplementedRequirement(id, implementedRequirementId, json, true); + return addImplementedRequirement(id, json); } /** @@ -207,6 +250,6 @@ public ResponseEntity updateImplementedRequirementPut( @Parameter @PathVariable String id, @Parameter @PathVariable String implementedRequirementId, @RequestBody String json) { - return updateImplementedRequirement(id, implementedRequirementId, json, false); + return updateImplementedRequirement(id, implementedRequirementId, json); } } \ No newline at end of file From c1574b58c41cd422974e80f285ead319f153cf0e Mon Sep 17 00:00:00 2001 From: hreineck Date: Sat, 14 May 2022 12:49:32 -0400 Subject: [PATCH 4/7] Update Tests POST SSP Impl Req Update the tests to accomodate the changes to the POST SSP Impl Req endpoint's parameters --- .../oscalrestservice/api/SspControllerTests.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java b/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java index 5dad9ee..3dfb9e2 100644 --- a/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java +++ b/oscal-rest-service-app/src/test/java/com/easydynamics/oscalrestservice/api/SspControllerTests.java @@ -22,6 +22,8 @@ */ public class SspControllerTests extends BaseOscalControllerTests { + private static final String ADD_SSP_IMPL_REQ_URL = + "/oscal/v1/system-security-plans/{id}/control-implementation/implemented-requirements"; private static final String UPDATE_SSP_IMPL_REQ_URL = "/oscal/v1/system-security-plans/{id}/control-implementation/implemented-requirements/{implementedRequirementId}"; private static final String EXISTING_IMPL_REQ_UUID = "aaadb3ff-6ae8-4332-92db-211468c52af2"; @@ -128,9 +130,8 @@ private void testSspImplReq( .andExpect(expectedStatus); } else if (httpMethod.equals(HttpMethod.POST)){ this.mockMvc.perform( - post(UPDATE_SSP_IMPL_REQ_URL, - exampleContent.uuid, - implReqUuid) + post(ADD_SSP_IMPL_REQ_URL, + exampleContent.uuid) .contentType(MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON) .characterEncoding("UTF-8") From 926cae5d94052e043d335eb0128d1f0dd7504039 Mon Sep 17 00:00:00 2001 From: hreineck Date: Sat, 14 May 2022 13:26:23 -0400 Subject: [PATCH 5/7] Fix Linter Issues Resolve linter complaints on SSP Impl Req implementation and tests --- .../oscalrestservice/api/SspController.java | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java index f280918..d2db049 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java @@ -113,8 +113,7 @@ protected ImplementedRequirement unmarshallImplReqAndValidateId(String id, Strin return incomingOscalObject; } - /** - * + /** * Helper function to add a new Implemented Requirement to the list * of Implemented Requirements in a given SSP. * @@ -123,20 +122,20 @@ protected ImplementedRequirement unmarshallImplReqAndValidateId(String id, Strin * */ private void addImplReqToList( - SystemSecurityPlan existingSsp, - ImplementedRequirement incomingImplReq - ){ - ControlImplementation controlImplementation = existingSsp.getControlImplementation(); - if (controlImplementation == null) { - controlImplementation = new ControlImplementation(); - existingSsp.setControlImplementation(controlImplementation); - } - List implReqs = controlImplementation.getImplementedRequirements(); - if (implReqs == null) { - implReqs = new ArrayList<>(); - controlImplementation.setImplementedRequirements(implReqs); - } - implReqs.add(incomingImplReq); + SystemSecurityPlan existingSsp, + ImplementedRequirement incomingImplReq + ) { + ControlImplementation controlImplementation = existingSsp.getControlImplementation(); + if (controlImplementation == null) { + controlImplementation = new ControlImplementation(); + existingSsp.setControlImplementation(controlImplementation); + } + List implReqs = controlImplementation.getImplementedRequirements(); + if (implReqs == null) { + implReqs = new ArrayList<>(); + controlImplementation.setImplementedRequirements(implReqs); + } + implReqs.add(incomingImplReq); } /** @@ -203,10 +202,10 @@ private ResponseEntity addImplementedRequirement( // Throw an exception if the Implemented Requirement alrady exists if (existingSsp.getControlImplementation() != null - && existingSsp.getControlImplementation().getImplementedRequirements() != null + && existingSsp.getControlImplementation().getImplementedRequirements() != null && existingSsp.getControlImplementation().getImplementedRequirements().stream() - .anyMatch(implReq -> incomingImplReq.getUuid().equals(implReq.getUuid()))){ - throw new OscalObjectConflictException("Implented Requirement already exists"); + .anyMatch(implReq -> incomingImplReq.getUuid().equals(implReq.getUuid()))) { + throw new OscalObjectConflictException("Implented Requirement already exists"); } addImplReqToList(existingSsp, incomingImplReq); @@ -221,7 +220,6 @@ private ResponseEntity addImplementedRequirement( * implemented requirements. * * @param id the SSP uuid - * @param implementedRequirementId the Implemented Requirement uuid * @param json the SSP contents */ @PostMapping(value = "/system-security-plans/{id}/control-implementation/" From 35ecf8806f60a14e3e22f1dc7dcb9032d2bfa050 Mon Sep 17 00:00:00 2001 From: Kyle Laker Date: Fri, 13 May 2022 23:45:40 -0400 Subject: [PATCH 6/7] Only return the new implemented requirement Per the specification, we must return the _new_ implemented requirement object; not the entire SSP. We can add somewhat more generic versions of the existing `makeXxxResponse` methods for this use case (and we can leave the old implementations as more convenient wrappers for the root object types). --- .../api/BaseOscalController.java | 75 +++++++++++++++---- .../oscalrestservice/api/SspController.java | 6 +- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java index 94bcae1..b3c0f73 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/BaseOscalController.java @@ -14,8 +14,8 @@ /** * BaseOscalController is the superclass for all Controllers that handles request to an Oscal - * endpoint. It uses its repository field to perform CRUD operations on files of the - * corresponding Oscal type. + * endpoint. It uses its repository field to perform CRUD operations on files of the corresponding + * Oscal type. */ public abstract class BaseOscalController { @@ -114,32 +114,79 @@ public ResponseEntity put(String id, String json) { return makeObjectResponse(oscalObjectService.save(incomingOscalObject)); } + /** + * Marshall an iterable collection of OSCAL objects to a response object. + * + *

In particular, this is useful for secondary objects. For root objects in the OSCAL + * hierarchy (and especially for objects of {@code }), consider using + * {@link #makeIterableResponse(Iterable)}. + * + * @param the type of OSCAL objects within the collection + * @param oscalObjectCollection the collection of OSCAL objects + * @param marshaller the marshaller that can convert objects of {@code } to JSON + * @return HTTP response with the objects as JSON + */ + protected ResponseEntity makeIterableResponse( + Iterable oscalObjectCollection, OscalObjectMarshaller marshaller) { + return makeResponse((outputStream) -> marshaller.toJson(oscalObjectCollection, outputStream), + oscalObjectCollection.getClass()); + } + + /** + * Marshall an iterable collection of OSCAL objects to a response object. + * + * @param oscalObjectCollection The collection of OSCAL objects + * @return HTTP response with the objects as JSON + */ protected ResponseEntity makeIterableResponse( Iterable oscalObjectCollection) { - return makeResponse( - (outputStream) -> oscalObjectMarshaller.toJson(oscalObjectCollection, outputStream), - oscalObjectCollection.getClass()); + return makeIterableResponse(oscalObjectCollection, oscalObjectMarshaller); + } + + /** + * Marshall single JSON object to an HTTP response. + * + *

In particular, this is useful for secondary objects. For root objects in the OSCAL + * hierarchy (and especially for objects of {@code }), consider using + * {@link #makeObjectResponse(T)}. + * + * @param the type of the OSCAL object to return + * @param oscalObject the object to include in the response + * @param marshaller the marshaller that can convert objects of {@code } to JSON + * @return HTTP response with the given object as JSON + */ + protected ResponseEntity makeObjectResponse(S oscalObject, + OscalObjectMarshaller marshaller) { + return makeResponse((outputStream) -> marshaller.toJson(oscalObject, outputStream), + oscalObject.getClass()); } + /** + * Marshall single JSON object to an HTTP response. + * + * @param oscalObject the object to include in the response + * @return HTTP response with the given object as JSON + */ protected ResponseEntity makeObjectResponse(T oscalObject) { - return makeResponse( - (outputStream) -> oscalObjectMarshaller.toJson(oscalObject, outputStream), - oscalObject.getClass()); + return makeObjectResponse(oscalObject, oscalObjectMarshaller); } + /** + * Create a Response object from any class given a valid marshaller to do so. + * + * @param marshallingTask the Consumer that will perform the marshalling + * @param clazz the Class object for the object being marshalled + */ protected ResponseEntity makeResponse( - Consumer marshallingTask, - Class clazz) { + Consumer marshallingTask, Class clazz) { StreamingResponseBody responseBody = outputStream -> { - logger.debug("Starting marshalling of object type: {}", - clazz.getName()); + logger.debug("Starting marshalling of object type: {}", clazz.getName()); marshallingTask.accept(outputStream); logger.debug("Marshalling complete"); }; - logger.debug("Returning wrapped response of type: {}", - clazz.getName()); + logger.debug("Returning wrapped response of type: {}", clazz.getName()); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java index d2db049..f7c14d3 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java @@ -179,8 +179,8 @@ private ResponseEntity updateImplementedRequirement( } logger.debug("SSP ImplementedRequiremnt updated, saving via service"); - - return makeObjectResponse(oscalObjectService.save(existingSsp)); + oscalObjectService.save(existingSsp); + return makeObjectResponse(incomingImplReq, oscalSspImplReqtMarshaller); } /** @@ -250,4 +250,4 @@ public ResponseEntity updateImplementedRequirementPut( @RequestBody String json) { return updateImplementedRequirement(id, implementedRequirementId, json); } -} \ No newline at end of file +} From 0ca3851fd6379088624a252bc9903b9d97f07847 Mon Sep 17 00:00:00 2001 From: Ray Gauss II Date: Tue, 17 May 2022 06:44:32 -0400 Subject: [PATCH 7/7] Spelling fixes --- .../oscalrestservice/api/SspController.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java index f7c14d3..e7ead59 100644 --- a/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java +++ b/oscal-rest-service-app/src/main/java/com/easydynamics/oscalrestservice/api/SspController.java @@ -36,7 +36,7 @@ @RestController public class SspController extends BaseOscalController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final OscalObjectMarshaller oscalSspImplReqtMarshaller; + private final OscalObjectMarshaller oscalSspImplReqMarshaller; @Autowired(required = true) public SspController( @@ -45,7 +45,7 @@ public SspController( OscalObjectMarshaller oscalSspImplReqtMarshaller ) { super(sspService, marshaller); - this.oscalSspImplReqtMarshaller = oscalSspImplReqtMarshaller; + this.oscalSspImplReqMarshaller = oscalSspImplReqtMarshaller; } @GetMapping("/system-security-plans") @@ -102,7 +102,7 @@ public ResponseEntity put( * @throws OscalObjectConflictException when the path ID does not match the body ID */ protected ImplementedRequirement unmarshallImplReqAndValidateId(String id, String json) { - ImplementedRequirement incomingOscalObject = oscalSspImplReqtMarshaller.toObject( + ImplementedRequirement incomingOscalObject = oscalSspImplReqMarshaller.toObject( new ByteArrayInputStream(json.getBytes())); UUID incomingUuid = incomingOscalObject.getUuid(); @@ -180,11 +180,11 @@ private ResponseEntity updateImplementedRequirement( logger.debug("SSP ImplementedRequiremnt updated, saving via service"); oscalObjectService.save(existingSsp); - return makeObjectResponse(incomingImplReq, oscalSspImplReqtMarshaller); + return makeObjectResponse(incomingImplReq, oscalSspImplReqMarshaller); } /** - * Does the work of finding an existing SSP and adding the + * Does the work of finding an existing SSP and adding the * new Implemented Requirement. * * @param id the SSP UUID @@ -197,7 +197,7 @@ private ResponseEntity addImplementedRequirement( SystemSecurityPlan existingSsp = oscalObjectService.findById(id) .orElseThrow(() -> new OscalObjectNotFoundException(id)); - ImplementedRequirement incomingImplReq = oscalSspImplReqtMarshaller.toObject( + ImplementedRequirement incomingImplReq = oscalSspImplReqMarshaller.toObject( new ByteArrayInputStream(json.getBytes())); // Throw an exception if the Implemented Requirement alrady exists @@ -205,9 +205,9 @@ private ResponseEntity addImplementedRequirement( && existingSsp.getControlImplementation().getImplementedRequirements() != null && existingSsp.getControlImplementation().getImplementedRequirements().stream() .anyMatch(implReq -> incomingImplReq.getUuid().equals(implReq.getUuid()))) { - throw new OscalObjectConflictException("Implented Requirement already exists"); + throw new OscalObjectConflictException("Implemented Requirement already exists"); } - + addImplReqToList(existingSsp, incomingImplReq); logger.debug("SSP ImplementedRequiremnt updated, saving via service");