resultRows = rows.size() > 1
- ? rows.subList(0, Math.min(rows.size(), limit))
- : rows;
-
- final ListVulnerabilityMetricsPageToken nextPageToken = rows.size() > limit
- ? new ListVulnerabilityMetricsPageToken(resultRows.getLast().year, resultRows.getLast().month)
- : null;
-
- return new Page<>(resultRows, pageTokenEncoder.encode(nextPageToken));
- }
-
/**
* Compute the portfolio metrics for the projects accessible by the calling principal.
*
diff --git a/apiserver/src/main/java/org/dependencytrack/persistence/jdbi/TeamDao.java b/apiserver/src/main/java/org/dependencytrack/persistence/jdbi/TeamDao.java
deleted file mode 100644
index ba9fa6f204..0000000000
--- a/apiserver/src/main/java/org/dependencytrack/persistence/jdbi/TeamDao.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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.persistence.jdbi;
-
-import org.dependencytrack.common.pagination.Page;
-import org.dependencytrack.common.pagination.Page.TotalCount;
-import org.dependencytrack.common.pagination.PageToken;
-import org.dependencytrack.common.pagination.PageTokenEncoder;
-import org.jdbi.v3.core.mapper.reflect.ConstructorMapper;
-import org.jdbi.v3.core.statement.Query;
-import org.jdbi.v3.core.statement.Update;
-import org.jdbi.v3.sqlobject.customizer.Bind;
-import org.jdbi.v3.sqlobject.statement.SqlUpdate;
-import org.jspecify.annotations.NullMarked;
-import org.jspecify.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-
-@NullMarked
-public interface TeamDao extends PaginationSupport {
-
- record ListTeamsPageToken(String lastName, TotalCount totalCount) implements PageToken {
- }
-
- record ListTeamsRow(String name, int apiKeys, int members) {
- }
-
- default Page listTeams(int limit, @Nullable String pageToken) {
- final PageTokenEncoder pageTokenEncoder =
- getHandle().getConfig(PaginationConfig.class).getPageTokenEncoder();
- final var decodedPageToken = pageTokenEncoder.decode(pageToken, ListTeamsPageToken.class);
-
- TotalCount totalCount;
- String lastName = null;
-
- if (decodedPageToken != null) {
- totalCount = decodedPageToken.totalCount();
- lastName = decodedPageToken.lastName();
- } else {
- totalCount = getBoundedTotalCount("FROM \"TEAM\"", null, 500);
- }
-
- final Query query = getHandle().createQuery(/* language=InjectedFreeMarker */ """
- <#-- @ftlvariable name="lastName" type="Boolean" -->
- SELECT "NAME" AS name
- , (SELECT COUNT(*) FROM "APIKEYS_TEAMS" WHERE "TEAM_ID" = "TEAM"."ID") AS api_keys
- , (SELECT COUNT(*) FROM "USERS_TEAMS" WHERE "TEAM_ID" = "TEAM"."ID") AS members
- FROM "TEAM"
- WHERE TRUE
- <#if lastName>
- AND "NAME" > :lastName
- #if>
- ORDER BY "NAME"
- LIMIT (:limit + 1)
- """);
-
- final List rows = query
- .bind("lastName", lastName)
- .bind("limit", limit)
- .defineNamedBindings()
- .map(ConstructorMapper.of(ListTeamsRow.class))
- .list();
-
- final List resultRows = rows.size() > 1
- ? rows.subList(0, Math.min(rows.size(), limit))
- : rows;
-
- final ListTeamsPageToken nextPageToken = rows.size() > limit
- ? new ListTeamsPageToken(resultRows.getLast().name(), totalCount)
- : null;
-
- return new Page<>(resultRows, pageTokenEncoder.encode(nextPageToken), totalCount);
- }
-
- record ListTeamMembershipsPageToken(
- String lastTeamName,
- String lastUsername,
- TotalCount totalCount) implements PageToken {
- }
-
- record ListTeamMembershipsRow(String teamName, String username) {
- }
-
- default Page listTeamMembers(
- @Nullable String teamName,
- @Nullable String username,
- int limit,
- @Nullable String pageToken) {
- final var whereConditions = new ArrayList();
- final var queryParams = new HashMap();
-
- whereConditions.add("TRUE");
- if (teamName != null) {
- whereConditions.add("t.\"NAME\" = :teamName");
- queryParams.put("teamName", teamName);
- }
- if (username != null) {
- whereConditions.add("u.\"USERNAME\" = :username");
- queryParams.put("username", username);
- }
-
- final PageTokenEncoder pageTokenEncoder =
- getHandle().getConfig(PaginationConfig.class).getPageTokenEncoder();
- final var decodedPageToken = pageTokenEncoder.decode(pageToken, ListTeamMembershipsPageToken.class);
-
- TotalCount totalCount;
- String lastTeamName = null;
- String lastUsername = null;
-
- if (decodedPageToken != null) {
- totalCount = decodedPageToken.totalCount();
- lastTeamName = decodedPageToken.lastTeamName();
- lastUsername = decodedPageToken.lastUsername();
- } else {
- totalCount = getBoundedTotalCount("""
- FROM "USERS_TEAMS" AS ut
- INNER JOIN "TEAM" AS t
- ON t."ID" = ut."TEAM_ID"
- INNER JOIN "USER" AS u
- ON u."ID" = ut."USER_ID"
- WHERE %s
- """.formatted(String.join(" AND ", whereConditions)),
- queryParams,
- 500);
- }
-
- final Query query = getHandle().createQuery(/* language=InjectedFreeMarker */ """
- <#-- @ftlvariable name="teamName" type="Boolean" -->
- <#-- @ftlvariable name="username" type="Boolean" -->
- <#-- @ftlvariable name="lastTeamName" type="Boolean" -->
- <#-- @ftlvariable name="lastUsername" type="Boolean" -->
- <#-- @ftlvariable name="whereConditions" type="java.util.Collection" -->
- SELECT t."NAME" AS team_name
- , u."USERNAME" AS username
- FROM "USERS_TEAMS" AS ut
- INNER JOIN "TEAM" AS t
- ON t."ID" = ut."TEAM_ID"
- INNER JOIN "USER" AS u
- ON u."ID" = ut."USER_ID"
- WHERE ${whereConditions?join(" AND ")}
- <#if lastTeamName && lastUsername>
- AND (t."NAME", u."USERNAME") > (:lastTeamName, :lastUsername)
- #if>
- ORDER BY t."NAME", u."USERNAME"
- LIMIT (:limit + 1)
- """);
-
- final List rows = query
- .bindMap(queryParams)
- .bind("lastTeamName", lastTeamName)
- .bind("lastUsername", lastUsername)
- .bind("limit", limit)
- .define("whereConditions", whereConditions)
- .defineNamedBindings()
- .map(ConstructorMapper.of(ListTeamMembershipsRow.class))
- .list();
-
- final List resultRows = rows.size() > 1
- ? rows.subList(0, Math.min(rows.size(), limit))
- : rows;
-
- final ListTeamMembershipsPageToken nextPageToken = rows.size() > limit
- ? new ListTeamMembershipsPageToken(
- resultRows.getLast().teamName(),
- resultRows.getLast().username(),
- totalCount)
- : null;
-
- return new Page<>(resultRows, pageTokenEncoder.encode(nextPageToken), totalCount);
- }
-
- @SqlUpdate("""
- DELETE
- FROM "USERS_TEAMS"
- WHERE "TEAM_ID" = (SELECT "ID" FROM "TEAM" WHERE "NAME" = :teamName)
- AND "USER_ID" = (SELECT "ID" FROM "USER" WHERE "USERNAME" = :username)
- """)
- boolean deleteTeamMembership(@Bind String teamName, @Bind String username);
-
- default List deleteTeamsByName(Collection names) {
- final Update update = getHandle().createUpdate("""
- DELETE
- FROM "TEAM"
- WHERE "NAME" = ANY(:names)
- RETURNING "NAME"
- """);
-
- return update
- .bindArray("names", String.class, names)
- .executeAndReturnGeneratedKeys()
- .mapTo(String.class)
- .list();
- }
-
-}
diff --git a/apiserver/src/main/java/org/dependencytrack/resources/v2/AdvisoriesResource.java b/apiserver/src/main/java/org/dependencytrack/resources/v2/AdvisoriesResource.java
index 2acf4893f1..40b29828b7 100644
--- a/apiserver/src/main/java/org/dependencytrack/resources/v2/AdvisoriesResource.java
+++ b/apiserver/src/main/java/org/dependencytrack/resources/v2/AdvisoriesResource.java
@@ -27,6 +27,7 @@
import org.dependencytrack.api.v2.model.GetAdvisoryResponse;
import org.dependencytrack.api.v2.model.ListAdvisoriesResponse;
import org.dependencytrack.api.v2.model.ListAdvisoriesResponseItem;
+import org.dependencytrack.api.v2.model.UploadAdvisoryResponse;
import org.dependencytrack.auth.Permissions;
import org.dependencytrack.common.pagination.Page;
import org.dependencytrack.csaf.CsafModelConverter;
@@ -172,7 +173,16 @@ private Response processCsafDocument(String content, String fileName, QueryManag
persistentAdvisory.setVulnerabilities(persistentVulns);
}
- return Response.ok("File uploaded successfully: " + fileName).build();
+ final UploadAdvisoryResponse responseBody = UploadAdvisoryResponse.builder()
+ .id(persistentAdvisory.getId())
+ .build();
+ return Response
+ .created(getUriInfo().getBaseUriBuilder()
+ .path("/advisories")
+ .path(persistentAdvisory.getId().toString())
+ .build())
+ .entity(responseBody)
+ .build();
});
}
diff --git a/apiserver/src/main/java/org/dependencytrack/resources/v2/ComponentsResource.java b/apiserver/src/main/java/org/dependencytrack/resources/v2/ComponentsResource.java
index 9ff69fc7fc..2f007f4e63 100644
--- a/apiserver/src/main/java/org/dependencytrack/resources/v2/ComponentsResource.java
+++ b/apiserver/src/main/java/org/dependencytrack/resources/v2/ComponentsResource.java
@@ -79,7 +79,7 @@ public class ComponentsResource extends AbstractApiResource implements Component
public Response createComponent(final CreateComponentRequest request) {
final UUID projectUuid = request.getProjectUuid();
try (QueryManager qm = new QueryManager()) {
- qm.callInTransaction(() -> {
+ final Component component = qm.callInTransaction(() -> {
final Project project = qm.getObjectByUuid(Project.class, projectUuid);
if (project == null) {
throw new NotFoundException();
@@ -96,6 +96,7 @@ public Response createComponent(final CreateComponentRequest request) {
return Response
.created(uriInfo.getBaseUriBuilder()
.path("/components")
+ .path(component.getUuid().toString())
.build())
.build();
} catch (RuntimeException e) {
diff --git a/apiserver/src/main/java/org/dependencytrack/resources/v2/CsafResource.java b/apiserver/src/main/java/org/dependencytrack/resources/v2/CsafResource.java
index 511018a8bc..c8a6b128c7 100644
--- a/apiserver/src/main/java/org/dependencytrack/resources/v2/CsafResource.java
+++ b/apiserver/src/main/java/org/dependencytrack/resources/v2/CsafResource.java
@@ -234,7 +234,13 @@ public Response triggerCsafProviderDiscovery(UUID id) {
SecurityMarkers.SECURITY_AUDIT,
"Triggered provider discovery for CSAF aggregator '{}'",
aggregator.getNamespace());
- return Response.accepted().build();
+ return Response
+ .accepted()
+ .location(getUriInfo().getBaseUriBuilder()
+ .path("/csaf-aggregators")
+ .path(id.toString())
+ .build())
+ .build();
}
@Override
diff --git a/apiserver/src/main/java/org/dependencytrack/resources/v2/MetricsResource.java b/apiserver/src/main/java/org/dependencytrack/resources/v2/MetricsResource.java
deleted file mode 100644
index d233b0ae5c..0000000000
--- a/apiserver/src/main/java/org/dependencytrack/resources/v2/MetricsResource.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.v2;
-
-import alpine.server.auth.PermissionRequired;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.ext.Provider;
-import org.dependencytrack.api.v2.MetricsApi;
-import org.dependencytrack.api.v2.model.ListVulnerabilityMetricsResponse;
-import org.dependencytrack.api.v2.model.ListVulnerabilityMetricsResponseItem;
-import org.dependencytrack.api.v2.model.PortfolioMetricsResponse;
-import org.dependencytrack.auth.Permissions;
-import org.dependencytrack.common.pagination.Page;
-import org.dependencytrack.model.PortfolioMetrics;
-import org.dependencytrack.persistence.jdbi.MetricsDao;
-import org.dependencytrack.resources.AbstractApiResource;
-
-import static org.dependencytrack.persistence.jdbi.JdbiFactory.inJdbiTransaction;
-import static org.dependencytrack.persistence.jdbi.JdbiFactory.withJdbiHandle;
-
-@Provider
-public class MetricsResource extends AbstractApiResource implements MetricsApi {
-
- @Override
- @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO)
- public Response getPortfolioCurrentMetrics() {
- PortfolioMetrics metrics = withJdbiHandle(
- getAlpineRequest(),
- handle -> handle.attach(MetricsDao.class).getMostRecentPortfolioMetrics());
- final var response = PortfolioMetricsResponse.builder()
- .components(metrics.getComponents())
- .critical(metrics.getCritical())
- .findingsAudited(metrics.getFindingsAudited())
- .findingsTotal(metrics.getFindingsTotal())
- .findingsUnaudited(metrics.getFindingsUnaudited())
- .high(metrics.getHigh())
- .inheritedRiskScore(metrics.getInheritedRiskScore())
- .observedAt(metrics.getLastOccurrence().getTime())
- .low(metrics.getLow())
- .medium(metrics.getMedium())
- .policyViolationsAudited(metrics.getPolicyViolationsAudited())
- .policyViolationsFail(metrics.getPolicyViolationsFail())
- .policyViolationsInfo(metrics.getPolicyViolationsInfo())
- .policyViolationsLicenseAudited(metrics.getPolicyViolationsLicenseAudited())
- .policyViolationsLicenseTotal(metrics.getPolicyViolationsLicenseTotal())
- .policyViolationsLicenseUnaudited(metrics.getPolicyViolationsLicenseUnaudited())
- .policyViolationsOperationalAudited(metrics.getPolicyViolationsOperationalAudited())
- .policyViolationsOperationalTotal(metrics.getPolicyViolationsOperationalTotal())
- .policyViolationsOperationalUnaudited(metrics.getPolicyViolationsOperationalUnaudited())
- .policyViolationsSecurityAudited(metrics.getPolicyViolationsSecurityAudited())
- .policyViolationsSecurityTotal(metrics.getPolicyViolationsSecurityTotal())
- .policyViolationsSecurityUnaudited(metrics.getPolicyViolationsSecurityUnaudited())
- .policyViolationsTotal(metrics.getPolicyViolationsTotal())
- .policyViolationsUnaudited(metrics.getPolicyViolationsUnaudited())
- .policyViolationsWarn(metrics.getPolicyViolationsWarn())
- .projects(metrics.getProjects())
- .suppressed(metrics.getSuppressed())
- .unassigned(metrics.getUnassigned())
- .vulnerabilities(metrics.getVulnerabilities())
- .vulnerableComponents(metrics.getVulnerableComponents())
- .vulnerableProjects(metrics.getVulnerableProjects())
- .build();
- return Response.ok(response).build();
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO)
- public Response getVulnerabilityMetrics(Integer limit, String pageToken) {
- final Page metricsPage = inJdbiTransaction(
- getAlpineRequest(),
- handle -> handle.attach(MetricsDao.class).getVulnerabilityMetrics(limit, pageToken));
-
- final var response = ListVulnerabilityMetricsResponse.builder()
- .items(metricsPage.items().stream()
- .map(
- metricRow -> ListVulnerabilityMetricsResponseItem.builder()
- .year(metricRow.year())
- .month(metricRow.month())
- .count(metricRow.count())
- .observedAt(metricRow.measuredAt().getEpochSecond())
- .build())
- .toList())
- .nextPageToken(metricsPage.nextPageToken())
- .total(convertTotalCount(metricsPage.totalCount()))
- .build();
-
- return Response.ok(response).build();
- }
-}
diff --git a/apiserver/src/main/java/org/dependencytrack/resources/v2/SecretsResource.java b/apiserver/src/main/java/org/dependencytrack/resources/v2/SecretsResource.java
index a41bcb0f73..83bc991873 100644
--- a/apiserver/src/main/java/org/dependencytrack/resources/v2/SecretsResource.java
+++ b/apiserver/src/main/java/org/dependencytrack/resources/v2/SecretsResource.java
@@ -66,7 +66,12 @@ public Response createSecret(final CreateSecretRequest request) {
}
LOGGER.info(SecurityMarkers.SECURITY_AUDIT, "Created secret: {}", request.getName());
- return Response.noContent().build();
+ return Response
+ .created(getUriInfo().getBaseUriBuilder()
+ .path("/secrets")
+ .path(request.getName())
+ .build())
+ .build();
}
@Override
diff --git a/apiserver/src/main/java/org/dependencytrack/resources/v2/TeamsResource.java b/apiserver/src/main/java/org/dependencytrack/resources/v2/TeamsResource.java
deleted file mode 100644
index 45b16514f8..0000000000
--- a/apiserver/src/main/java/org/dependencytrack/resources/v2/TeamsResource.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * 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.v2;
-
-import alpine.model.Permission;
-import alpine.model.Team;
-import alpine.model.User;
-import alpine.server.auth.PermissionRequired;
-import jakarta.ws.rs.NotFoundException;
-import jakarta.ws.rs.core.Context;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.UriInfo;
-import jakarta.ws.rs.ext.Provider;
-import org.dependencytrack.api.v2.TeamsApi;
-import org.dependencytrack.api.v2.model.CreateTeamMembershipRequest;
-import org.dependencytrack.api.v2.model.CreateTeamRequest;
-import org.dependencytrack.api.v2.model.GetTeamResponse;
-import org.dependencytrack.api.v2.model.ListTeamMembershipsResponse;
-import org.dependencytrack.api.v2.model.ListTeamMembershipsResponseItem;
-import org.dependencytrack.api.v2.model.ListTeamsResponse;
-import org.dependencytrack.api.v2.model.ListTeamsResponseItem;
-import org.dependencytrack.auth.Permissions;
-import org.dependencytrack.common.pagination.Page;
-import org.dependencytrack.exception.AlreadyExistsException;
-import org.dependencytrack.persistence.QueryManager;
-import org.dependencytrack.persistence.jdbi.TeamDao;
-import org.dependencytrack.persistence.jdbi.TeamDao.ListTeamMembershipsRow;
-import org.dependencytrack.persistence.jdbi.TeamDao.ListTeamsRow;
-import org.dependencytrack.resources.AbstractApiResource;
-import org.owasp.security.logging.SecurityMarkers;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
-import static org.dependencytrack.persistence.jdbi.JdbiFactory.inJdbiTransaction;
-import static org.dependencytrack.util.PersistenceUtil.isUniqueConstraintViolation;
-
-@Provider
-public class TeamsResource extends AbstractApiResource implements TeamsApi {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(TeamsResource.class);
-
- @Context
- private UriInfo uriInfo;
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response listTeams(final Integer limit, final String pageToken) {
- final Page teamsPage = inJdbiTransaction(
- handle -> handle.attach(TeamDao.class).listTeams(limit, pageToken));
-
- final var response = ListTeamsResponse.builder()
- .items(teamsPage.items().stream()
- .map(
- teamRow -> ListTeamsResponseItem.builder()
- .name(teamRow.name())
- .apiKeys(teamRow.apiKeys())
- .members(teamRow.members())
- .build())
- .toList())
- .nextPageToken(teamsPage.nextPageToken())
- .total(convertTotalCount(teamsPage.totalCount()))
- .build();
-
- return Response.ok(response).build();
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response getTeam(final String name) {
- try (final var qm = new QueryManager()) {
- final Team team = qm.getTeam(name);
- if (team == null) {
- throw new NotFoundException();
- }
-
- final var response = GetTeamResponse.builder()
- .name(name)
- .permissions(
- team.getPermissions().stream()
- .map(Permission::getName)
- .sorted()
- .toList())
- .build();
-
- return Response.ok(response).build();
- }
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response createTeam(final CreateTeamRequest request) {
- try (final var qm = new QueryManager()) {
- qm.runInTransaction(() -> {
- final List permissions =
- qm.getPermissionsByName(request.getPermissions());
-
- final var team = new Team();
- team.setName(request.getName());
- team.setPermissions(permissions);
- qm.persist(team);
- });
- } catch (RuntimeException e) {
- if (isUniqueConstraintViolation(e)) {
- throw new AlreadyExistsException("Team already exists", e);
- }
-
- throw e;
- }
-
- LOGGER.info(
- SecurityMarkers.SECURITY_AUDIT,
- "Team created: {}", request.getName());
- return Response
- .created(uriInfo.getBaseUriBuilder()
- .path("/teams")
- .path(request.getName())
- .build())
- .build();
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response deleteTeam(final String name) {
- final List deletedTeamNames = inJdbiTransaction(
- handle -> handle.attach(TeamDao.class).deleteTeamsByName(List.of(name)));
- if (deletedTeamNames.isEmpty()) {
- throw new NotFoundException();
- }
-
- LOGGER.info(SecurityMarkers.SECURITY_AUDIT, "Team deleted: {}", name);
- return Response.noContent().build();
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response listTeamMemberships(final String team, final String user, final Integer limit, final String pageToken) {
- final Page membershipsPage = inJdbiTransaction(
- handle -> handle.attach(TeamDao.class).listTeamMembers(team, user, limit, pageToken));
-
- final var response = ListTeamMembershipsResponse.builder()
- .items(membershipsPage.items().stream()
- .map(
- membershipRow -> ListTeamMembershipsResponseItem.builder()
- .teamName(membershipRow.teamName())
- .username(membershipRow.username())
- .build())
- .toList())
- .nextPageToken(membershipsPage.nextPageToken())
- .total(convertTotalCount(membershipsPage.totalCount()))
- .build();
-
- return Response.ok(response).build();
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response createTeamMembership(final CreateTeamMembershipRequest request) {
- try (final var qm = new QueryManager()) {
- qm.runInTransaction(() -> {
- final Team team = qm.getTeam(request.getTeamName());
- if (team == null) {
- throw new NotFoundException();
- }
-
- final User user = qm.getUser(request.getUsername());
- if (user == null) {
- throw new NotFoundException();
- }
-
- team.getUsers().add(user);
- user.getTeams().add(team);
- });
- } catch (RuntimeException e) {
- if (isUniqueConstraintViolation(e)) {
- throw new AlreadyExistsException("Team membership already exists", e);
- }
-
- throw e;
- }
-
- LOGGER.info(
- SecurityMarkers.SECURITY_AUDIT,
- "Team membership created: team={}, user={}",
- request.getTeamName(),
- request.getUsername());
- return Response.created(null).build();
- }
-
- @Override
- @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT)
- public Response deleteTeamMembership(final String team, final String user) {
- final boolean deleted = inJdbiTransaction(
- handle -> handle.attach(TeamDao.class).deleteTeamMembership(team, user));
- if (!deleted) {
- throw new NotFoundException();
- }
-
- LOGGER.info(
- SecurityMarkers.SECURITY_AUDIT,
- "Team membership deleted: team={}, user={}", team, user);
- return Response.noContent().build();
- }
-}
diff --git a/apiserver/src/test/java/org/dependencytrack/resources/v2/AdvisoriesResourceTest.java b/apiserver/src/test/java/org/dependencytrack/resources/v2/AdvisoriesResourceTest.java
index ef25867079..01cb4cc105 100644
--- a/apiserver/src/test/java/org/dependencytrack/resources/v2/AdvisoriesResourceTest.java
+++ b/apiserver/src/test/java/org/dependencytrack/resources/v2/AdvisoriesResourceTest.java
@@ -300,7 +300,7 @@ public void testUploadAdvisoryCsafInvalid_returns400() {
}
@Test
- public void testUploadAdvisoryCsafValid_returns200() throws Exception {
+ public void testUploadAdvisoryCsafValid_returns201() throws Exception {
initializeWithPermissions(Permissions.VULNERABILITY_MANAGEMENT_CREATE);
// Load a valid CSAF document from test resources
@@ -322,14 +322,15 @@ public void testUploadAdvisoryCsafValid_returns200() throws Exception {
.header(X_API_KEY, apiKey)
.post(Entity.entity(multiPart, multiPart.getMediaType()))) {
- // If not 200, print the error for debugging
- if (response.getStatus() != 200) {
- String errorBody = response.readEntity(String.class);
- System.out.println("Error response: " + errorBody);
- }
-
// Assert successful upload
- assertThat(response.getStatus()).isEqualTo(200);
+ assertThat(response.getStatus()).isEqualTo(201);
+ assertThat(response.getLocation()).isNotNull();
+ assertThat(response.getLocation().getPath()).matches("/advisories/.+");
+ assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
+ {
+ "id": "${json-unit.any-string}"
+ }
+ """);
}
}
diff --git a/apiserver/src/test/java/org/dependencytrack/resources/v2/ComponentsResourceTest.java b/apiserver/src/test/java/org/dependencytrack/resources/v2/ComponentsResourceTest.java
index b3cd19b749..a6c40e8a49 100644
--- a/apiserver/src/test/java/org/dependencytrack/resources/v2/ComponentsResourceTest.java
+++ b/apiserver/src/test/java/org/dependencytrack/resources/v2/ComponentsResourceTest.java
@@ -71,7 +71,8 @@ public void createComponentTest() {
}
""".formatted(project.getUuid())));
assertThat(response.getStatus()).isEqualTo(201);
- assertThat(response.getLocation()).hasPath("/components");
+ assertThat(response.getLocation()).isNotNull();
+ assertThat(response.getLocation().getPath()).matches("/components/.+");
assertThat(getPlainTextBody(response)).isEmpty();
qm.getPersistenceManager().evictAll();
diff --git a/apiserver/src/test/java/org/dependencytrack/resources/v2/CsafResourceTest.java b/apiserver/src/test/java/org/dependencytrack/resources/v2/CsafResourceTest.java
index 0f703d072b..53398a1f0f 100644
--- a/apiserver/src/test/java/org/dependencytrack/resources/v2/CsafResourceTest.java
+++ b/apiserver/src/test/java/org/dependencytrack/resources/v2/CsafResourceTest.java
@@ -404,7 +404,8 @@ void triggerCsafProviderDiscoveryShouldReturnAccepted() {
.header(X_API_KEY, apiKey)
.post(null);
assertThat(response.getStatus()).isEqualTo(202);
- assertThat(getPlainTextBody(response)).isEmpty();
+ assertThat(response.getLocation()).isNotNull();
+ assertThat(response.getLocation().getPath()).endsWith("/csaf-aggregators/" + aggregator.getId());
final CreateWorkflowRunRequest> createRunRequest = createRunCaptor.getValue();
assertThat(createRunRequest.workflowName()).isEqualTo("discover-csaf-providers");
diff --git a/apiserver/src/test/java/org/dependencytrack/resources/v2/MetricsResourceTest.java b/apiserver/src/test/java/org/dependencytrack/resources/v2/MetricsResourceTest.java
deleted file mode 100644
index 2f665f7f69..0000000000
--- a/apiserver/src/test/java/org/dependencytrack/resources/v2/MetricsResourceTest.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * 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.v2;
-
-import jakarta.json.JsonObject;
-import jakarta.ws.rs.core.Response;
-import net.javacrumbs.jsonunit.core.Option;
-import org.dependencytrack.JerseyTestExtension;
-import org.dependencytrack.ResourceTest;
-import org.dependencytrack.auth.Permissions;
-import org.dependencytrack.model.Project;
-import org.dependencytrack.model.ProjectMetrics;
-import org.dependencytrack.model.VulnerabilityMetrics;
-import org.dependencytrack.persistence.jdbi.MetricsTestDao;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.Date;
-
-import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.dependencytrack.persistence.jdbi.JdbiFactory.useJdbiHandle;
-
-public class MetricsResourceTest extends ResourceTest {
-
- @RegisterExtension
- static JerseyTestExtension jersey = new JerseyTestExtension(new ResourceConfig());
-
- @Test
- public void getCurrentPortfolioMetricsEmptyTest() {
- initializeWithPermissions(Permissions.VIEW_PORTFOLIO);
- enablePortfolioAccessControl();
-
- final Response response = jersey
- .target("/metrics/portfolio/current")
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response))
- .isEqualTo(/* language=JSON */ """
- {
- "components": 0,
- "critical": 0,
- "findings_audited": 0,
- "findings_total": 0,
- "findings_unaudited": 0,
- "high": 0,
- "inherited_risk_score": 0.0,
- "low": 0,
- "medium": 0,
- "observed_at": "${json-unit.any-number}",
- "policy_violations_audited": 0,
- "policy_violations_fail": 0,
- "policy_violations_info": 0,
- "policy_violations_license_audited": 0,
- "policy_violations_license_total": 0,
- "policy_violations_license_unaudited": 0,
- "policy_violations_operational_audited": 0,
- "policy_violations_operational_total": 0,
- "policy_violations_operational_unaudited": 0,
- "policy_violations_security_audited": 0,
- "policy_violations_security_total": 0,
- "policy_violations_security_unaudited": 0,
- "policy_violations_total": 0,
- "policy_violations_unaudited": 0,
- "policy_violations_warn": 0,
- "projects": 0,
- "suppressed": 0,
- "unassigned": 0,
- "vulnerabilities": 0,
- "vulnerable_components": 0,
- "vulnerable_projects": 0
- }
- """);
- }
-
- @Test
- public void getCurrentPortfolioMetricsAclTest() {
- initializeWithPermissions(Permissions.VIEW_PORTFOLIO);
- enablePortfolioAccessControl();
-
- final var accessibleProjectA = new Project();
- accessibleProjectA.setName("acme-app-a");
- accessibleProjectA.addAccessTeam(super.team);
- qm.persist(accessibleProjectA);
-
- final var accessibleProjectB = new Project();
- accessibleProjectB.setName("acme-app-b");
- accessibleProjectB.addAccessTeam(super.team);
- qm.persist(accessibleProjectB);
-
- final var inactiveAccessibleProject = new Project();
- inactiveAccessibleProject.setName("acme-app-inactive");
- inactiveAccessibleProject.setInactiveSince(new Date());
- inactiveAccessibleProject.addAccessTeam(super.team);
- qm.persist(inactiveAccessibleProject);
-
- final var inaccessibleProject = new Project();
- inaccessibleProject.setName("acme-app-inaccessible");
- qm.persist(inaccessibleProject);
-
- final var today = LocalDate.now();
-
- useJdbiHandle(handle -> {
- var dao = handle.attach(MetricsTestDao.class);
-
- dao.createMetricsPartitionsForDate("PROJECTMETRICS", today);
- dao.createMetricsPartitionsForDate("PROJECTMETRICS", today.minusDays(1));
- dao.createMetricsPartitionsForDate("PROJECTMETRICS", today.minusDays(2));
-
- {
- // Create metrics for "yesterday".
-
- var accessibleProjectAMetrics = new ProjectMetrics();
- accessibleProjectAMetrics.setProjectId(accessibleProjectA.getId());
- accessibleProjectAMetrics.setComponents(2);
- accessibleProjectAMetrics.setFirstOccurrence(Date.from(today.minusDays(1).atTime(1, 1).atZone(ZoneId.systemDefault()).toInstant()));
- accessibleProjectAMetrics.setLastOccurrence(accessibleProjectAMetrics.getFirstOccurrence());
- dao.createProjectMetrics(accessibleProjectAMetrics);
- }
-
- {
- // Create metrics for "today".
-
- // Do not create metrics for accessibleProjectA.
- // Its metrics from "yesterday" are supposed to carry over to "today".
-
- var accessibleProjectBMetrics = new ProjectMetrics();
- accessibleProjectBMetrics.setProjectId(accessibleProjectB.getId());
- accessibleProjectBMetrics.setComponents(1);
- accessibleProjectBMetrics.setFirstOccurrence(Date.from(today.atTime(1, 1).atZone(ZoneId.systemDefault()).toInstant()));
- accessibleProjectBMetrics.setLastOccurrence(accessibleProjectBMetrics.getFirstOccurrence());
- dao.createProjectMetrics(accessibleProjectBMetrics);
-
- // Metrics of inactive projects must not be considered.
- var inactiveAccessibleProjectMetrics = new ProjectMetrics();
- inactiveAccessibleProjectMetrics.setProjectId(inactiveAccessibleProject.getId());
- inactiveAccessibleProjectMetrics.setComponents(111);
- inactiveAccessibleProjectMetrics.setFirstOccurrence(Date.from(today.atTime(2, 2).atZone(ZoneId.systemDefault()).toInstant()));
- inactiveAccessibleProjectMetrics.setLastOccurrence(inactiveAccessibleProjectMetrics.getFirstOccurrence());
- dao.createProjectMetrics(inactiveAccessibleProjectMetrics);
-
- // Metrics of inaccessible projects must not be considered.
- var inaccessibleProjectMetrics = new ProjectMetrics();
- inaccessibleProjectMetrics.setProjectId(inaccessibleProject.getId());
- inaccessibleProjectMetrics.setComponents(666);
- inaccessibleProjectMetrics.setFirstOccurrence(Date.from(today.atTime(3, 3).atZone(ZoneId.systemDefault()).toInstant()));
- inaccessibleProjectMetrics.setLastOccurrence(inaccessibleProjectMetrics.getFirstOccurrence());
- dao.createProjectMetrics(inaccessibleProjectMetrics);
- }
- });
-
- final Response response = jersey
- .target("/metrics/portfolio/current")
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response))
- .withOptions(Option.IGNORING_EXTRA_FIELDS)
- .isEqualTo(/* language=JSON */ """
- {
- "projects": 2,
- "components": 3
- }
- """);
- }
-
- @Test
- public void getVulnerabilityMetricsPaginated() {
- initializeWithPermissions(Permissions.VIEW_PORTFOLIO);
- enablePortfolioAccessControl();
-
- for (int i = 1; i < 4; i++) {
- var metrics = new VulnerabilityMetrics();
- metrics.setYear(2025);
- metrics.setMonth(i);
- metrics.setCount(i);
- metrics.setMeasuredAt(Date.from(Instant.now()));
- qm.persist(metrics);
- }
-
- Response response = jersey.target("/metrics/vulnerabilities")
- .queryParam("limit", 2)
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- final JsonObject responseJson = parseJsonObject(response);
- assertThatJson(responseJson.toString()).isEqualTo(/* language=JSON */ """
- {
- "items" :
- [
- {
- "observed_at" : "${json-unit.any-number}",
- "year" : 2025,
- "month" : 1,
- "count" : 1
- },
- {
- "observed_at" : "${json-unit.any-number}",
- "year" : 2025,
- "month" : 2,
- "count" : 2
- }
- ],
- "next_page_token": "${json-unit.any-string}"
- }
- """);
-
- final String nextPageToken = responseJson.getString("next_page_token");
-
- response = jersey.target("/metrics/vulnerabilities")
- .queryParam("limit", 2)
- .queryParam("page_token", nextPageToken)
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "items" :
- [
- {
- "observed_at" : "${json-unit.any-number}",
- "year" : 2025,
- "month" : 3,
- "count" : 3
- }
- ]
- }
- """);
- }
-}
\ No newline at end of file
diff --git a/apiserver/src/test/java/org/dependencytrack/resources/v2/SecretsResourceTest.java b/apiserver/src/test/java/org/dependencytrack/resources/v2/SecretsResourceTest.java
index 9c2152746d..b729bba1a8 100644
--- a/apiserver/src/test/java/org/dependencytrack/resources/v2/SecretsResourceTest.java
+++ b/apiserver/src/test/java/org/dependencytrack/resources/v2/SecretsResourceTest.java
@@ -70,7 +70,7 @@ void afterEach() {
}
@Test
- void createSecretShouldCreateSecretAndReturnNoContent() {
+ void createSecretShouldCreateSecretAndReturnCreated() {
initializeWithPermissions(Permissions.SECRET_MANAGEMENT_CREATE);
final Response response = jersey
@@ -84,8 +84,9 @@ void createSecretShouldCreateSecretAndReturnNoContent() {
"value": "baz"
}
"""));
- assertThat(response.getStatus()).isEqualTo(204);
- assertThat(getPlainTextBody(response)).isEmpty();
+ assertThat(response.getStatus()).isEqualTo(201);
+ assertThat(response.getLocation()).isNotNull();
+ assertThat(response.getLocation().getPath()).endsWith("/secrets/foo");
verify(SECRET_MANAGER_MOCK).createSecret(eq("foo"), eq("bar"), eq("baz"));
}
@@ -149,7 +150,7 @@ void createSecretShouldReturnConflictWhenAlreadyExists() {
}
@Test
- void updateSecretShouldUpdateDescriptionAndReturnNoContent() {
+ void shouldUpdateDescriptionAndReturnNoContent() {
initializeWithPermissions(Permissions.SECRET_MANAGEMENT_UPDATE);
doReturn(true).when(SECRET_MANAGER_MOCK).updateSecret(eq("foo"), any(), any());
@@ -170,7 +171,7 @@ void updateSecretShouldUpdateDescriptionAndReturnNoContent() {
}
@Test
- void updateSecretShouldUpdateValueAndReturnNoContent() {
+ void shouldUpdateValueAndReturnNoContent() {
initializeWithPermissions(Permissions.SECRET_MANAGEMENT_UPDATE);
doReturn(true).when(SECRET_MANAGER_MOCK).updateSecret(eq("foo"), any(), any());
diff --git a/apiserver/src/test/java/org/dependencytrack/resources/v2/TeamsResourceTest.java b/apiserver/src/test/java/org/dependencytrack/resources/v2/TeamsResourceTest.java
deleted file mode 100644
index 2866fb01a4..0000000000
--- a/apiserver/src/test/java/org/dependencytrack/resources/v2/TeamsResourceTest.java
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
- * 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.v2;
-
-import alpine.model.Permission;
-import alpine.model.Team;
-import alpine.model.User;
-import jakarta.json.JsonObject;
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.core.Response;
-import org.dependencytrack.JerseyTestExtension;
-import org.dependencytrack.ResourceTest;
-import org.dependencytrack.auth.Permissions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-import java.util.List;
-
-import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class TeamsResourceTest extends ResourceTest {
-
- @RegisterExtension
- static JerseyTestExtension jersey = new JerseyTestExtension(new ResourceConfig());
-
- @Test
- public void listTeamsShouldReturnPaginatedTeams() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("team0");
- qm.createTeam("team1");
-
- Response response = jersey.target("/teams")
- .queryParam("limit", 2)
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- final JsonObject responseJson = parseJsonObject(response);
- assertThatJson(responseJson.toString()).isEqualTo(/* language=JSON */ """
- {
- "items": [
- {
- "name": "Test Users",
- "api_keys": 1,
- "members": 0
- },
- {
- "name": "team0",
- "api_keys": 0,
- "members": 0
- }
- ],
- "next_page_token": "${json-unit.any-string}",
- "total": {
- "count": 3,
- "type": "EXACT"
- }
- }
- """);
-
- final String nextPageToken = responseJson.getString("next_page_token");
-
- response = jersey.target("/teams")
- .queryParam("limit", 2)
- .queryParam("page_token", nextPageToken)
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "items": [
- {
- "name": "team1",
- "api_keys": 0,
- "members": 0
- }
- ],
- "total": {
- "count": 3,
- "type": "EXACT"
- }
- }
- """);
- }
-
- @Test
- public void createTeamShouldCreateTeam() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createPermission(
- Permissions.VIEW_PORTFOLIO.name(),
- Permissions.VIEW_PORTFOLIO.getDescription());
-
- final Response response = jersey.target("/teams")
- .request()
- .header(X_API_KEY, apiKey)
- .post(Entity.json(/* language=JSON */ """
- {
- "name": "foo",
- "permissions": [
- "VIEW_PORTFOLIO"
- ]
- }
- """));
- assertThat(response.getStatus()).isEqualTo(201);
- assertThat(response.getLocation()).hasPath("/teams/foo");
- assertThat(getPlainTextBody(response)).isEmpty();
-
- qm.getPersistenceManager().evictAll();
-
- final Team team = qm.getTeam("foo");
- assertThat(team).isNotNull();
- assertThat(team.getPermissions()).extracting(Permission::getName).containsOnly("VIEW_PORTFOLIO");
- }
-
- @Test
- public void createTeamShouldReturnConflictWhenAlreadyExists() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("foo");
-
- final Response response = jersey.target("/teams")
- .request()
- .header(X_API_KEY, apiKey)
- .post(Entity.json(/* language=JSON */ """
- {
- "name": "foo",
- "permissions": [
- "VIEW_PORTFOLIO"
- ]
- }
- """));
- assertThat(response.getStatus()).isEqualTo(409);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status":409,
- "title": "Resource already exists",
- "detail": "Team already exists"
- }
- """);
-
- qm.getPersistenceManager().evictAll();
-
- final Team team = qm.getTeam("foo");
- assertThat(team).isNotNull();
- assertThat(team.getPermissions()).isEmpty();
- }
-
- @Test
- public void getTeamShouldReturnTeam() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Team team = qm.createTeam("foo");
- team.setPermissions(List.of(
- qm.createPermission(
- Permissions.VIEW_PORTFOLIO.name(),
- Permissions.VIEW_PORTFOLIO.getDescription()),
- qm.createPermission(
- Permissions.VIEW_VULNERABILITY.name(),
- Permissions.VIEW_VULNERABILITY.getDescription())));
-
- final Response response = jersey.target("/teams/foo")
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "name": "foo",
- "permissions": [
- "VIEW_PORTFOLIO",
- "VIEW_VULNERABILITY"
- ]
- }
- """);
- }
-
- @Test
- public void getTeamShouldReturnNotFoundWhenTeamDoesNotExist() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Response response = jersey.target("/teams/foo")
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
- @Test
- public void deleteTeamShouldDeleteTeam() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("foo");
-
- final Response response = jersey.target("/teams/foo")
- .request()
- .header(X_API_KEY, apiKey)
- .delete();
- assertThat(response.getStatus()).isEqualTo(204);
- assertThat(getPlainTextBody(response)).isEmpty();
-
- qm.getPersistenceManager().evictAll();
-
- assertThat(qm.getTeam("foo")).isNull();
- }
-
- @Test
- public void deleteTeamsShouldReturnNotFoundWhenNotExists() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Response response = jersey.target("/teams/does-not-exist")
- .request()
- .header(X_API_KEY, apiKey)
- .delete();
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type": "about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
- @Test
- public void listTeamMembershipsShouldReturnPaginatedTeamMemberships() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Team teamA = qm.createTeam("team-a");
- qm.addUserToTeam(qm.createManagedUser("foo", "password"), teamA);
- qm.addUserToTeam(qm.createManagedUser("bar", "password"), teamA);
-
- final Team teamB = qm.createTeam("team-b");
- qm.addUserToTeam(qm.createManagedUser("aaa", "password"), teamB);
-
- Response response = jersey.target("/team-memberships")
- .queryParam("limit", 2)
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- final JsonObject responseJson = parseJsonObject(response);
- assertThatJson(responseJson.toString()).isEqualTo(/* language=JSON */ """
- {
- "items": [
- {
- "team_name": "team-a",
- "username": "bar"
- },
- {
- "team_name": "team-a",
- "username": "foo"
- }
- ],
- "next_page_token": "${json-unit.any-string}",
- "total": {
- "count": 3,
- "type": "EXACT"
- }
- }
- """);
-
- final String nextPageToken = responseJson.getString("next_page_token");
-
- response = jersey.target("/team-memberships")
- .queryParam("limit", 2)
- .queryParam("page_token", nextPageToken)
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "items": [
- {
- "team_name": "team-b",
- "username": "aaa"
- }
- ],
- "total": {
- "count": 3,
- "type": "EXACT"
- }
- }
- """);
- }
-
- @Test
- public void listTeamMembershipsShouldFilterByTeamName() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Team teamA = qm.createTeam("team-a");
- qm.addUserToTeam(qm.createManagedUser("foo", "password"), teamA);
-
- final Team teamB = qm.createTeam("team-b");
- qm.addUserToTeam(qm.createManagedUser("bar", "password"), teamB);
-
- Response response = jersey.target("/team-memberships")
- .queryParam("team", "team-b")
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "items": [
- {
- "team_name": "team-b",
- "username": "bar"
- }
- ],
- "total": {
- "count": 1,
- "type": "EXACT"
- }
- }
- """);
- }
-
- @Test
- public void listTeamMembershipsShouldFilterByUsername() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Team teamA = qm.createTeam("team-a");
- qm.addUserToTeam(qm.createManagedUser("foo", "password"), teamA);
-
- final Team teamB = qm.createTeam("team-b");
- qm.addUserToTeam(qm.createManagedUser("bar", "password"), teamB);
-
- final Response response = jersey.target("/team-memberships")
- .queryParam("user", "bar")
- .request()
- .header(X_API_KEY, apiKey)
- .get();
- assertThat(response.getStatus()).isEqualTo(200);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "items": [
- {
- "team_name": "team-b",
- "username": "bar"
- }
- ],
- "total": {
- "count": 1,
- "type": "EXACT"
- }
- }
- """);
- }
-
- @Test
- public void createTeamMembershipShouldCreateTeamMembership() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("team-foo");
- qm.createManagedUser("user-bar", "password");
-
- final Response response = jersey.target("/team-memberships")
- .request()
- .header(X_API_KEY, apiKey)
- .post(Entity.json(/* language=JSON */ """
- {
- "team_name": "team-foo",
- "username": "user-bar"
- }
- """));
- assertThat(response.getStatus()).isEqualTo(201);
- assertThat(response.getLocation()).isNull();
- assertThat(getPlainTextBody(response)).isEmpty();
-
- qm.getPersistenceManager().evictAll();
-
- assertThat(qm.getTeam("team-foo").getUsers())
- .extracting(User::getUsername)
- .contains("user-bar");
- }
-
- @Test
- public void createTeamMembershipShouldReturnConflictWhenAlreadyExists() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Team team = qm.createTeam("team-foo");
- final User user = qm.createManagedUser("user-bar", "password");
- qm.addUserToTeam(user, team);
-
- final Response response = jersey.target("/team-memberships")
- .request()
- .header(X_API_KEY, apiKey)
- .post(Entity.json(/* language=JSON */ """
- {
- "team_name": "team-foo",
- "username": "user-bar"
- }
- """));
- assertThat(response.getStatus()).isEqualTo(409);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status":409,
- "title": "Resource already exists",
- "detail": "Team membership already exists"
- }
- """);
- }
-
- @Test
- public void createTeamMembershipShouldReturnNotFoundWhenTeamNotExists() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createManagedUser("user-bar", "password");
-
- final Response response = jersey.target("/team-memberships")
- .request()
- .header(X_API_KEY, apiKey)
- .post(Entity.json(/* language=JSON */ """
- {
- "team_name": "team-foo",
- "username": "user-bar"
- }
- """));
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
- @Test
- public void createTeamMembershipShouldReturnNotFoundWhenUserNotExists() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("team-foo");
-
- final Response response = jersey.target("/team-memberships")
- .request()
- .header(X_API_KEY, apiKey)
- .post(Entity.json(/* language=JSON */ """
- {
- "team_name": "team-foo",
- "username": "user-bar"
- }
- """));
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
- @Test
- public void deleteTeamMembershipShouldDeleteTeamMembership() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- final Team team = qm.createTeam("foo");
- qm.addUserToTeam(qm.createManagedUser("bar", "password"), team);
-
- final Response response = jersey.target("/team-memberships")
- .queryParam("team", "foo")
- .queryParam("user", "bar")
- .request()
- .header(X_API_KEY, apiKey)
- .delete();
- assertThat(response.getStatus()).isEqualTo(204);
-
- qm.getPersistenceManager().evictAll();
-
- assertThat(team.getUsers()).isEmpty();
- }
-
- @Test
- public void deleteTeamMembershipShouldReturnNotFoundWhenTeamDoesNotExist() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createManagedUser("bar", "password");
-
- final Response response = jersey.target("/team-memberships")
- .queryParam("team", "foo")
- .queryParam("user", "bar")
- .request()
- .header(X_API_KEY, apiKey)
- .delete();
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
- @Test
- public void deleteTeamMembershipShouldReturnNotFoundWhenUserDoesNotExist() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("foo");
-
- final Response response = jersey.target("/team-memberships")
- .queryParam("team", "foo")
- .queryParam("user", "bar")
- .request()
- .header(X_API_KEY, apiKey)
- .delete();
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
- @Test
- public void deleteTeamMembershipShouldReturnNotFoundWhenMembershipDoesNotExist() {
- initializeWithPermissions(Permissions.ACCESS_MANAGEMENT);
-
- qm.createTeam("foo");
- qm.createManagedUser("bar", "password");
-
- final Response response = jersey.target("/team-memberships")
- .queryParam("team", "foo")
- .queryParam("user", "bar")
- .request()
- .header(X_API_KEY, apiKey)
- .delete();
- assertThat(response.getStatus()).isEqualTo(404);
- assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """
- {
- "type":"about:blank",
- "status": 404,
- "title": "Not Found",
- "detail": "The requested resource could not be found."
- }
- """);
- }
-
-}
\ No newline at end of file