Skip to content

Commit

Permalink
Add option to deactivate old project versions on BOM upload
Browse files Browse the repository at this point in the history
Fixes DependencyTrack#4532

Signed-off-by: Christoffer Rumohr <[email protected]>
  • Loading branch information
crumohr committed Jan 7, 2025
1 parent bcd2651 commit dfa6e6f
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 15 deletions.
39 changes: 39 additions & 0 deletions src/main/java/org/dependencytrack/resources/v1/BomResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,14 @@ public Response uploadBom(@Parameter(required = true) BomSubmitRequest request)
null, true, request.isLatestProjectVersion(), true);
Principal principal = getPrincipal();
qm.updateNewProjectACL(project, principal);
if (request.isDeactivateOtherVersions()) {
if (!request.isLatestProjectVersion()) {
var message = "Value \"isLatest=true\" required when \"deactivateOtherVersions=true\".";
LOGGER.error(message);
return Response.status(Response.Status.NOT_ACCEPTABLE).entity(message).build();
}
qm.runInTransaction(() -> deactivateOtherVersions(qm, trimmedProjectName));
}
} else {
return Response.status(Response.Status.UNAUTHORIZED).entity("The principal does not have permission to create project.").build();
}
Expand Down Expand Up @@ -404,6 +412,7 @@ public Response uploadBom(
@FormDataParam("parentVersion") String parentVersion,
@FormDataParam("parentUUID") String parentUUID,
@DefaultValue("false") @FormDataParam("isLatest") boolean isLatest,
@DefaultValue("false") @FormDataParam("deactivateOtherVersions") boolean deactivateOtherVersions,
@Parameter(schema = @Schema(type = "string")) @FormDataParam("bom") final List<FormDataBodyPart> artifactParts
) {
if (projectUuid != null) { // behavior in v3.0.0
Expand Down Expand Up @@ -450,6 +459,15 @@ public Response uploadBom(
project = qm.createProject(trimmedProjectName, null, trimmedProjectVersion, tags, parent, null, true, isLatest, true);
Principal principal = getPrincipal();
qm.updateNewProjectACL(project, principal);

if (deactivateOtherVersions) {
if (!isLatest) {
var message = "Value \"isLatest=true\" required when \"deactivateOtherVersions=true\".";
LOGGER.error(message);
return Response.status(Response.Status.NOT_ACCEPTABLE).entity(message).build();
}
qm.runInTransaction(() -> deactivateOtherVersions(qm, trimmedProjectName));
}
} else {
return Response.status(Response.Status.UNAUTHORIZED).entity("The principal does not have permission to create project.").build();
}
Expand All @@ -459,6 +477,7 @@ public Response uploadBom(
}
}


@GET
@Path("/token/{uuid}")
@Produces(MediaType.APPLICATION_JSON)
Expand Down Expand Up @@ -501,6 +520,26 @@ public Response isTokenBeingProcessed (
return Response.ok(response).build();
}

/**
* Deactivates all non-latest versions of a specific project.
* Ensures that only the latest version of a project remains active.
* If the principal does not have access to a project version, an exception is thrown.
*
* @param qm The QueryManager instance used to query and manage projects.
* @param projectName A string representing the name of the project versions to be evaluated.
*/
private void deactivateOtherVersions(QueryManager qm, String projectName) {
qm.getProjects(projectName, true, false, null).getList(Project.class).forEach(p -> {
if (p.isLatest()) {
return;
} else if (!qm.hasAccess(super.getPrincipal(), p)) {
throw new WebApplicationException("Could not deactivate project, no access: " + p.getUuid() + " / " + p.getName());
}
p.setActive(false);
qm.updateProject(p, true);
});
}

/**
* Common logic that processes a BOM given a project and encoded payload.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ public final class BomSubmitRequest {

private final boolean isLatestProjectVersion;

private final boolean deactivateOtherVersions;

public BomSubmitRequest(String project,
String projectName,
String projectVersion,
List<Tag> projectTags,
boolean autoCreate,
boolean isLatestProjectVersion,
boolean deactivateOtherProjectVersions,
String bom) {
this(project, projectName, projectVersion, projectTags, autoCreate, null, null, null, isLatestProjectVersion, bom);
this(project, projectName, projectVersion, projectTags, autoCreate, null, null, null, isLatestProjectVersion, deactivateOtherProjectVersions, bom);
}

@JsonCreator
Expand All @@ -94,6 +97,7 @@ public BomSubmitRequest(@JsonProperty(value = "project") String project,
@JsonProperty(value = "parentName") String parentName,
@JsonProperty(value = "parentVersion") String parentVersion,
@JsonProperty(value = "isLatestProjectVersion", defaultValue = "false") boolean isLatestProjectVersion,
@JsonProperty(value = "deactivateOtherVersions", defaultValue = "false") boolean deactivateOtherVersions,
@JsonProperty(value = "bom", required = true) String bom) {
this.project = project;
this.projectName = projectName;
Expand All @@ -104,6 +108,7 @@ public BomSubmitRequest(@JsonProperty(value = "project") String project,
this.parentName = parentName;
this.parentVersion = parentVersion;
this.isLatestProjectVersion = isLatestProjectVersion;
this.deactivateOtherVersions = deactivateOtherVersions;
this.bom = bom;
}

Expand Down Expand Up @@ -149,6 +154,10 @@ public boolean isAutoCreate() {
@JsonProperty("isLatestProjectVersion")
public boolean isLatestProjectVersion() { return isLatestProjectVersion; }

public boolean isDeactivateOtherVersions() {
return deactivateOtherVersions;
}

@Schema(
description = "Base64 encoded BOM",
required = true,
Expand Down
Loading

0 comments on commit dfa6e6f

Please sign in to comment.