Skip to content

Commit

Permalink
Fix /api/v1/event/{token} not checking workflow states (#915)
Browse files Browse the repository at this point in the history
  • Loading branch information
nscuro committed Sep 19, 2024
1 parent 8c5ab17 commit 37351e4
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,16 @@ HAVING NOW() - MAX("UPDATED_AT") > :retentionDuration
""")
int deleteAllForRetention(@Bind Duration retentionDuration);

/**
* @since 5.6.0
*/
@SqlQuery("""
SELECT EXISTS(
SELECT 1
FROM "WORKFLOW_STATE"
WHERE "TOKEN" = :token
AND "STATUS" IN ('PENDING', 'TIMED_OUT'))
""")
boolean existsWithNonTerminalStatus(@Bind UUID token);

}
18 changes: 15 additions & 3 deletions src/main/java/org/dependencytrack/resources/v1/EventResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.dependencytrack.model.validation.ValidUuid;
import org.dependencytrack.persistence.jdbi.WorkflowDao;
import org.dependencytrack.resources.v1.vo.IsTokenBeingProcessedResponse;

import java.util.UUID;

import static org.dependencytrack.persistence.jdbi.JdbiFactory.withJdbiHandle;

/**
* JAX-RS resources for processing Events
*
Expand Down Expand Up @@ -82,9 +85,18 @@ public class EventResource extends AlpineResource {
public Response isTokenBeingProcessed(
@Parameter(description = "The UUID of the token to query", schema = @Schema(type = "string", format = "uuid"), required = true)
@PathParam("uuid") @ValidUuid String uuid) {
final boolean value = Event.isEventBeingProcessed(UUID.fromString(uuid));
IsTokenBeingProcessedResponse response = new IsTokenBeingProcessedResponse();
response.setProcessing(value);
final UUID token = UUID.fromString(uuid);

final boolean isProcessing;
if (Event.isEventBeingProcessed(token)) {
isProcessing = true;
} else {
isProcessing = withJdbiHandle(getAlpineRequest(), handle ->
handle.attach(WorkflowDao.class).existsWithNonTerminalStatus(token));
}

final var response = new IsTokenBeingProcessedResponse();
response.setProcessing(isProcessing);
return Response.ok(response).build();
}
}
1 change: 1 addition & 0 deletions src/test/java/org/dependencytrack/ResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public abstract class ResourceTest {
protected final String V1_CONFIG_PROPERTY = "/v1/configProperty";
protected final String V1_CWE = "/v1/cwe";
protected final String V1_DEPENDENCY = "/v1/dependency";
protected final String V1_EVENT = "/v1/event";
protected final String V1_FINDING = "/v1/finding";
protected final String V1_LDAP = "/v1/ldap";
protected final String V1_LICENSE = "/v1/license";
Expand Down
125 changes: 125 additions & 0 deletions src/test/java/org/dependencytrack/resources/v1/EventResourceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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 alpine.server.filters.ApiFilter;
import alpine.server.filters.AuthenticationFilter;
import jakarta.ws.rs.core.Response;
import org.apache.http.HttpStatus;
import org.dependencytrack.JerseyTestRule;
import org.dependencytrack.ResourceTest;
import org.dependencytrack.model.WorkflowState;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.ClassRule;
import org.junit.Test;

import java.util.Date;
import java.util.UUID;

import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.assertj.core.api.Assertions.assertThat;
import static org.dependencytrack.model.WorkflowStatus.COMPLETED;
import static org.dependencytrack.model.WorkflowStatus.FAILED;
import static org.dependencytrack.model.WorkflowStatus.PENDING;
import static org.dependencytrack.model.WorkflowStep.BOM_CONSUMPTION;
import static org.dependencytrack.model.WorkflowStep.BOM_PROCESSING;

public class EventResourceTest extends ResourceTest {

@ClassRule
public static JerseyTestRule jersey = new JerseyTestRule(
new ResourceConfig(EventResource.class)
.register(ApiFilter.class)
.register(AuthenticationFilter.class));

@Test
public void isTokenBeingProcessedTrueTest() {
final UUID uuid = UUID.randomUUID();
final WorkflowState workflowState1 = new WorkflowState();
workflowState1.setParent(null);
workflowState1.setStep(BOM_CONSUMPTION);
workflowState1.setStatus(COMPLETED);
workflowState1.setToken(uuid);
workflowState1.setUpdatedAt(new Date());
var workflowState1Persisted = qm.persist(workflowState1);
final WorkflowState workflowState2 = new WorkflowState();
workflowState2.setParent(workflowState1Persisted);
workflowState2.setStep(BOM_PROCESSING);
workflowState2.setStatus(PENDING);
workflowState2.setToken(uuid);
workflowState2.setUpdatedAt(new Date());
qm.persist(workflowState2);

final Response response = jersey.target(V1_EVENT + "/token/" + uuid).request()
.header(X_API_KEY, apiKey)
.get(Response.class);
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
final String jsonResponse = getPlainTextBody(response);
assertThatJson(jsonResponse).isEqualTo("""
{
"processing": true
}
""");
}

@Test
public void isTokenBeingProcessedFalseTest() {
final UUID uuid = UUID.randomUUID();
final WorkflowState workflowState1 = new WorkflowState();
workflowState1.setParent(null);
workflowState1.setStep(BOM_CONSUMPTION);
workflowState1.setStatus(COMPLETED);
workflowState1.setToken(uuid);
workflowState1.setUpdatedAt(new Date());
var workflowState1Persisted = qm.persist(workflowState1);
final WorkflowState workflowState2 = new WorkflowState();
workflowState2.setParent(workflowState1Persisted);
workflowState2.setStep(BOM_PROCESSING);
workflowState2.setStatus(FAILED);
workflowState2.setToken(uuid);
workflowState2.setUpdatedAt(new Date());
qm.persist(workflowState2);

final Response response = jersey.target(V1_EVENT + "/token/" + uuid).request()
.header(X_API_KEY, apiKey)
.get(Response.class);
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
final String jsonResponse = getPlainTextBody(response);
assertThatJson(jsonResponse).isEqualTo("""
{
"processing": false
}
""");
}

@Test
public void isTokenBeingProcessedNotExistsTest() {
final Response response = jersey.target(V1_EVENT + "/token/" + UUID.randomUUID()).request()
.header(X_API_KEY, apiKey)
.get(Response.class);
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
final String jsonResponse = getPlainTextBody(response);
assertThatJson(jsonResponse).isEqualTo("""
{
"processing": false
}
""");
}

}

0 comments on commit 37351e4

Please sign in to comment.