Skip to content

Commit

Permalink
#455 validate version on provider and module upload
Browse files Browse the repository at this point in the history
  • Loading branch information
PacoVK committed Nov 2, 2024
1 parent 56b2b6e commit d74ecfb
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/main/java/api/Modules.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package api;

import core.exceptions.InvalidVersionException;
import core.exceptions.StorageException;
import core.service.ModuleService;
import core.service.StorageService;
import core.service.VersionService;
import core.storage.util.StorageUtil;
import core.terraform.ArtifactVersion;
import core.terraform.Module;
Expand Down Expand Up @@ -54,6 +56,9 @@ public Response getModuleByName(String namespace, String name, String provider)
public Response uploadModule(
String namespace, String name, String provider,
String version, FormData archive) throws Exception {
if(!VersionService.isValidModuleVersion(version)) {
throw new InvalidVersionException(version);
}
Module module = new Module(namespace, name, provider, version);
module.setPublished_at(Instant.now());
archive.setEntity(module);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/api/Providers.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import api.dto.ProviderTerraformDto;
import api.dto.ProviderVersionsDto;
import core.exceptions.InvalidVersionException;
import core.service.ProviderService;
import core.service.VersionService;
import core.terraform.Provider;
import core.upload.FormData;
import core.upload.service.UploadService;
Expand Down Expand Up @@ -67,6 +69,9 @@ public Response getDownloadUrl(
@Path("{namespace}/{type}/{version}")
public Response uploadProvider(String namespace, String type, String version, FormData archive)
throws Exception {
if(!VersionService.isValidProviderVersion(version)) {
throw new InvalidVersionException(version);
}
Provider provider = new Provider(namespace, type);
provider.setPublished_at(Instant.now());
archive.setEntity(provider);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/api/mapper/exceptions/TapirExceptionMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import api.mapper.exceptions.response.ErrorResponse;
import core.exceptions.NotFoundException;
import core.exceptions.RegistryComplianceException;
import core.exceptions.TapirException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
Expand All @@ -26,6 +27,9 @@ public Response toResponse(TapirException e) {
if (e instanceof NotFoundException) {
status = Response.Status.NOT_FOUND;
}
if(e instanceof RegistryComplianceException) {
status = Response.Status.BAD_REQUEST;
}
return Response.status(status)
.entity(new ErrorResponse(errorId, errorMessage))
.header("Content-Type", MediaType.APPLICATION_JSON).build();
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/core/exceptions/InvalidVersionException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package core.exceptions;

public class InvalidVersionException extends TapirException implements RegistryComplianceException {

private static final String errorMessage = "Version %s is invalid and does not comply with the Terraform registry versioning specification";

public InvalidVersionException(String version) {
super(String.format(errorMessage, version));
}

public InvalidVersionException(String version, Throwable cause) {
super(String.format(errorMessage, version), cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package core.exceptions;

public interface RegistryComplianceException {
}
20 changes: 20 additions & 0 deletions src/main/java/core/service/VersionService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package core.service;

import java.util.regex.Pattern;

public class VersionService {

// see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
private static final String SEMVER_PATTERN_TEMPLATE = "^%s(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$";

private static final Pattern moduleSemVerPattern = Pattern.compile(String.format(SEMVER_PATTERN_TEMPLATE, "(v?)"));
private static final Pattern providerSemVerPattern = Pattern.compile(String.format(SEMVER_PATTERN_TEMPLATE, "v"));

public static boolean isValidModuleVersion(String version) {
return moduleSemVerPattern.matcher(version).matches();
}

public static boolean isValidProviderVersion(String version) {
return providerSemVerPattern.matcher(version).matches();
}
}
12 changes: 12 additions & 0 deletions src/test/java/api/mapper/exceptions/TapirExceptionMapperTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package api.mapper.exceptions;

import core.exceptions.InvalidVersionException;
import static org.junit.jupiter.api.Assertions.assertEquals;

import api.mapper.exceptions.response.ErrorResponse;
Expand All @@ -14,6 +15,7 @@
class TapirExceptionMapperTest {

TapirExceptionMapper mapper = new TapirExceptionMapper();

@Test
void getCorrectStatusWhenModuleNotFoundExceptionOccurs() {
TapirException notFoundException = new ModuleNotFoundException("fake-id-version");
Expand Down Expand Up @@ -43,4 +45,14 @@ void getCorrectStatusWhenRuntimeExceptionOccurs() {
assertEquals(errors.size(), 1);
assertEquals(errors.get(0).getMessage(), "Module/ Provider with id fake-id-version could not be found");
}

@Test
void getCorrectStatusWhenRegistryComplianceExceptionOccurs() {
TapirException invalidVersionException = new InvalidVersionException("wrong-version");
Response badRequestResponse = mapper.toResponse(invalidVersionException);
List<ErrorResponse.ErrorMessage> errors = ((ErrorResponse) badRequestResponse.getEntity()).getErrors();
assertEquals(badRequestResponse.getStatus(), 400);
assertEquals(errors.size(), 1);
assertEquals(errors.get(0).getMessage(), "Version wrong-version is invalid and does not comply with the Terraform registry versioning specification");
}
}
29 changes: 29 additions & 0 deletions src/test/java/core/service/VersionServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package core.service;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class VersionServiceTest {

@Test
void isValidModuleVersion() {
assertTrue(VersionService.isValidModuleVersion("1.0.0"));
assertTrue(VersionService.isValidModuleVersion("v1.0.0"));
assertTrue(VersionService.isValidModuleVersion("2.1.3-alpha"));
assertTrue(VersionService.isValidModuleVersion("0.0.1-beta+exp.sha.5114f85"));
assertTrue(VersionService.isValidModuleVersion("1.0.0+20130313144700"));
assertFalse(VersionService.isValidModuleVersion("1.0"));
assertFalse(VersionService.isValidModuleVersion("1.Foo.0"));
}

@Test
void isValidProviderVersion() {
assertTrue(VersionService.isValidProviderVersion("v1.0.0"));
assertTrue(VersionService.isValidProviderVersion("v2.1.3-alpha"));
assertTrue(VersionService.isValidProviderVersion("v0.0.1-beta+exp.sha.5114f85"));
assertFalse(VersionService.isValidProviderVersion("1.0.0"));
assertFalse(VersionService.isValidProviderVersion("1.0.0+20130313144700"));
assertFalse(VersionService.isValidProviderVersion("v1.0"));
assertFalse(VersionService.isValidProviderVersion("v1.Foo.0"));
}
}

0 comments on commit d74ecfb

Please sign in to comment.