Skip to content

Commit

Permalink
Merge pull request #3843 from JCHacking/autocreate_project_tags
Browse files Browse the repository at this point in the history
feat: autocreate project with tags

Closes #1674
  • Loading branch information
nscuro authored Jun 18, 2024
2 parents 343c1d9 + df322b4 commit 66e9ae3
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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<Tag> 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;

Expand All @@ -71,15 +75,17 @@ public final class BomSubmitRequest {
public BomSubmitRequest(String project,
String projectName,
String projectVersion,
List<Tag> 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<Tag> projectTags,
@JsonProperty(value = "autoCreate") boolean autoCreate,
@JsonProperty(value = "parentUUID") String parentUUID,
@JsonProperty(value = "parentName") String parentName,
Expand All @@ -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;
Expand All @@ -110,6 +117,11 @@ public String getProjectVersion() {
return projectVersion;
}

@Schema(description = "Overwrite project tags")
public List<Tag> getProjectTags() {
return projectTags;
}

@Schema(example = "5341f53c-611b-4388-9d9c-731026dc5eec")
public String getParentUUID() {
return parentUUID;
Expand Down
48 changes: 38 additions & 10 deletions src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -726,7 +729,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));
Expand All @@ -741,7 +744,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));
Expand All @@ -755,7 +758,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));
Expand All @@ -768,10 +771,35 @@ 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<Tag> 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);
assertThat(project.getTags())
.extracting(Tag::getName)
.containsExactlyInAnyOrder("tag1", "tag2");
}

@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));
Expand All @@ -785,7 +813,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));
Expand All @@ -797,7 +825,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));
Expand All @@ -813,7 +841,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));
Expand All @@ -828,7 +856,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));
Expand All @@ -847,15 +875,15 @@ 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));
Assert.assertEquals(404, response.getStatus(), 0);
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));
Expand Down

0 comments on commit 66e9ae3

Please sign in to comment.