From e4e6dc175fad5f6b340ee59a300e1298423fbb55 Mon Sep 17 00:00:00 2001 From: JCHacking Date: Thu, 13 Jun 2024 12:12:11 +0200 Subject: [PATCH 1/4] feat: autocreate project with tags Refs: 1674 Signed-off-by: JCHacking --- .../resources/v1/BomResource.java | 2 +- .../resources/v1/vo/BomSubmitRequest.java | 13 +++++++++++- .../resources/v1/BomResourceTest.java | 20 +++++++++---------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/BomResource.java b/src/main/java/org/dependencytrack/resources/v1/BomResource.java index 76910dd738..c643a783a1 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BomResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BomResource.java @@ -314,7 +314,7 @@ public Response uploadBom(@Parameter(required = true) BomSubmitRequest request) } } - project = qm.createProject(StringUtils.trimToNull(request.getProjectName()), null, StringUtils.trimToNull(request.getProjectVersion()), null, parent, null, true, true); + project = qm.createProject(StringUtils.trimToNull(request.getProjectName()), null, StringUtils.trimToNull(request.getProjectVersion()), request.getProjectTags(), parent, null, true, true); Principal principal = getPrincipal(); qm.updateNewProjectACL(project, principal); } else { diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java b/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java index 00281d4b9b..9d5c07e146 100644 --- a/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java +++ b/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java @@ -18,6 +18,7 @@ */ package org.dependencytrack.resources.v1.vo; +import org.dependencytrack.model.Tag; import alpine.common.validation.RegexSequence; import alpine.server.json.TrimmedStringDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; @@ -28,6 +29,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import java.util.List; /** * Defines a custom request object used when uploading bill-of-material (bom) documents. @@ -51,6 +53,8 @@ public final class BomSubmitRequest { @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The project version may only contain printable characters") private final String projectVersion; + private final List projectTags; + @Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", message = "The parent UUID must be a valid 36 character UUID") private final String parentUUID; @@ -71,15 +75,17 @@ public final class BomSubmitRequest { public BomSubmitRequest(String project, String projectName, String projectVersion, + List projectTags, boolean autoCreate, String bom) { - this(project, projectName, projectVersion, autoCreate, null, null, null, bom); + this(project, projectName, projectVersion, projectTags, autoCreate, null, null, null, bom); } @JsonCreator public BomSubmitRequest(@JsonProperty(value = "project") String project, @JsonProperty(value = "projectName") String projectName, @JsonProperty(value = "projectVersion") String projectVersion, + @JsonProperty(value = "projectTags") List projectTags, @JsonProperty(value = "autoCreate") boolean autoCreate, @JsonProperty(value = "parentUUID") String parentUUID, @JsonProperty(value = "parentName") String parentName, @@ -88,6 +94,7 @@ public BomSubmitRequest(@JsonProperty(value = "project") String project, this.project = project; this.projectName = projectName; this.projectVersion = projectVersion; + this.projectTags = projectTags; this.autoCreate = autoCreate; this.parentUUID = parentUUID; this.parentName = parentName; @@ -110,6 +117,10 @@ public String getProjectVersion() { return projectVersion; } + public List getProjectTags() { + return projectTags; + } + @Schema(example = "5341f53c-611b-4388-9d9c-731026dc5eec") public String getParentUUID() { return parentUUID; diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 8beb695155..0bca5c8b1d 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -726,7 +726,7 @@ public void uploadBomTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD); Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(project.getUuid().toString(), null, null, false, bomString); + BomSubmitRequest request = new BomSubmitRequest(project.getUuid().toString(), null, null, null, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -741,7 +741,7 @@ public void uploadBomTest() throws Exception { public void uploadBomInvalidProjectTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(UUID.randomUUID().toString(), null, null, false, bomString); + BomSubmitRequest request = new BomSubmitRequest(UUID.randomUUID().toString(), null, null, null, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -755,7 +755,7 @@ public void uploadBomInvalidProjectTest() throws Exception { public void uploadBomAutoCreateTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -771,7 +771,7 @@ public void uploadBomAutoCreateTest() throws Exception { @Test public void uploadBomUnauthorizedTest() throws Exception { String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -785,7 +785,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); // Upload parent project - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Parent", "1.0", true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Parent", "1.0", null, true, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -797,7 +797,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { String parentUUID = parent.getUuid().toString(); // Upload first child, search parent by UUID - request = new BomSubmitRequest(null, "Acme Example", "1.0", true, parentUUID, null, null, bomString); + request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, parentUUID, null, null, bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -813,7 +813,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { // Upload second child, search parent by name+ver - request = new BomSubmitRequest(null, "Acme Example", "2.0", true, null, "Acme Parent", "1.0", bomString); + request = new BomSubmitRequest(null, "Acme Example", "2.0", null, true, null, "Acme Parent", "1.0", bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -828,7 +828,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { Assert.assertEquals(parentUUID, child.getParent().getUuid().toString()); // Upload third child, specify parent's UUID, name, ver. Name and ver are ignored when UUID is specified. - request = new BomSubmitRequest(null, "Acme Example", "3.0", true, parentUUID, "Non-existent parent", "1.0", bomString); + request = new BomSubmitRequest(null, "Acme Example", "3.0", null, true, parentUUID, "Non-existent parent", "1.0", bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -847,7 +847,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { public void uploadBomInvalidParentTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", true, UUID.randomUUID().toString(), null, null, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, UUID.randomUUID().toString(), null, null, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -855,7 +855,7 @@ public void uploadBomInvalidParentTest() throws Exception { String body = getPlainTextBody(response); Assert.assertEquals("The parent component could not be found.", body); - request = new BomSubmitRequest(null, "Acme Example", "2.0", true, null, "Non-existent parent", null, bomString); + request = new BomSubmitRequest(null, "Acme Example", "2.0", null, true, null, "Non-existent parent", null, bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); From f4544009fe4327a781d97a36c4b8fa7e7750f452 Mon Sep 17 00:00:00 2001 From: JCHacking Date: Fri, 14 Jun 2024 12:48:41 +0200 Subject: [PATCH 2/4] test: add test for autocreate project with tags Refs: 1674 Signed-off-by: JCHacking --- .../resources/v1/BomResourceTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 0bca5c8b1d..c816b2edb7 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -38,6 +38,7 @@ import org.dependencytrack.model.ProjectMetadata; import org.dependencytrack.model.ProjectProperty; import org.dependencytrack.model.Severity; +import org.dependencytrack.model.Tag; import org.dependencytrack.model.Vulnerability; import org.dependencytrack.parser.cyclonedx.CycloneDxValidator; import org.dependencytrack.resources.v1.exception.JsonMappingExceptionMapper; @@ -56,6 +57,8 @@ import java.util.Base64; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; @@ -768,6 +771,29 @@ public void uploadBomAutoCreateTest() throws Exception { Assert.assertNotNull(project); } + @Test + public void uploadBomAutoCreateWithTagsTest() throws Exception { + initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); + String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); + List tags = Stream.of("tag1", "tag2").map(name -> { + Tag tag = new Tag(); + tag.setName(name); + return tag; + }).collect(Collectors.toList()); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", tags, true, bomString); + Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(request, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertNotNull(json.getString("token")); + Assert.assertTrue(UuidUtil.isValidUUID(json.getString("token"))); + Project project = qm.getProject("Acme Example", "1.0"); + Assert.assertNotNull(project); + Assert.assertEquals(tags, project.getTags()); + } + @Test public void uploadBomUnauthorizedTest() throws Exception { String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); From 22e3207f19524c4f63286ed8cb8a754ef75c7188 Mon Sep 17 00:00:00 2001 From: JCHacking Date: Sat, 15 Jun 2024 12:15:59 +0200 Subject: [PATCH 3/4] docs: add description of projectTags Refs: 1674 Signed-off-by: JCHacking --- .../org/dependencytrack/resources/v1/vo/BomSubmitRequest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java b/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java index 9d5c07e146..99129c7a81 100644 --- a/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java +++ b/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java @@ -117,6 +117,7 @@ public String getProjectVersion() { return projectVersion; } + @Schema(description = "Overwrite project tags") public List getProjectTags() { return projectTags; } From df322b4b7de81fe388378d94b00c8936194d3ecc Mon Sep 17 00:00:00 2001 From: JCHacking Date: Tue, 18 Jun 2024 17:33:43 +0200 Subject: [PATCH 4/4] tests: fix test Refs: 1674 Signed-off-by: JCHacking --- .../org/dependencytrack/resources/v1/BomResourceTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index c816b2edb7..c99eb4c63b 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -791,7 +791,9 @@ public void uploadBomAutoCreateWithTagsTest() throws Exception { Assert.assertTrue(UuidUtil.isValidUUID(json.getString("token"))); Project project = qm.getProject("Acme Example", "1.0"); Assert.assertNotNull(project); - Assert.assertEquals(tags, project.getTags()); + assertThat(project.getTags()) + .extracting(Tag::getName) + .containsExactlyInAnyOrder("tag1", "tag2"); } @Test