diff --git a/pom.xml b/pom.xml
index 435e1ed..a06e341 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
UTF-8
quarkus-bom
io.quarkus.platform
- 3.30.8
+ 3.34.3
true
3.5.4
@@ -64,16 +64,24 @@
io.quarkus
- quarkus-elytron-security
+ quarkus-arc
io.quarkus
- quarkus-arc
+ quarkus-flyway
+
+
+ org.flywaydb
+ flyway-database-postgresql
commons-io
commons-io
- 2.21.0
+
+
+ at.favre.lib
+ bcrypt
+ 0.10.2
commons-codec
@@ -83,7 +91,6 @@
org.apache.commons
commons-lang3
- 3.20.0
compile
@@ -94,12 +101,16 @@
org.apache.maven
maven-artifact
- 3.9.12
compile
io.quarkus
- quarkus-junit5
+ quarkus-junit
+ test
+
+
+ io.quarkus
+ quarkus-test-security
test
@@ -138,7 +149,7 @@
maven-surefire-plugin
${surefire-plugin.version}
- --add-opens java.base/java.lang=ALL-UNNAMED
+ @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED
org.jboss.logmanager.LogManager
${maven.home}
@@ -157,7 +168,7 @@
- --add-opens java.base/java.lang=ALL-UNNAMED
+ @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED
${project.build.directory}/${project.build.finalName}-runner
org.jboss.logmanager.LogManager
diff --git a/src/main/java/dev/dinauer/maven/CustomAuthenticationMechanism.java b/src/main/java/dev/dinauer/maven/CustomAuthenticationMechanism.java
index 9ef46d6..d433b65 100644
--- a/src/main/java/dev/dinauer/maven/CustomAuthenticationMechanism.java
+++ b/src/main/java/dev/dinauer/maven/CustomAuthenticationMechanism.java
@@ -27,8 +27,6 @@ import java.util.Base64;
@ApplicationScoped
public class CustomAuthenticationMechanism implements HttpAuthenticationMechanism
{
- private static final String SESSION_COOKIE = "session";
-
@Override
public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager)
{
diff --git a/src/main/java/dev/dinauer/maven/Dev.java b/src/main/java/dev/dinauer/maven/Dev.java
index 622dd91..cb2f124 100644
--- a/src/main/java/dev/dinauer/maven/Dev.java
+++ b/src/main/java/dev/dinauer/maven/Dev.java
@@ -11,7 +11,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
@ApplicationScoped
-@IfBuildProfile("test")
+@IfBuildProfile("x")
public class Dev
{
@Inject
diff --git a/src/main/java/dev/dinauer/maven/OidcCallback.java b/src/main/java/dev/dinauer/maven/OidcCallback.java
deleted file mode 100644
index 05b37bd..0000000
--- a/src/main/java/dev/dinauer/maven/OidcCallback.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package dev.dinauer.maven;
-
-import io.quarkus.oidc.client.NamedOidcClient;
-import io.quarkus.oidc.client.OidcClient;
-import io.quarkus.oidc.client.Tokens;
-import jakarta.annotation.security.PermitAll;
-import jakarta.inject.Inject;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.QueryParam;
-import jakarta.ws.rs.core.NewCookie;
-import jakarta.ws.rs.core.Response;
-
-import java.net.URI;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.util.Date;
-import java.util.Map;
-
-@Path("/callback")
-public class OidcCallback
-{
- @Inject
- @NamedOidcClient("ac")
- OidcClient client;
-
- @GET
- public Response callback(@QueryParam("code") String code)
- {
- Tokens tokens = client.getTokens(grantParams(code)).await().indefinitely();
- NewCookie cookie = new NewCookie.Builder("session").value(tokens.getAccessToken()).path("/").maxAge((int) (tokens.getAccessTokenExpiresAt() - ZonedDateTime.now().toEpochSecond())).secure(false).httpOnly(true).build();
- return Response.seeOther(URI.create("http://localhost:3000")).cookie(cookie).build();
- }
-
- private Map grantParams(String code)
- {
- return Map.ofEntries(
- Map.entry("grant_type", "authorization_code"),
- Map.entry("code", code),
- Map.entry("redirect_uri", "redirectUri")
- );
- }
-}
diff --git a/src/main/java/dev/dinauer/maven/TokenIdentityProvider.java b/src/main/java/dev/dinauer/maven/TokenIdentityProvider.java
index 006d0ad..0de99b0 100644
--- a/src/main/java/dev/dinauer/maven/TokenIdentityProvider.java
+++ b/src/main/java/dev/dinauer/maven/TokenIdentityProvider.java
@@ -1,13 +1,12 @@
package dev.dinauer.maven;
+import at.favre.lib.crypto.bcrypt.BCrypt;
import dev.dinauer.maven.maven.token.TokenEntity;
import dev.dinauer.maven.maven.token.TokenRepo;
-import io.quarkus.elytron.security.common.BcryptUtil;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.IdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
-import io.quarkus.security.identity.request.TokenAuthenticationRequest;
import io.quarkus.security.identity.request.UsernamePasswordAuthenticationRequest;
import io.quarkus.security.runtime.QuarkusPrincipal;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
@@ -17,12 +16,14 @@ import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;
import jakarta.inject.Inject;
+import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
-import java.util.Base64;
@ApplicationScoped
public class TokenIdentityProvider implements IdentityProvider
{
+ private static final BCrypt.Verifyer VERIFIER = BCrypt.verifyer();
+
@Inject
TokenRepo tokenRepo;
@@ -41,7 +42,7 @@ public class TokenIdentityProvider implements IdentityProvider get()
{
diff --git a/src/main/java/dev/dinauer/maven/maven/core/JarService.java b/src/main/java/dev/dinauer/maven/maven/core/JarService.java
new file mode 100644
index 0000000..9467ac1
--- /dev/null
+++ b/src/main/java/dev/dinauer/maven/maven/core/JarService.java
@@ -0,0 +1,72 @@
+package dev.dinauer.maven.maven.core;
+
+import dev.dinauer.maven.jpa.maven.Jar;
+import dev.dinauer.maven.jpa.maven.Version;
+import dev.dinauer.maven.maven.core.model.MavenContext;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.apache.commons.codec.digest.DigestUtils;
+
+import java.util.List;
+
+@ApplicationScoped
+public class JarService
+{
+ @Inject
+ VersionService versionService;
+
+ public Response store(MavenContext mavenContext, byte[] body)
+ {
+ String md5 = DigestUtils.md5Hex(body);
+ String sha1 = DigestUtils.sha1Hex(body);
+ Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
+ if (!existsJar(version.getJars(), mavenContext.file().getRaw()))
+ {
+ version.getJars().add(new Jar().setJar(body).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(mavenContext.path()).setFilename(mavenContext.file().getRaw()));
+ versionService.persist(version);
+ return Response.status(Response.Status.CREATED).build();
+ }
+ return Response.status(Response.Status.CONFLICT).build();
+ }
+
+ public Response find(MavenContext context)
+ {
+ return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(findJar(context).getJar()).build();
+ }
+
+ public Response findHash(MavenContext context)
+ {
+ switch (context.file().getHash())
+ {
+ case SHA1 ->
+ {
+ return Response.status(Response.Status.OK).type(MediaType.TEXT_PLAIN).entity(findJar(context).getSha1()).build();
+ }
+ case MD5 ->
+ {
+ return Response.status(Response.Status.OK).type(MediaType.TEXT_PLAIN).entity(findJar(context).getMd5()).build();
+ }
+ }
+ throw new BadRequestException();
+ }
+
+ private Jar findJar(MavenContext context)
+ {
+ return versionService.findOptional(context.groupId(), context.artifactId(), context.version().getRaw()).orElseThrow().getJarByFilename(context.file().getRaw()).orElseThrow();
+ }
+
+ private boolean existsJar(List jars, String filename)
+ {
+ for (Jar jar : jars)
+ {
+ if (filename.equals(jar.getFilename()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/dev/dinauer/maven/maven/core/MavenContextProvider.java b/src/main/java/dev/dinauer/maven/maven/core/MavenContextProvider.java
index 32ca385..156147a 100644
--- a/src/main/java/dev/dinauer/maven/maven/core/MavenContextProvider.java
+++ b/src/main/java/dev/dinauer/maven/maven/core/MavenContextProvider.java
@@ -15,12 +15,12 @@ public class MavenContextProvider
case JAR, POM ->
{
MavenUrlParser parser = MavenUrlParser.parse(path);
- return new MavenContext(parser.groupId(), parser.artifactId(), parser.version(), file);
+ return new MavenContext(path, parser.groupId(), parser.artifactId(), parser.version(), file);
}
case XML ->
{
MavenMetadataUrlParser parser = MavenMetadataUrlParser.parse(path);
- return new MavenContext(parser.groupId(), parser.artifactId(), null, file);
+ return new MavenContext(path, parser.groupId(), parser.artifactId(), null, file);
}
}
throw new RuntimeException();
diff --git a/src/main/java/dev/dinauer/maven/maven/core/PomService.java b/src/main/java/dev/dinauer/maven/maven/core/PomService.java
new file mode 100644
index 0000000..e4f5873
--- /dev/null
+++ b/src/main/java/dev/dinauer/maven/maven/core/PomService.java
@@ -0,0 +1,47 @@
+package dev.dinauer.maven.maven.core;
+
+import dev.dinauer.maven.jpa.maven.Pom;
+import dev.dinauer.maven.jpa.maven.Version;
+import dev.dinauer.maven.maven.core.model.MavenContext;
+import dev.dinauer.maven.maven.token.TokenService;
+import io.quarkus.security.identity.SecurityIdentity;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.apache.commons.codec.digest.DigestUtils;
+
+import java.time.ZonedDateTime;
+
+@ApplicationScoped
+public class PomService
+{
+ @Inject
+ SecurityIdentity securityIdentity;
+
+ @Inject
+ VersionService versionService;
+
+ public Response store(MavenContext mavenContext, byte[] body)
+ {
+ String md5 = DigestUtils.md5Hex(body);
+ String sha1 = DigestUtils.sha1Hex(body);
+ Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
+ version.setUploadedBy(securityIdentity.getPrincipal().getName());
+ if (version.getPom() == null)
+ {
+ version.setPom(new Pom().setPom(new String(body)).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(mavenContext.path()).setFilename(mavenContext.file().getRaw()));
+ versionService.persist(version);
+ return Response.status(Response.Status.CREATED).build();
+ }
+ return Response.status(Response.Status.CONFLICT).build();
+ }
+
+ public Response find(MavenContext mavenContext)
+ {
+ Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
+ version.setLastPulled(ZonedDateTime.now());
+ version.incrementPullCount();
+ return Response.status(Response.Status.OK).type(MediaType.APPLICATION_XML).entity(version.getPom().getPom()).build();
+ }
+}
diff --git a/src/main/java/dev/dinauer/maven/maven/core/Service.java b/src/main/java/dev/dinauer/maven/maven/core/Service.java
index 796d7db..4e76a73 100644
--- a/src/main/java/dev/dinauer/maven/maven/core/Service.java
+++ b/src/main/java/dev/dinauer/maven/maven/core/Service.java
@@ -16,7 +16,9 @@ import jakarta.transaction.Transactional;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
+import jdk.jshell.spi.ExecutionControl;
import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.NotImplementedException;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -32,7 +34,10 @@ public class Service
ArtifactService artifactService;
@Inject
- TokenService tokenService;
+ JarService jarService;
+
+ @Inject
+ PomService pomService;
@Transactional
public Response upload(String path, byte[] body)
@@ -44,12 +49,7 @@ public class Service
{
if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
- String md5 = DigestUtils.md5Hex(body);
- String sha1 = DigestUtils.sha1Hex(body);
- Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
- version.getJars().add(new Jar().setJar(body).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(path).setFilename(mavenContext.file().getRaw()));
- versionService.persist(version);
- return Response.status(Response.Status.CREATED).build();
+ return jarService.store(mavenContext, body);
}
return Response.accepted().build();
}
@@ -57,17 +57,7 @@ public class Service
{
if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
- String md5 = DigestUtils.md5Hex(body);
- String sha1 = DigestUtils.sha1Hex(body);
- Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
- version.setUploadedBy(tokenService.require());
- if (version.getPom() == null)
- {
- version.setPom(new Pom().setPom(new String(body)).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(path).setFilename(mavenContext.file().getRaw()));
- versionService.persist(version);
- return Response.status(Response.Status.CREATED).build();
- }
- return Response.status(Response.Status.CONFLICT).build();
+ return pomService.store(mavenContext, body);
}
return Response.accepted().build();
}
@@ -87,32 +77,19 @@ public class Service
{
case JAR ->
{
- Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
- Optional optionalJar = version.getJarByFilename(mavenContext.file().getRaw());
- if (optionalJar.isEmpty())
+ if (FileHash.NONE.equals(mavenContext.file().getHash()))
{
- throw new NotFoundException();
+ return jarService.find(mavenContext);
}
- switch (mavenContext.file().getHash())
- {
- case SHA1 ->
- {
- return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(optionalJar.get().getSha1()).build();
- }
- case MD5 ->
- {
- return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(optionalJar.get().getMd5()).build();
- }
- }
- version.setLastPulled(ZonedDateTime.now());
- return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(optionalJar.get().getJar()).build();
+ return jarService.findHash(mavenContext);
}
case POM ->
{
- Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
- version.setLastPulled(ZonedDateTime.now());
- version.incrementPullCount();
- return Response.status(Response.Status.OK).type(MediaType.APPLICATION_XML).entity(version.getPom().getPom()).build();
+ if (FileHash.NONE.equals(mavenContext.file().getHash()))
+ {
+ return pomService.find(mavenContext);
+ }
+ throw new NotImplementedException();
}
case XML ->
{
diff --git a/src/main/java/dev/dinauer/maven/maven/core/VersionService.java b/src/main/java/dev/dinauer/maven/maven/core/VersionService.java
index 5fa37d9..7e0478f 100644
--- a/src/main/java/dev/dinauer/maven/maven/core/VersionService.java
+++ b/src/main/java/dev/dinauer/maven/maven/core/VersionService.java
@@ -8,6 +8,7 @@ import dev.dinauer.maven.jpa.maven.ArtifactId;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.jpa.maven.repo.VersionRepo;
import dev.dinauer.maven.maven.token.TokenService;
+import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@@ -27,7 +28,7 @@ public class VersionService
EventRepo eventRepo;
@Inject
- TokenService tokenService;
+ SecurityIdentity securityIdentity;
public Optional findOptional(String groupId, String artifactId, String version)
{
@@ -52,6 +53,6 @@ public class VersionService
public void persist(Version version)
{
versionRepo.persist(version);
- eventRepo.persist(new Event().setAccountId(tokenService.require()).setType(EventType.UPLOAD).setResource(new Resource().setGroupId(version.getGroupId()).setArtifactId(version.getArtifactId()).setVersion(version.getVersion())).setTimestamp(ZonedDateTime.now()));
+ eventRepo.persist(new Event().setAccountId(securityIdentity.getPrincipal().getName()).setType(EventType.UPLOAD).setResource(new Resource().setGroupId(version.getGroupId()).setArtifactId(version.getArtifactId()).setVersion(version.getVersion())).setTimestamp(ZonedDateTime.now()));
}
}
\ No newline at end of file
diff --git a/src/main/java/dev/dinauer/maven/maven/core/model/MavenContext.java b/src/main/java/dev/dinauer/maven/maven/core/model/MavenContext.java
index 8f80ba8..adedc59 100644
--- a/src/main/java/dev/dinauer/maven/maven/core/model/MavenContext.java
+++ b/src/main/java/dev/dinauer/maven/maven/core/model/MavenContext.java
@@ -1,5 +1,5 @@
package dev.dinauer.maven.maven.core.model;
-public record MavenContext(String groupId, String artifactId, Version version, File file)
+public record MavenContext(String path, String groupId, String artifactId, Version version, File file)
{
}
diff --git a/src/main/java/dev/dinauer/maven/maven/token/TokenService.java b/src/main/java/dev/dinauer/maven/maven/token/TokenService.java
index 81eef2f..b4f7513 100644
--- a/src/main/java/dev/dinauer/maven/maven/token/TokenService.java
+++ b/src/main/java/dev/dinauer/maven/maven/token/TokenService.java
@@ -1,31 +1,24 @@
package dev.dinauer.maven.maven.token;
+import at.favre.lib.crypto.bcrypt.BCrypt;
import dev.dinauer.maven.maven.token.dto.TokenCreation;
import dev.dinauer.maven.maven.token.dto.TokenSecret;
-import io.quarkus.elytron.security.common.BcryptUtil;
-import io.quarkus.security.UnauthorizedException;
-import io.vertx.core.http.HttpHeaders;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import io.vertx.core.http.HttpServerRequest;
import jakarta.transaction.Transactional;
-import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.SecurityContext;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-import java.time.LocalDate;
import java.time.ZonedDateTime;
-import java.util.Base64;
import java.util.UUID;
@ApplicationScoped
public class TokenService
{
- @Inject
- TokenRepo tokenRepo;
+ private static final BCrypt.Hasher HASHER = BCrypt.withDefaults();
@Inject
- HttpServerRequest request;
+ TokenRepo tokenRepo;
@Inject
SecurityContext securityContext;
@@ -36,7 +29,7 @@ public class TokenService
String secret = UUID.randomUUID().toString();
TokenEntity entity = new TokenEntity()
.setName(tokenCreation.name())
- .setToken(BcryptUtil.bcryptHash(secret))
+ .setToken(HASHER.hashToString(11, secret.toCharArray()))
.setUserId(securityContext.getUserPrincipal().getName())
.setExpiresAt(tokenCreation.expiresAt())
.setCreatedAt(ZonedDateTime.now());
@@ -53,37 +46,4 @@ public class TokenService
tokenRepo.delete(token);
}
}
-
- public String require()
- {
- String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
- if (authHeader != null && !authHeader.isBlank())
- {
- String[] sections = authHeader.split("\\s+");
- if (sections.length == 2 && sections[0].equals("Basic"))
- {
- String value = new String(Base64.getDecoder().decode(sections[1]));
- String[] parts = value.split(":");
- if (parts.length == 2)
- {
- String username = parts[0];
- String password = parts[1];
- for (TokenEntity token : tokenRepo.findByUserId(username))
- {
- if (BcryptUtil.matches(password, token.getToken()))
- {
- LocalDate now = LocalDate.now();
- if (now.equals(token.getExpiresAt()) || now.isBefore(token.getExpiresAt()))
- {
- return username;
- }
- }
- }
- throw new UnauthorizedException();
- }
- }
- throw new BadRequestException();
- }
- throw new UnauthorizedException();
- }
}
diff --git a/src/main/java/dev/dinauer/maven/user/UserResource.java b/src/main/java/dev/dinauer/maven/user/UserResource.java
index 1dd64f2..8ab895b 100644
--- a/src/main/java/dev/dinauer/maven/user/UserResource.java
+++ b/src/main/java/dev/dinauer/maven/user/UserResource.java
@@ -12,13 +12,9 @@ public class UserResource
@RestClient
UserClient userClient;
- @Inject
- SecurityIdentity identity;
-
@GET
public Object get()
{
- System.out.println(identity.getPrincipal().getName());
return userClient.getUser();
}
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 1569706..549dc68 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -4,20 +4,17 @@
%dev.quarkus.http.cors.origins=/.*/
%dev.quarkus.http.cors.access-control-allow-credentials=true
quarkus.http.access-log.enabled=true
+
+%test.quarkus.http.test-port=9081
# Auth
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
-quarkus.http.auth.permission.permit.paths=/callback
-quarkus.http.auth.permission.permit.policy=permit
# Postgres
-%dev,test.quarkus.datasource.db-kind=postgresql
-%dev,test.quarkus.hibernate-orm.schema-management.strategy=none
-%dev,test.quarkus.datasource.username=postgres
-%dev,test.quarkus.datasource.password=postgres
-%dev,test.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
-
-%prod.quarkus.hibernate-orm.schema-management.strategy=drop-and-create
+quarkus.datasource.db-kind=postgresql
+%dev.quarkus.datasource.username=postgres
+%dev.quarkus.datasource.password=postgres
+%dev.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
quarkus.oidc.auth-server-url=http://localhost:8089/api/realms/maven
quarkus.oidc.client-id=backend
@@ -26,4 +23,7 @@ quarkus.oidc-client.auth-server-url=http://localhost:8089/api/realms/maven
quarkus.oidc-client.client-id=backend
quarkus.oidc-client.credentials.secret=backend
-quarkus.rest-client.idp.url=http://localhost:8089/api/realms/maven
\ No newline at end of file
+quarkus.rest-client.idp.url=http://localhost:8089/api/realms/maven
+
+%dev,test.quarkus.flyway.clean-at-start=true
+quarkus.flyway.migrate-at-start=true
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V1.0.0__init.sql b/src/main/resources/db/migration/V1.0.0__init.sql
new file mode 100644
index 0000000..f9c9d82
--- /dev/null
+++ b/src/main/resources/db/migration/V1.0.0__init.sql
@@ -0,0 +1,101 @@
+create table group_id
+(
+ created_at timestamp(6) with time zone,
+ updated_at timestamp(6) with time zone,
+ group_id varchar(255),
+ id varchar(255) not null
+ primary key
+);
+
+create table artifact_id
+(
+ created_at timestamp(6) with time zone,
+ updated_at timestamp(6) with time zone,
+ artifact_id varchar(255),
+ group_id varchar(255)
+ constraint fkghhaxmc9rw9lhck83y9o6bp1o
+ references group_id,
+ group_id_long varchar(255),
+ id varchar(255) not null
+ primary key
+);
+
+create table resource
+(
+ artifact_id varchar(255),
+ group_id varchar(255),
+ id varchar(255) not null
+ primary key,
+ version varchar(255)
+);
+
+create table event
+(
+ timestamp timestamp(6) with time zone,
+ accountid varchar(255),
+ id varchar(255) not null
+ primary key,
+ resource_id varchar(255)
+ unique
+ constraint fkclx5xnhdf2y3l1g3ae6ygsjmf
+ references resource,
+ type varchar(255)
+ constraint event_type_check
+ check ((type)::text = ANY ((ARRAY ['UPLOAD'::character varying, 'DELETE'::character varying])::text[]))
+);
+
+create table token
+(
+ expires_at date,
+ created_at timestamp(6) with time zone,
+ id varchar(255) not null
+ primary key,
+ name varchar(255),
+ token varchar(255),
+ user_id varchar(255)
+);
+
+create table version
+(
+ pull_count integer,
+ last_pulled timestamp(6) with time zone,
+ artifact_id varchar(255)
+ constraint fklcnjnhvigubmjqng9wt7lcw71
+ references artifact_id,
+ artifact_id_long varchar(255),
+ group_id_long varchar(255),
+ id varchar(255) not null
+ primary key,
+ uploaded_by varchar(255),
+ version varchar(255)
+);
+
+create table jar
+(
+ filename varchar(255),
+ id varchar(255) not null
+ primary key,
+ md5 varchar(255),
+ sha1 varchar(255),
+ url varchar(255),
+ version_id varchar(255)
+ constraint fkaf1ctqgugsre652w5465uhjbu
+ references version,
+ jar bytea
+);
+
+create table pom
+(
+ filename varchar(255),
+ id varchar(255) not null
+ primary key,
+ md5 varchar(255),
+ pom text,
+ sha1 varchar(255),
+ url varchar(255),
+ version_id varchar(255)
+ unique
+ constraint fker1jyb1nf0vpmyv5b6ooydplu
+ references version
+);
+
diff --git a/src/test/java/dev/dinauer/maven/maven/core/ResourceTest.java b/src/test/java/dev/dinauer/maven/maven/core/ResourceTest.java
new file mode 100644
index 0000000..85fd9fc
--- /dev/null
+++ b/src/test/java/dev/dinauer/maven/maven/core/ResourceTest.java
@@ -0,0 +1,52 @@
+package dev.dinauer.maven.maven.core;
+
+import dev.dinauer.maven.jpa.maven.Version;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.RestAssured;
+import jakarta.inject.Inject;
+import org.flywaydb.core.Flyway;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.util.Optional;
+
+@QuarkusTest
+public class ResourceTest
+{
+ @Inject
+ Flyway flyway;
+
+ @Inject
+ VersionService versionService;
+
+ @BeforeEach
+ void before()
+ {
+ flyway.clean();
+ flyway.migrate();
+ }
+
+ @Test
+ @TestSecurity(user = "user")
+ void test()
+ {
+ RestAssured.given().body(readFile("/jar/postgresql-42.7.9.jar")).put("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.jar");
+ RestAssured.given().body(readFile("/jar/postgresql-42.7.9.pom")).put("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom");
+ byte[] responseJar = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.jar").getBody().asByteArray();
+ String responsePom = RestAssured.given().get("/maven2/org/postgresql/postgresql/42.7.9/postgresql-42.7.9.pom").getBody().asString();
+
+ Optional version = versionService.findOptional("org.postgresql", "postgresql", "42.7.9");
+
+ Assertions.assertTrue(version.isPresent());
+ Assertions.assertTrue(responseJar.length > 0);
+ Assertions.assertTrue(responsePom !=null && responsePom.startsWith("