diff --git a/src/main/java/org/dependencytrack/model/CryptoAssetProperties.java b/src/main/java/org/dependencytrack/model/CryptoAssetProperties.java index d71aec0cd..e329b10d0 100644 --- a/src/main/java/org/dependencytrack/model/CryptoAssetProperties.java +++ b/src/main/java/org/dependencytrack/model/CryptoAssetProperties.java @@ -77,10 +77,6 @@ public class CryptoAssetProperties implements Serializable { @Pattern(regexp = "^([0-2])((\\.0)|(\\.[1-9][0-9]*))*$", message = "The OID must be a valid") private String oid; - public long getId() { - return id; - } - public AssetType getAssetType() { return assetType; } diff --git a/src/main/java/org/dependencytrack/persistence/CryptoAssetQueryManager.java b/src/main/java/org/dependencytrack/persistence/CryptoAssetQueryManager.java index fed975469..c231f3602 100644 --- a/src/main/java/org/dependencytrack/persistence/CryptoAssetQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/CryptoAssetQueryManager.java @@ -34,6 +34,7 @@ import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; +import jakarta.validation.constraints.NotNull; public class CryptoAssetQueryManager extends QueryManager implements IQueryManager { @@ -54,18 +55,13 @@ public class CryptoAssetQueryManager extends QueryManager implements IQueryManag super(pm, request); } - private static final String globalFilter = setGlobalFilter(); - private static String setGlobalFilter() { - return "bomReference == null"; - } - /** * Returns a complete list of all CryptoAssets * @return a List of CryptoAssets */ @SuppressWarnings("unchecked") public List getAllCryptoAssets() { - final Query query = pm.newQuery(Component.class, globalFilter + " && (classifier == :asset)"); + final Query query = pm.newQuery(Component.class, "(classifier == :asset)"); query.getFetchPlan().setMaxFetchDepth(3); return (List) query.execute(Classifier.CRYPTOGRAPHIC_ASSET); } @@ -78,7 +74,7 @@ public List getAllCryptoAssets() { */ @SuppressWarnings("unchecked") public List getAllCryptoAssets(Project project) { - final Query query = pm.newQuery(Component.class, globalFilter + " && (project == :project) && (classifier == :asset)"); + final Query query = pm.newQuery(Component.class, "(project == :project) && (classifier == :asset)"); query.getFetchPlan().setMaxFetchDepth(3); query.setOrdering("name asc"); return (List)query.execute(project, Classifier.CRYPTOGRAPHIC_ASSET); @@ -89,17 +85,14 @@ public List getAllCryptoAssets(Project project) { * @param identity the asset identity to query against * @return a list of components */ - public PaginatedResult getCryptoAssets(ComponentIdentity identity) { - if (identity == null) { - return null; - } + public PaginatedResult getCryptoAssets(@NotNull ComponentIdentity identity) { Pair, HashMap> queryProp = buildIdentityQuery(identity); String filter = String.join(" && ", queryProp.getKey()); return loadComponents(filter, queryProp.getValue()); } private PaginatedResult loadComponents(String queryFilter, Map params) { - var query = pm.newQuery(Component.class, globalFilter); + var query = pm.newQuery(Component.class); query.getFetchPlan().setMaxFetchDepth(3); if (orderBy == null) { query.setOrdering("id asc"); @@ -108,11 +101,7 @@ private PaginatedResult loadComponents(String queryFilter, Map p return execute(query, params); } - private Pair, HashMap> buildIdentityQuery(ComponentIdentity identity) { - if (identity == null) { - return null; - } - + private Pair, HashMap> buildIdentityQuery(@NotNull ComponentIdentity identity) { final var queryFilterElements = new ArrayList(); final var queryParams = new HashMap(); diff --git a/src/main/java/org/dependencytrack/resources/v1/CryptoAssetsResource.java b/src/main/java/org/dependencytrack/resources/v1/CryptoAssetsResource.java index 6d46db10e..afecf1718 100644 --- a/src/main/java/org/dependencytrack/resources/v1/CryptoAssetsResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/CryptoAssetsResource.java @@ -23,6 +23,8 @@ import alpine.server.resources.AlpineResource; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -67,6 +69,10 @@ @Path("/v1/crypto") @Tag(name = "crypto") +@SecurityRequirements({ + @SecurityRequirement(name = "ApiKeyAuth"), + @SecurityRequirement(name = "BearerAuth") +}) public class CryptoAssetsResource extends AlpineResource { @GET @@ -85,7 +91,7 @@ public class CryptoAssetsResource extends AlpineResource { ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified crypto asset is forbidden"), - @ApiResponse(responseCode = "404", description = "The rypto asset could not be found") + @ApiResponse(responseCode = "404", description = "The crypto asset could not be found.") }) public Response getAllCryptoAssetsOfAProject(@PathParam("uuid") String uuid) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { @@ -118,7 +124,6 @@ public Response getAllCryptoAssetsOfAProject(@PathParam("uuid") String uuid) { @ApiResponse( responseCode = "200", description = "A crypto asset", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of crypto assets", schema = @Schema(format = "integer")), content = @Content(schema = @Schema(implementation = Component.class)) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -134,13 +139,14 @@ public Response getCryptoAssetByUuid( if (component != null && component.getClassifier() == Classifier.CRYPTOGRAPHIC_ASSET) { final Project project = component.getProject(); if (qm.hasAccess(super.getPrincipal(), project)) { + qm.getPersistenceManager().getFetchPlan().setMaxFetchDepth(3); final Component asset = qm.detach(Component.class, component.getId()); // TODO: Force project to be loaded. It should be anyway, but JDO seems to be having issues here. return Response.ok(asset).build(); } else { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified crypto asset is forbidden").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The crypto asset could not be found").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The crypto asset could not be found.").build(); } } } @@ -289,6 +295,7 @@ public Response createComponent(@PathParam("uuid") String uuid, Component jsonCo description = "The updated component", content = @Content(schema = @Schema(implementation = Component.class)) ), + @ApiResponse(responseCode = "400", description = "No data for crypto asset properties provided"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified component is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the component could not be found"), diff --git a/src/test/java/org/dependencytrack/ResourceTest.java b/src/test/java/org/dependencytrack/ResourceTest.java index 5fe3d1627..b86deb238 100644 --- a/src/test/java/org/dependencytrack/ResourceTest.java +++ b/src/test/java/org/dependencytrack/ResourceTest.java @@ -52,6 +52,7 @@ public abstract class ResourceTest { protected final String V1_BOM = "/v1/bom"; protected final String V1_CALCULATOR = "/v1/calculator"; protected final String V1_COMPONENT = "/v1/component"; + protected final String V1_CRYPTO = "/v1/crypto"; protected final String V1_DEPENDENCY_GRAPH = "/v1/dependencyGraph"; protected final String V1_CONFIG_PROPERTY = "/v1/configProperty"; protected final String V1_CWE = "/v1/cwe"; diff --git a/src/test/java/org/dependencytrack/model/CipherSuiteTest.java b/src/test/java/org/dependencytrack/model/CipherSuiteTest.java new file mode 100644 index 000000000..602264abb --- /dev/null +++ b/src/test/java/org/dependencytrack/model/CipherSuiteTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of Dependency-Track. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + + +public class CipherSuiteTest { + + @Test + public void testCipherSuite() { + CipherSuite cs = new CipherSuite(); + String name = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"; + List algorithms = List.of( + "crypto/algorithm/ecdh-curve25519@1.3.132.1.12", + "crypto/algorithm/rsa-2048@1.2.840.113549.1.1.1", + "crypto/algorithm/aes-256-gcm@2.16.840.1.101.3.4.1.46", + "crypto/algorithm/sha-384@2.16.840.1.101.3.4.2.9" + ); + List identifiers = List.of("a", "b", "c", "d"); + String location = "httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java"; + String addittionalContext = "javax.crypto.spec.SecretKeySpec#([BLjava/lang/String;)V"; + String bomRef = "471d7b60-0e38-4373-9e66-799d9fbea5de"; + cs.setAlgorithms(algorithms); + cs.setName(name); + cs.setIdentifiers(identifiers); + + Assert.assertEquals(algorithms, cs.getAlgorithms()); + Assert.assertEquals(name, cs.getName()); + Assert.assertEquals(identifiers, cs.getIdentifiers()); + } +} diff --git a/src/test/java/org/dependencytrack/model/CryptoAlgorithmPropertiesTest.java b/src/test/java/org/dependencytrack/model/CryptoAlgorithmPropertiesTest.java index b8d44681c..031ea4291 100644 --- a/src/test/java/org/dependencytrack/model/CryptoAlgorithmPropertiesTest.java +++ b/src/test/java/org/dependencytrack/model/CryptoAlgorithmPropertiesTest.java @@ -35,9 +35,11 @@ public class CryptoAlgorithmPropertiesTest { @Test public void testCryptoAlgorithmProperties() { + String curve = "Curve25519"; CryptoAlgorithmProperties cap = new CryptoAlgorithmProperties(); cap.setPrimitive(Primitive.AE); cap.setParameterSetIdentifier("128"); + cap.setCurve(curve); cap.setExecutionEnvironment(ExecutionEnvironment.SOFTWARE_PLAIN_RAM); cap.setImplementationPlatform(ImplementationPlatform.X86_64); cap.setCertificationLevel(CertificationLevel.NONE); @@ -50,6 +52,7 @@ public void testCryptoAlgorithmProperties() { Assert.assertEquals(Primitive.AE, cap.getPrimitive()); Assert.assertEquals("128", cap.getParameterSetIdentifier()); + Assert.assertEquals(curve, cap.getCurve()); Assert.assertEquals(ExecutionEnvironment.SOFTWARE_PLAIN_RAM, cap.getExecutionEnvironment()); Assert.assertEquals(ImplementationPlatform.X86_64, cap.getImplementationPlatform()); Assert.assertEquals(CertificationLevel.NONE, cap.getCertificationLevel()); diff --git a/src/test/java/org/dependencytrack/model/CryptoRelatedMaterialPropertiesTest.java b/src/test/java/org/dependencytrack/model/CryptoRelatedMaterialPropertiesTest.java index 0d8bc6e63..1dc994360 100644 --- a/src/test/java/org/dependencytrack/model/CryptoRelatedMaterialPropertiesTest.java +++ b/src/test/java/org/dependencytrack/model/CryptoRelatedMaterialPropertiesTest.java @@ -55,6 +55,8 @@ public void testCryptoRelatedMaterialProperties() { rel.setSecuredByMechanism(Mechanism.SOFTWARE); Assert.assertEquals(RelatedCryptoMaterialType.DIGEST, rel.getType()); + Assert.assertEquals(identifier, rel.getIdentifier()); + Assert.assertEquals(State.ACTIVE, rel.getState()); Assert.assertEquals(algorithmRef, rel.getAlgorithmRef()); Assert.assertEquals(DateUtil.fromISO8601(creationDate), rel.getCreationDate()); Assert.assertEquals(DateUtil.fromISO8601(activationDate), rel.getActivationDate()); diff --git a/src/test/java/org/dependencytrack/model/OccurrenceTest.java b/src/test/java/org/dependencytrack/model/OccurrenceTest.java index 09d2984b5..e6a087513 100644 --- a/src/test/java/org/dependencytrack/model/OccurrenceTest.java +++ b/src/test/java/org/dependencytrack/model/OccurrenceTest.java @@ -42,6 +42,7 @@ public void testOccurrence() { Assert.assertEquals((Integer)42, o.getOffset()); Assert.assertEquals(location, o.getLocation()); Assert.assertEquals(addittionalContext, o.getAdditionalContext()); + Assert.assertEquals((Integer)0, o.getSymbol()); Assert.assertEquals(bomRef, o.getBomRef()); } } diff --git a/src/test/java/org/dependencytrack/persistence/CryptoAssetQueryManagerTest.java b/src/test/java/org/dependencytrack/persistence/CryptoAssetQueryManagerTest.java new file mode 100644 index 000000000..237e3c651 --- /dev/null +++ b/src/test/java/org/dependencytrack/persistence/CryptoAssetQueryManagerTest.java @@ -0,0 +1,99 @@ +/* + * This file is part of Dependency-Track. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.persistence; + +import org.cyclonedx.model.component.crypto.enums.AssetType; +import org.cyclonedx.model.component.crypto.enums.Primitive; +import org.dependencytrack.PersistenceCapableTest; +import org.dependencytrack.model.Classifier; +import org.dependencytrack.model.Component; +import org.dependencytrack.model.ComponentIdentity; +import org.dependencytrack.model.CryptoAlgorithmProperties; +import org.dependencytrack.model.CryptoAssetProperties; +import org.dependencytrack.model.Project; + +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CryptoAssetQueryManagerTest extends PersistenceCapableTest { + + private Component persistCryptoAsset() { + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + final var component = new Component(); + component.setProject(project); + component.setName("acme-crypto"); + component.setVersion("1.0.0"); + component.setClassifier(Classifier.CRYPTOGRAPHIC_ASSET); + component.setBomRef("x"); + + CryptoAlgorithmProperties cap = new CryptoAlgorithmProperties(); + cap.setPrimitive(Primitive.AE); + cap.setParameterSetIdentifier("128"); + + CryptoAssetProperties cp = new CryptoAssetProperties(); + cp.setAssetType(AssetType.ALGORITHM); + cp.setAlgorithmProperties(cap); + + component.setCryptoAssetProperties(cp); + return qm.persist(component); + } + + @Test + public void TestGetAllCryptoAssets() { + Component component = persistCryptoAsset(); + List components = qm.getAllCryptoAssets(); + assertThat(components).isNotNull(); + assertThat(components).hasSize(1); + assertThat(components).satisfiesExactlyInAnyOrder(c -> { + assertThat(c.getClassifier()).isEqualTo(component.getClassifier()); + assertThat(c.getCryptoAssetProperties()).isEqualTo(component.getCryptoAssetProperties()); + }); + } + + @Test + public void TestGetAllCryptoAssetsPerProject() { + Component component = persistCryptoAsset(); + List components = qm.getAllCryptoAssets(component.getProject()); + assertThat(components).isNotNull(); + assertThat(components).hasSize(1); + assertThat(components).satisfiesExactlyInAnyOrder(c -> { + assertThat(c.getClassifier()).isEqualTo(component.getClassifier()); + assertThat(c.getCryptoAssetProperties()).isEqualTo(component.getCryptoAssetProperties()); + }); + } + + @Test + public void TestGetAllCryptoAssetByIdentity() { + Component component = persistCryptoAsset(); + List components = qm.getCryptoAssets(new ComponentIdentity(AssetType.ALGORITHM)).getList(Component.class); + assertThat(components).isNotNull(); + assertThat(components).hasSize(1); + assertThat(components).satisfiesExactlyInAnyOrder(c -> { + assertThat(c.getClassifier()).isEqualTo(component.getClassifier()); + assertThat(c.getCryptoAssetProperties()).isEqualTo(component.getCryptoAssetProperties()); + }); + } +} \ No newline at end of file diff --git a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java index d0863a1b9..f4a3f01ba 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java @@ -132,7 +132,7 @@ private Project prepareProject() throws MalformedPackageURLException { } @Test - public void getComponentByUuidTest() { + public void getCryptoAssetByUuidTest() { Project project = qm.createProject("Acme Application", null, null, null, null, null, true, false); Component component = new Component(); component.setProject(project); @@ -148,7 +148,7 @@ public void getComponentByUuidTest() { } @Test - public void getComponentByInvalidUuidTest() { + public void getCryptoAssetByInvalidUuidTest() { Response response = jersey.target(V1_COMPONENT + "/" + UUID.randomUUID()) .request().header(X_API_KEY, apiKey).get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); @@ -387,7 +387,7 @@ public void getComponentByIdentityWithCoordinatesTest() { componentA.setPurl("pkg:maven/groupB/nameB@versionB?baz=qux"); componentB = qm.createComponent(componentB, false); - final Response response = jersey.target(V1_COMPONENT + "/identity") + final Response response = jersey.target(V1_COMPONENT + "/identity").property(FILTER, projectB) .queryParam("group", "groupB") .queryParam("name", "nameB") .queryParam("version", "versionB") diff --git a/src/test/java/org/dependencytrack/resources/v1/CryptoAssetsResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/CryptoAssetsResourceTest.java new file mode 100644 index 000000000..ed5ccd7b8 --- /dev/null +++ b/src/test/java/org/dependencytrack/resources/v1/CryptoAssetsResourceTest.java @@ -0,0 +1,251 @@ +/* + * This file is part of Dependency-Track. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.UUID; + +import org.apache.http.HttpStatus; +import org.cyclonedx.model.component.crypto.enums.AssetType; +import org.cyclonedx.model.component.crypto.enums.Primitive; +import org.dependencytrack.JerseyTestRule; +import org.dependencytrack.ResourceTest; +import org.dependencytrack.event.kafka.KafkaTopics; +import org.dependencytrack.model.Classifier; +import org.dependencytrack.model.Component; +import org.dependencytrack.model.CryptoAlgorithmProperties; +import org.dependencytrack.model.CryptoAssetProperties; +import org.dependencytrack.model.Project; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; + +import alpine.common.util.UuidUtil; +import alpine.server.filters.ApiFilter; +import alpine.server.filters.AuthenticationFilter; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +public class CryptoAssetsResourceTest extends ResourceTest { + + @ClassRule + public static JerseyTestRule jersey = new JerseyTestRule( + new ResourceConfig(CryptoAssetsResource.class) + .register(ApiFilter.class) + .register(AuthenticationFilter.class)); + + + private Component getTestCryptoAsset() { + Project project = qm.createProject("Acme Application", null, null, null, null, null, true, false); + Component component = new Component(); + component.setProject(project); + component.setName("ABC"); + component.setClassifier(Classifier.CRYPTOGRAPHIC_ASSET); + + CryptoAlgorithmProperties cap = new CryptoAlgorithmProperties(); + cap.setPrimitive(Primitive.AE); + cap.setParameterSetIdentifier("128"); + + CryptoAssetProperties cp = new CryptoAssetProperties(); + cp.setAssetType(AssetType.ALGORITHM); + cp.setAlgorithmProperties(cap); + + component.setCryptoAssetProperties(cp); + return qm.createComponent(component, false); + } + + @Test + public void getCryptoAssetByUuidTest() { + Component component = getTestCryptoAsset(); + Response response = jersey.target(V1_CRYPTO + "/" + component.getUuid()) + .request().header(X_API_KEY, apiKey).get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals(component.getName(), json.getString("name")); + Assert.assertEquals(component.getClassifier(), Classifier.valueOf(json.getString("classifier"))); + } + + @Test + public void getCryptoAssetByInvalidUuidTest() { + Response response = jersey.target(V1_CRYPTO + "/" + UUID.randomUUID()) + .request().header(X_API_KEY, apiKey).get(Response.class); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The crypto asset could not be found.", body); + } + + @Test + public void getCryptoAssetsByProjectTest() { + Component component = getTestCryptoAsset(); + Response response = jersey.target(V1_CRYPTO + "/project/" + component.getProject().getUuid()) + .request().header(X_API_KEY, apiKey).get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("1", response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonArray jsonArray = parseJsonArray(response); + Assert.assertNotNull(jsonArray); + JsonObject json = jsonArray.getFirst().asJsonObject(); + Assert.assertEquals(component.getName(), json.getString("name")); + Assert.assertEquals(component.getClassifier(), Classifier.valueOf(json.getString("classifier"))); + } + + @Test + public void getCryptoAssetsByInvalidProjectTest() { + Response response = jersey.target(V1_CRYPTO + "/project/" + UUID.randomUUID()) + .request().header(X_API_KEY, apiKey).get(Response.class); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The project could not be found.", body); + } + + @Test + public void getCryptoByIdentityTest() { + Component component = getTestCryptoAsset(); + final Response response = jersey.target(V1_CRYPTO + "/identity") + .queryParam("assetType", AssetType.ALGORITHM.toString()) + .request() + .header(X_API_KEY, apiKey) + .get(Response.class); + assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + + final JsonArray json = parseJsonArray(response); + assertThat(json).hasSize(1); + + final JsonObject jsonComponent = json.getJsonObject(0); + assertThat(jsonComponent).isNotNull(); + assertThat(jsonComponent.getString("uuid")).isEqualTo(component.getUuid().toString()); + } + + @Test + public void createCryptoAssetTest() { + Component component = getTestCryptoAsset(); + Response response = jersey.target(V1_CRYPTO + "/project/" + component.getProject().getUuid()).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(component, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertEquals(component.getName(), json.getString("name")); + Assert.assertEquals(component.getClassifier(), Classifier.valueOf(json.getString("classifier"))); + Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid"))); + assertThat(kafkaMockProducer.history()).satisfiesExactly( + record -> assertThat(record.topic()).isEqualTo(KafkaTopics.NOTIFICATION_PROJECT_CREATED.name()) + ); + } + + @Test + public void createCryptoAssetsInvalidProjectTest() { + Component component = getTestCryptoAsset(); + Response response = jersey.target(V1_CRYPTO + "/project/" + UUID.randomUUID()).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(component, MediaType.APPLICATION_JSON)); + String body = getPlainTextBody(response); + Assert.assertEquals("The project could not be found.", body); + } + + @Test + public void updateCryptoAssetInvalidClassifierTest() { + Component component = getTestCryptoAsset(); + var jsonComponent = new Component(); + jsonComponent.setUuid(component.getUuid()); + jsonComponent.setName(component.getName()); + Response response = jersey.target(V1_CRYPTO).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonComponent, MediaType.APPLICATION_JSON)); + Assert.assertEquals(400, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The component you provided is not a crypto asset", body); + } + + @Test + public void updateCryptoAssetNoCryptoTest() { + Component component = getTestCryptoAsset(); + var jsonComponent = new Component(); + jsonComponent.setUuid(component.getUuid()); + jsonComponent.setName(component.getName()); + jsonComponent.setClassifier(component.getClassifier()); + Response response = jersey.target(V1_CRYPTO).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonComponent, MediaType.APPLICATION_JSON)); + Assert.assertEquals(400, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("No data for crypto asset properties provided", body); + } + + @Test + public void updateCryptoAssetTest() { + Component component = getTestCryptoAsset(); + var jsonComponent = new Component(); + jsonComponent.setUuid(component.getUuid()); + jsonComponent.setName(component.getName()); + jsonComponent.setClassifier(component.getClassifier()); + jsonComponent.setCryptoAssetProperties(component.getCryptoAssetProperties()); + Response response = jersey.target(V1_CRYPTO).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonComponent, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertEquals(jsonComponent.getName(), json.getString("name")); + Assert.assertEquals(component.getClassifier(), Classifier.valueOf(json.getString("classifier"))); + Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid"))); + assertThat(kafkaMockProducer.history()).satisfiesExactly( + record -> assertThat(record.topic()).isEqualTo(KafkaTopics.NOTIFICATION_PROJECT_CREATED.name()) + ); + } + + @Test + public void updateCryptoAssetInvalidUUIDTest() { + Component component = getTestCryptoAsset(); + var jsonComponent = new Component(); + jsonComponent.setUuid(UUID.randomUUID()); + jsonComponent.setName(component.getName()); + jsonComponent.setClassifier(component.getClassifier()); + jsonComponent.setCryptoAssetProperties(component.getCryptoAssetProperties()); + Response response = jersey.target(V1_CRYPTO).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonComponent, MediaType.APPLICATION_JSON)); + Assert.assertEquals(404, response.getStatus(), 0); + String body = getPlainTextBody(response); + Assert.assertEquals("The UUID of the component could not be found.", body); + } + + @Test + public void deleteCryptoAssetTest() { + Component component = getTestCryptoAsset(); + Response response = jersey.target(V1_CRYPTO + "/" + component.getUuid().toString()) + .request().header(X_API_KEY, apiKey).delete(); + Assert.assertEquals(204, response.getStatus(), 0); + } + + @Test + public void deleteCryptoAssetInvalidUUIDTest() { + Response response = jersey.target(V1_COMPONENT + "/" + UUID.randomUUID()) + .request().header(X_API_KEY, apiKey).delete(); + Assert.assertEquals(404, response.getStatus(), 0); + } +}