🚧 Add file retrieval
This commit is contained in:
parent
9e28c1c7f3
commit
1545a2c70d
29
pom.xml
29
pom.xml
@ -12,7 +12,7 @@
|
|||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||||
<quarkus.platform.version>3.30.8</quarkus.platform.version>
|
<quarkus.platform.version>3.34.3</quarkus.platform.version>
|
||||||
<skipITs>true</skipITs>
|
<skipITs>true</skipITs>
|
||||||
<surefire-plugin.version>3.5.4</surefire-plugin.version>
|
<surefire-plugin.version>3.5.4</surefire-plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
@ -64,16 +64,24 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-elytron-security</artifactId>
|
<artifactId>quarkus-arc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-arc</artifactId>
|
<artifactId>quarkus-flyway</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-database-postgresql</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>2.21.0</version>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>at.favre.lib</groupId>
|
||||||
|
<artifactId>bcrypt</artifactId>
|
||||||
|
<version>0.10.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
@ -83,7 +91,6 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>3.20.0</version>
|
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -94,12 +101,16 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-artifact</artifactId>
|
<artifactId>maven-artifact</artifactId>
|
||||||
<version>3.9.12</version>
|
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-junit5</artifactId>
|
<artifactId>quarkus-junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-test-security</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -138,7 +149,7 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>${surefire-plugin.version}</version>
|
<version>${surefire-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
|
<argLine>@{argLine} --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||||
<maven.home>${maven.home}</maven.home>
|
<maven.home>${maven.home}</maven.home>
|
||||||
@ -157,7 +168,7 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
|
<argLine>@{argLine} --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
||||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||||
|
|||||||
@ -27,8 +27,6 @@ import java.util.Base64;
|
|||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class CustomAuthenticationMechanism implements HttpAuthenticationMechanism
|
public class CustomAuthenticationMechanism implements HttpAuthenticationMechanism
|
||||||
{
|
{
|
||||||
private static final String SESSION_COOKIE = "session";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager)
|
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
@IfBuildProfile("test")
|
@IfBuildProfile("x")
|
||||||
public class Dev
|
public class Dev
|
||||||
{
|
{
|
||||||
@Inject
|
@Inject
|
||||||
|
|||||||
@ -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<String, String> grantParams(String code)
|
|
||||||
{
|
|
||||||
return Map.ofEntries(
|
|
||||||
Map.entry("grant_type", "authorization_code"),
|
|
||||||
Map.entry("code", code),
|
|
||||||
Map.entry("redirect_uri", "redirectUri")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +1,12 @@
|
|||||||
package dev.dinauer.maven;
|
package dev.dinauer.maven;
|
||||||
|
|
||||||
|
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||||
import dev.dinauer.maven.maven.token.TokenEntity;
|
import dev.dinauer.maven.maven.token.TokenEntity;
|
||||||
import dev.dinauer.maven.maven.token.TokenRepo;
|
import dev.dinauer.maven.maven.token.TokenRepo;
|
||||||
import io.quarkus.elytron.security.common.BcryptUtil;
|
|
||||||
import io.quarkus.security.AuthenticationFailedException;
|
import io.quarkus.security.AuthenticationFailedException;
|
||||||
import io.quarkus.security.identity.AuthenticationRequestContext;
|
import io.quarkus.security.identity.AuthenticationRequestContext;
|
||||||
import io.quarkus.security.identity.IdentityProvider;
|
import io.quarkus.security.identity.IdentityProvider;
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
import io.quarkus.security.identity.request.TokenAuthenticationRequest;
|
|
||||||
import io.quarkus.security.identity.request.UsernamePasswordAuthenticationRequest;
|
import io.quarkus.security.identity.request.UsernamePasswordAuthenticationRequest;
|
||||||
import io.quarkus.security.runtime.QuarkusPrincipal;
|
import io.quarkus.security.runtime.QuarkusPrincipal;
|
||||||
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
|
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
|
||||||
@ -17,12 +16,14 @@ import jakarta.enterprise.context.ApplicationScoped;
|
|||||||
import jakarta.enterprise.context.control.ActivateRequestContext;
|
import jakarta.enterprise.context.control.ActivateRequestContext;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class TokenIdentityProvider implements IdentityProvider<UsernamePasswordAuthenticationRequest>
|
public class TokenIdentityProvider implements IdentityProvider<UsernamePasswordAuthenticationRequest>
|
||||||
{
|
{
|
||||||
|
private static final BCrypt.Verifyer VERIFIER = BCrypt.verifyer();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TokenRepo tokenRepo;
|
TokenRepo tokenRepo;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ public class TokenIdentityProvider implements IdentityProvider<UsernamePasswordA
|
|||||||
String password = new String(request.getPassword().getPassword());
|
String password = new String(request.getPassword().getPassword());
|
||||||
for (TokenEntity token : tokenRepo.findByUserId(username))
|
for (TokenEntity token : tokenRepo.findByUserId(username))
|
||||||
{
|
{
|
||||||
if (BcryptUtil.matches(password, token.getToken()))
|
if (VERIFIER.verify(password.getBytes(StandardCharsets.UTF_8), token.getToken().getBytes(StandardCharsets.UTF_8)).verified)
|
||||||
{
|
{
|
||||||
LocalDate now = LocalDate.now();
|
LocalDate now = LocalDate.now();
|
||||||
if (!now.isAfter(token.getExpiresAt()))
|
if (!now.isAfter(token.getExpiresAt()))
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package dev.dinauer.maven.app;
|
package dev.dinauer.maven.app;
|
||||||
|
|
||||||
import dev.dinauer.maven.jpa.maven.repo.VersionRepo;
|
|
||||||
import dev.dinauer.maven.user.User;
|
import dev.dinauer.maven.user.User;
|
||||||
import dev.dinauer.maven.jpa.maven.Version;
|
import dev.dinauer.maven.jpa.maven.Version;
|
||||||
import dev.dinauer.maven.jpa.maven.repo.ArtifactRepo;
|
import dev.dinauer.maven.jpa.maven.repo.ArtifactRepo;
|
||||||
@ -27,9 +26,6 @@ public class ArtifactResource
|
|||||||
@RestClient
|
@RestClient
|
||||||
UserClient userClient;
|
UserClient userClient;
|
||||||
|
|
||||||
@Inject
|
|
||||||
VersionRepo versionRepo;
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
public List<ArtifactId> get()
|
public List<ArtifactId> get()
|
||||||
{
|
{
|
||||||
|
|||||||
72
src/main/java/dev/dinauer/maven/maven/core/JarService.java
Normal file
72
src/main/java/dev/dinauer/maven/maven/core/JarService.java
Normal file
@ -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<Jar> jars, String filename)
|
||||||
|
{
|
||||||
|
for (Jar jar : jars)
|
||||||
|
{
|
||||||
|
if (filename.equals(jar.getFilename()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,12 +15,12 @@ public class MavenContextProvider
|
|||||||
case JAR, POM ->
|
case JAR, POM ->
|
||||||
{
|
{
|
||||||
MavenUrlParser parser = MavenUrlParser.parse(path);
|
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 ->
|
case XML ->
|
||||||
{
|
{
|
||||||
MavenMetadataUrlParser parser = MavenMetadataUrlParser.parse(path);
|
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();
|
throw new RuntimeException();
|
||||||
|
|||||||
47
src/main/java/dev/dinauer/maven/maven/core/PomService.java
Normal file
47
src/main/java/dev/dinauer/maven/maven/core/PomService.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,9 @@ import jakarta.transaction.Transactional;
|
|||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import jdk.jshell.spi.ExecutionControl;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
@ -32,7 +34,10 @@ public class Service
|
|||||||
ArtifactService artifactService;
|
ArtifactService artifactService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TokenService tokenService;
|
JarService jarService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PomService pomService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Response upload(String path, byte[] body)
|
public Response upload(String path, byte[] body)
|
||||||
@ -44,12 +49,7 @@ public class Service
|
|||||||
{
|
{
|
||||||
if (FileHash.NONE.equals(mavenContext.file().getHash()))
|
if (FileHash.NONE.equals(mavenContext.file().getHash()))
|
||||||
{
|
{
|
||||||
String md5 = DigestUtils.md5Hex(body);
|
return jarService.store(mavenContext, 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 Response.accepted().build();
|
return Response.accepted().build();
|
||||||
}
|
}
|
||||||
@ -57,17 +57,7 @@ public class Service
|
|||||||
{
|
{
|
||||||
if (FileHash.NONE.equals(mavenContext.file().getHash()))
|
if (FileHash.NONE.equals(mavenContext.file().getHash()))
|
||||||
{
|
{
|
||||||
String md5 = DigestUtils.md5Hex(body);
|
return pomService.store(mavenContext, 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 Response.accepted().build();
|
return Response.accepted().build();
|
||||||
}
|
}
|
||||||
@ -87,32 +77,19 @@ public class Service
|
|||||||
{
|
{
|
||||||
case JAR ->
|
case JAR ->
|
||||||
{
|
{
|
||||||
Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
|
if (FileHash.NONE.equals(mavenContext.file().getHash()))
|
||||||
Optional<Jar> optionalJar = version.getJarByFilename(mavenContext.file().getRaw());
|
|
||||||
if (optionalJar.isEmpty())
|
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
return jarService.find(mavenContext);
|
||||||
}
|
}
|
||||||
switch (mavenContext.file().getHash())
|
return jarService.findHash(mavenContext);
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
case POM ->
|
case POM ->
|
||||||
{
|
{
|
||||||
Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
|
if (FileHash.NONE.equals(mavenContext.file().getHash()))
|
||||||
version.setLastPulled(ZonedDateTime.now());
|
{
|
||||||
version.incrementPullCount();
|
return pomService.find(mavenContext);
|
||||||
return Response.status(Response.Status.OK).type(MediaType.APPLICATION_XML).entity(version.getPom().getPom()).build();
|
}
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
case XML ->
|
case XML ->
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import dev.dinauer.maven.jpa.maven.ArtifactId;
|
|||||||
import dev.dinauer.maven.jpa.maven.Version;
|
import dev.dinauer.maven.jpa.maven.Version;
|
||||||
import dev.dinauer.maven.jpa.maven.repo.VersionRepo;
|
import dev.dinauer.maven.jpa.maven.repo.VersionRepo;
|
||||||
import dev.dinauer.maven.maven.token.TokenService;
|
import dev.dinauer.maven.maven.token.TokenService;
|
||||||
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ public class VersionService
|
|||||||
EventRepo eventRepo;
|
EventRepo eventRepo;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TokenService tokenService;
|
SecurityIdentity securityIdentity;
|
||||||
|
|
||||||
public Optional<Version> findOptional(String groupId, String artifactId, String version)
|
public Optional<Version> findOptional(String groupId, String artifactId, String version)
|
||||||
{
|
{
|
||||||
@ -52,6 +53,6 @@ public class VersionService
|
|||||||
public void persist(Version version)
|
public void persist(Version version)
|
||||||
{
|
{
|
||||||
versionRepo.persist(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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
package dev.dinauer.maven.maven.core.model;
|
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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,24 @@
|
|||||||
package dev.dinauer.maven.maven.token;
|
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.TokenCreation;
|
||||||
import dev.dinauer.maven.maven.token.dto.TokenSecret;
|
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.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import io.vertx.core.http.HttpServerRequest;
|
import io.vertx.core.http.HttpServerRequest;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import jakarta.ws.rs.BadRequestException;
|
|
||||||
import jakarta.ws.rs.core.SecurityContext;
|
import jakarta.ws.rs.core.SecurityContext;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class TokenService
|
public class TokenService
|
||||||
{
|
{
|
||||||
@Inject
|
private static final BCrypt.Hasher HASHER = BCrypt.withDefaults();
|
||||||
TokenRepo tokenRepo;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
HttpServerRequest request;
|
TokenRepo tokenRepo;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SecurityContext securityContext;
|
SecurityContext securityContext;
|
||||||
@ -36,7 +29,7 @@ public class TokenService
|
|||||||
String secret = UUID.randomUUID().toString();
|
String secret = UUID.randomUUID().toString();
|
||||||
TokenEntity entity = new TokenEntity()
|
TokenEntity entity = new TokenEntity()
|
||||||
.setName(tokenCreation.name())
|
.setName(tokenCreation.name())
|
||||||
.setToken(BcryptUtil.bcryptHash(secret))
|
.setToken(HASHER.hashToString(11, secret.toCharArray()))
|
||||||
.setUserId(securityContext.getUserPrincipal().getName())
|
.setUserId(securityContext.getUserPrincipal().getName())
|
||||||
.setExpiresAt(tokenCreation.expiresAt())
|
.setExpiresAt(tokenCreation.expiresAt())
|
||||||
.setCreatedAt(ZonedDateTime.now());
|
.setCreatedAt(ZonedDateTime.now());
|
||||||
@ -53,37 +46,4 @@ public class TokenService
|
|||||||
tokenRepo.delete(token);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,9 @@ public class UserResource
|
|||||||
@RestClient
|
@RestClient
|
||||||
UserClient userClient;
|
UserClient userClient;
|
||||||
|
|
||||||
@Inject
|
|
||||||
SecurityIdentity identity;
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
public Object get()
|
public Object get()
|
||||||
{
|
{
|
||||||
System.out.println(identity.getPrincipal().getName());
|
|
||||||
return userClient.getUser();
|
return userClient.getUser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,20 +4,17 @@
|
|||||||
%dev.quarkus.http.cors.origins=/.*/
|
%dev.quarkus.http.cors.origins=/.*/
|
||||||
%dev.quarkus.http.cors.access-control-allow-credentials=true
|
%dev.quarkus.http.cors.access-control-allow-credentials=true
|
||||||
quarkus.http.access-log.enabled=true
|
quarkus.http.access-log.enabled=true
|
||||||
|
|
||||||
|
%test.quarkus.http.test-port=9081
|
||||||
# Auth
|
# Auth
|
||||||
quarkus.http.auth.permission.authenticated.paths=/*
|
quarkus.http.auth.permission.authenticated.paths=/*
|
||||||
quarkus.http.auth.permission.authenticated.policy=authenticated
|
quarkus.http.auth.permission.authenticated.policy=authenticated
|
||||||
quarkus.http.auth.permission.permit.paths=/callback
|
|
||||||
quarkus.http.auth.permission.permit.policy=permit
|
|
||||||
|
|
||||||
# Postgres
|
# Postgres
|
||||||
%dev,test.quarkus.datasource.db-kind=postgresql
|
quarkus.datasource.db-kind=postgresql
|
||||||
%dev,test.quarkus.hibernate-orm.schema-management.strategy=none
|
%dev.quarkus.datasource.username=postgres
|
||||||
%dev,test.quarkus.datasource.username=postgres
|
%dev.quarkus.datasource.password=postgres
|
||||||
%dev,test.quarkus.datasource.password=postgres
|
%dev.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
|
||||||
%dev,test.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
|
|
||||||
|
|
||||||
%prod.quarkus.hibernate-orm.schema-management.strategy=drop-and-create
|
|
||||||
|
|
||||||
quarkus.oidc.auth-server-url=http://localhost:8089/api/realms/maven
|
quarkus.oidc.auth-server-url=http://localhost:8089/api/realms/maven
|
||||||
quarkus.oidc.client-id=backend
|
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.client-id=backend
|
||||||
quarkus.oidc-client.credentials.secret=backend
|
quarkus.oidc-client.credentials.secret=backend
|
||||||
|
|
||||||
quarkus.rest-client.idp.url=http://localhost:8089/api/realms/maven
|
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
|
||||||
101
src/main/resources/db/migration/V1.0.0__init.sql
Normal file
101
src/main/resources/db/migration/V1.0.0__init.sql
Normal file
@ -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
|
||||||
|
);
|
||||||
|
|
||||||
52
src/test/java/dev/dinauer/maven/maven/core/ResourceTest.java
Normal file
52
src/test/java/dev/dinauer/maven/maven/core/ResourceTest.java
Normal file
@ -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> version = versionService.findOptional("org.postgresql", "postgresql", "42.7.9");
|
||||||
|
|
||||||
|
Assertions.assertTrue(version.isPresent());
|
||||||
|
Assertions.assertTrue(responseJar.length > 0);
|
||||||
|
Assertions.assertTrue(responsePom !=null && responsePom.startsWith("<?xml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream readFile(String path)
|
||||||
|
{
|
||||||
|
return getClass().getResourceAsStream(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user