Skip to content

Commit

Permalink
Merge pull request #3960 from nscuro/fix-bomupload-tags-multipart
Browse files Browse the repository at this point in the history
  • Loading branch information
nscuro authored Jul 9, 2024
2 parents 0e6c95e + fcd3914 commit db8b360
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 11 deletions.
7 changes: 7 additions & 0 deletions src/main/java/org/dependencytrack/model/Tag.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ public class Tag implements Serializable {
@Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC"))
private List<Project> projects;

public Tag() {
}

public Tag(final String name) {
this.name = name;
}

public long getId() {
return id;
}
Expand Down
31 changes: 20 additions & 11 deletions src/main/java/org/dependencytrack/resources/v1/BomResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
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 org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -45,6 +44,7 @@
import org.dependencytrack.model.Component;
import org.dependencytrack.model.ConfigPropertyConstants;
import org.dependencytrack.model.Project;
import org.dependencytrack.model.Tag;
import org.dependencytrack.model.validation.ValidUuid;
import org.dependencytrack.notification.NotificationConstants.Title;
import org.dependencytrack.notification.NotificationGroup;
Expand Down Expand Up @@ -80,19 +80,22 @@
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import static java.util.function.Predicate.not;

/**
* JAX-RS resources for processing bill-of-material (bom) documents.
*
* @author Steve Springett
* @since 3.0.0
*/
@Path("/v1/bom")
@Tag(name = "bom")
@io.swagger.v3.oas.annotations.tags.Tag(name = "bom")
@SecurityRequirements({
@SecurityRequirement(name = "ApiKeyAuth"),
@SecurityRequirement(name = "BearerAuth")
Expand Down Expand Up @@ -367,14 +370,17 @@ public Response uploadBom(@Parameter(required = true) BomSubmitRequest request)
@ApiResponse(responseCode = "404", description = "The project could not be found")
})
@PermissionRequired(Permissions.Constants.BOM_UPLOAD)
public Response uploadBom(@FormDataParam("project") String projectUuid,
@DefaultValue("false") @FormDataParam("autoCreate") boolean autoCreate,
@FormDataParam("projectName") String projectName,
@FormDataParam("projectVersion") String projectVersion,
@FormDataParam("parentName") String parentName,
@FormDataParam("parentVersion") String parentVersion,
@FormDataParam("parentUUID") String parentUUID,
@Parameter(schema = @Schema(type = "string")) @FormDataParam("bom") final List<FormDataBodyPart> artifactParts) {
public Response uploadBom(
@FormDataParam("project") String projectUuid,
@DefaultValue("false") @FormDataParam("autoCreate") boolean autoCreate,
@FormDataParam("projectName") String projectName,
@FormDataParam("projectVersion") String projectVersion,
@FormDataParam("projectTags") String projectTags,
@FormDataParam("parentName") String parentName,
@FormDataParam("parentVersion") String parentVersion,
@FormDataParam("parentUUID") String parentUUID,
@Parameter(schema = @Schema(type = "string")) @FormDataParam("bom") final List<FormDataBodyPart> artifactParts
) {
if (projectUuid != null) { // behavior in v3.0.0
try (QueryManager qm = new QueryManager()) {
final Project project = qm.getObjectByUuid(Project.class, projectUuid);
Expand Down Expand Up @@ -404,7 +410,10 @@ public Response uploadBom(@FormDataParam("project") String projectUuid,
return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified parent project is forbidden").build();
}
}
project = qm.createProject(trimmedProjectName, null, trimmedProjectVersion, null, parent, null, true, true);
final List<org.dependencytrack.model.Tag> tags = (projectTags != null && !projectTags.isBlank())
? Arrays.stream(projectTags.split(",")).map(String::trim).filter(not(String::isEmpty)).map(Tag::new).toList()
: null;
project = qm.createProject(trimmedProjectName, null, trimmedProjectVersion, tags, 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 @@ -44,16 +44,21 @@
import org.dependencytrack.resources.v1.exception.JsonMappingExceptionMapper;
import org.dependencytrack.resources.v1.vo.BomSubmitRequest;
import org.dependencytrack.tasks.scanners.AnalyzerIdentity;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;

import jakarta.json.JsonObject;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
Expand All @@ -63,6 +68,7 @@
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
import static org.apache.commons.io.IOUtils.resourceToByteArray;
import static org.apache.commons.io.IOUtils.resourceToString;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_ENABLED;
Expand Down Expand Up @@ -871,6 +877,39 @@ public void uploadBomAutoCreateWithTagsTest() throws Exception {
.containsExactlyInAnyOrder("tag1", "tag2");
}

@Test
public void uploadBomAutoCreateWithTagsMultipartTest() throws Exception {
initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD);

final var multiPart = new FormDataMultiPart()
.field("bom", resourceToString("/unit/bom-1.xml", StandardCharsets.UTF_8), MediaType.APPLICATION_XML_TYPE)
.field("projectName", "Acme Example")
.field("projectVersion", "1.0")
.field("projectTags", "tag1,tag2")
.field("autoCreate", "true");

// NB: The GrizzlyConnectorProvider doesn't work with MultiPart requests.
// https://github.com/eclipse-ee4j/jersey/issues/5094
final var client = ClientBuilder.newClient(new ClientConfig()
.connectorProvider(new HttpUrlConnectorProvider()));

final Response response = client.target(jersey.target(V1_BOM).getUri()).request()
.header(X_API_KEY, apiKey)
.post(Entity.entity(multiPart, multiPart.getMediaType()));
assertThat(response.getStatus()).isEqualTo(200);
assertThatJson(getPlainTextBody(response)).isEqualTo("""
{
"token": "${json-unit.any-string}"
}
""");

final Project project = qm.getProject("Acme Example", "1.0");
assertThat(project).isNotNull();
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"));
Expand Down

0 comments on commit db8b360

Please sign in to comment.