diff --git a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java index 098644040..0c14cb2b1 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java @@ -49,6 +49,8 @@ import javax.ws.rs.core.Response; import java.util.List; +import static org.datanucleus.PropertyNames.PROPERTY_RETAIN_VALUES; + /** * JAX-RS resources for processing teams. * @@ -243,6 +245,35 @@ public Response regenerateApiKey( } } + @POST + @Path("/key/{apikey}/comment") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Updates an API key's comment", response = ApiKey.class) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The API key could not be found") + }) + @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) + public Response updateApiKeyComment(@PathParam("apikey") final String apikey, + final String comment) { + try (final var qm = new QueryManager()) { + qm.getPersistenceManager().setProperty(PROPERTY_RETAIN_VALUES, "true"); + + return qm.runInTransaction(() -> { + final ApiKey apiKey = qm.getApiKey(apikey); + if (apiKey == null) { + return Response + .status(Response.Status.NOT_FOUND) + .entity("The API key could not be found.") + .build(); + } + apiKey.setComment(comment); + return Response.ok(apiKey).build(); + }); + } + } + @DELETE @Path("/key/{apikey}") @ApiOperation( diff --git a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java index d944acf50..b2dba1852 100644 --- a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java @@ -19,6 +19,7 @@ package org.dependencytrack.resources.v1; import alpine.common.util.UuidUtil; +import alpine.model.ApiKey; import alpine.model.ConfigProperty; import alpine.model.ManagedUser; import alpine.model.Team; @@ -43,6 +44,10 @@ import javax.ws.rs.core.Response; import java.util.UUID; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.hamcrest.CoreMatchers.equalTo; + public class TeamResourceTest extends ResourceTest { @ClassRule @@ -270,4 +275,42 @@ public void deleteApiKeyInvalidTest() { String body = getPlainTextBody(response); Assert.assertEquals("The API key could not be found.", body); } + + @Test + public void updateApiKeyCommentTest() { + final Team team = qm.createTeam("foo", true); + final ApiKey apiKey = team.getApiKeys().get(0); + + assertThat(apiKey.getCreated()).isNotNull(); + assertThat(apiKey.getLastUsed()).isNull(); + assertThat(apiKey.getComment()).isNull(); + + final Response response = jersey.target("%s/key/%s/comment".formatted(V1_TEAM, apiKey.getKey())).request() + .header(X_API_KEY, this.apiKey) + .post(Entity.entity("Some comment 123", MediaType.TEXT_PLAIN)); + + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)) + .withMatcher("key", equalTo(apiKey.getKey())) + .withMatcher("maskedKey", equalTo(apiKey.getMaskedKey())) + .isEqualTo(""" + { + "key": "${json-unit.matches:key}", + "maskedKey": "${json-unit.matches:maskedKey}", + "created": "${json-unit.any-number}", + "lastUsed": null, + "comment": "Some comment 123" + } + """); + } + + @Test + public void updateApiKeyCommentNotFoundTest() { + final Response response = jersey.target("%s/key/does-not-exist/comment".formatted(V1_TEAM)).request() + .header(X_API_KEY, this.apiKey) + .post(Entity.entity("Some comment 123", MediaType.TEXT_PLAIN)); + + assertThat(response.getStatus()).isEqualTo(404); + assertThat(getPlainTextBody(response)).isEqualTo("The API key could not be found."); + } }