From ab454832fbebe330a37ce34b1b2fee6d13eed5bf Mon Sep 17 00:00:00 2001 From: dzikoysk Date: Sat, 26 Jun 2021 23:47:18 +0200 Subject: [PATCH] GH-458 Simplify 'web' domain and move infrastructure to the dedicated layer --- .../org/panda_lang/reposilite/Reposilite.kt | 11 +- .../reposilite/ReposiliteWebConfiguration.kt | 22 +++- .../AuthenticationWebConfiguration.kt | 2 +- .../infrastructure/AuthenticationEndpoint.kt | 4 +- .../auth/infrastructure/PostAuthHandler.kt | 4 +- .../reposilite/console/StatusCommand.kt | 2 +- .../application/ConsoleWebConfiguration.kt | 11 +- .../infrastructure/RemoteExecutionEndpoint.kt | 4 +- .../application/FailureWebConfiguration.kt | 8 +- .../application/FrontendWebConfiguration.kt | 2 +- .../infrastructure/FrontendHandler.kt | 4 +- .../reposilite/maven/LookupService.kt | 22 ++-- .../reposilite/maven/ProxyService.kt | 3 +- .../maven/RepositorySecurityProvider.kt | 30 +++++ .../reposilite/maven/api/FileDetails.kt | 29 ++--- .../reposilite/maven/api/LookupRequest.kt | 2 +- .../application/MavenWebConfiguration.kt | 5 +- .../infrastructure/DeploymentEndpoint.kt | 15 ++- .../maven/infrastructure/LookupApiEndpoint.kt | 6 +- .../maven/infrastructure/LookupEndpoint.kt | 10 +- .../application/StatisticsWebConfiguration.kt | 2 +- .../infrastructure/StatisticsHandler.kt | 4 +- .../FileSystemStorageProvider.kt | 15 +-- .../infrastructure/S3StorageProvider.kt | 21 ++-- .../panda_lang/reposilite/web/ContextDsl.kt | 5 +- .../reposilite/web/WebExtensions.kt | 52 +++++++-- .../panda_lang/reposilite/web/WebServer.kt | 13 +++ .../reposilite/web/api/RouteHandler.kt | 2 +- .../web/application/WebConfiguration.kt | 11 ++ .../JavalinServlet.kt} | 6 +- .../JavalinWebServer.kt} | 107 ++++++++++-------- .../src/main/resources/WEB-INF/web.xml | 2 +- 32 files changed, 274 insertions(+), 162 deletions(-) create mode 100644 reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/RepositorySecurityProvider.kt create mode 100644 reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebServer.kt create mode 100644 reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/WebConfiguration.kt rename reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/{application/HttpServletConfiguration.kt => infrastructure/JavalinServlet.kt} (89%) rename reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/{application/HttpServerConfiguration.kt => infrastructure/JavalinWebServer.kt} (56%) diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/Reposilite.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/Reposilite.kt index de42b3188..b628609b4 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/Reposilite.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/Reposilite.kt @@ -29,8 +29,8 @@ import org.panda_lang.reposilite.shared.CachedLogger import org.panda_lang.reposilite.shared.TimeUtils import org.panda_lang.reposilite.statistics.StatisticsFacade import org.panda_lang.reposilite.token.AccessTokenFacade -import org.panda_lang.reposilite.web.application.HttpServerConfiguration import org.panda_lang.reposilite.web.ReposiliteContextFactory +import org.panda_lang.reposilite.web.WebServer import org.panda_lang.utilities.commons.console.Effect import java.nio.file.Path import java.util.concurrent.atomic.AtomicBoolean @@ -42,6 +42,7 @@ class Reposilite( val configuration: Configuration, val workingDirectory: Path, val testEnv: Boolean, + val webServer: WebServer, val contextFactory: ReposiliteContextFactory, val failureFacade: FailureFacade, val authenticationFacade: AuthenticationFacade, @@ -49,11 +50,9 @@ class Reposilite( val consoleFacade: ConsoleFacade, val accessTokenFacade: AccessTokenFacade, val frontendFacade: FrontendFacade, - val statisitcsFacade: StatisticsFacade, + val statisticsFacade: StatisticsFacade, ) : Journalist { - internal val httpServer = HttpServerConfiguration(this, false) - val cachedLogger = CachedLogger(Channel.ALL, configuration.cachedLogSize) private val logger = AggregatedLogger(logger, cachedLogger) @@ -96,7 +95,7 @@ class Reposilite( try { logger.info("Binding server at ${configuration.hostname}::${configuration.port}") - httpServer.start(configuration) + webServer.start(this) Runtime.getRuntime().addShutdownHook(shutdownHook) logger.info("Done (" + TimeUtils.format(getUptime() / 1000.0) + "s)!") @@ -125,7 +124,7 @@ class Reposilite( logger.info("Shutting down ${configuration.hostname}::${configuration.port} ...") ReposiliteWebConfiguration.dispose(this) - httpServer.stop() + webServer.stop() } fun getUptime(): Long = diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/ReposiliteWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/ReposiliteWebConfiguration.kt index 428bce3d1..52c2a31b4 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/ReposiliteWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/ReposiliteWebConfiguration.kt @@ -16,6 +16,7 @@ package org.panda_lang.reposilite +import io.javalin.Javalin import net.dzikoysk.dynamiclogger.Journalist import org.panda_lang.reposilite.auth.application.AuthenticationWebConfiguration import org.panda_lang.reposilite.config.Configuration @@ -26,7 +27,8 @@ import org.panda_lang.reposilite.maven.application.MavenWebConfiguration import org.panda_lang.reposilite.statistics.application.StatisticsWebConfiguration import org.panda_lang.reposilite.token.application.AccessTokenWebConfiguration import org.panda_lang.reposilite.web.ReposiliteContextFactory -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.application.WebConfiguration import java.nio.file.Path object ReposiliteWebConfiguration { @@ -34,6 +36,7 @@ object ReposiliteWebConfiguration { fun createReposilite(journalist: Journalist, configuration: Configuration, workingDirectory: Path, testEnv: Boolean): Reposilite { val logger = journalist.logger + val webServer = WebConfiguration.createWebServer() val failureFacade = FailureWebConfiguration.createFacade(logger) val consoleFacade = ConsoleWebConfiguration.createFacade(logger, failureFacade) val mavenFacade = MavenWebConfiguration.createFacade(logger, failureFacade, configuration.repositories) @@ -48,6 +51,7 @@ object ReposiliteWebConfiguration { configuration = configuration, workingDirectory = workingDirectory, testEnv = testEnv, + webServer = webServer, failureFacade = failureFacade, contextFactory = contextFactory, authenticationFacade = authenticationFacade, @@ -55,7 +59,7 @@ object ReposiliteWebConfiguration { consoleFacade = consoleFacade, accessTokenFacade = accessTokenFacade, frontendFacade = frontendFacade, - statisitcsFacade = statisticFacade + statisticsFacade = statisticFacade ) } @@ -66,22 +70,28 @@ object ReposiliteWebConfiguration { // MavenWebConfiguration.initialize() // FrontendWebConfiguration.initialize() // MavenWebConfiguration.initialize() - StatisticsWebConfiguration.initialize(reposilite.statisitcsFacade, reposilite.consoleFacade) + StatisticsWebConfiguration.initialize(reposilite.statisticsFacade, reposilite.consoleFacade) AccessTokenWebConfiguration.initialize(reposilite.accessTokenFacade, reposilite.consoleFacade) } fun routing(reposilite: Reposilite): Collection = setOf( AuthenticationWebConfiguration.routing(reposilite.authenticationFacade), - ConsoleWebConfiguration.routing(reposilite.httpServer.javalin!!, reposilite), - FailureWebConfiguration.routing(reposilite.httpServer.javalin!!, reposilite.failureFacade), + ConsoleWebConfiguration.routing(reposilite), + FailureWebConfiguration.routing(), FrontendWebConfiguration.routing(reposilite.frontendFacade), MavenWebConfiguration.routing(reposilite.contextFactory, reposilite.mavenFacade), - StatisticsWebConfiguration.routing(reposilite.statisitcsFacade), + StatisticsWebConfiguration.routing(reposilite.statisticsFacade), // AccessTokenWebConfiguration.routing(), ) .flatten() + // TOFIX: Remove dependency on infrastructure details + fun javalin(reposilite: Reposilite, javalin: Javalin) { + ConsoleWebConfiguration.javalin(javalin, reposilite) + FailureWebConfiguration.javalin(javalin, reposilite.failureFacade) + } + fun dispose(reposilite: Reposilite) { ConsoleWebConfiguration.dispose(reposilite.consoleFacade) } diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/application/AuthenticationWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/application/AuthenticationWebConfiguration.kt index 8487de9ab..a70f2a004 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/application/AuthenticationWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/application/AuthenticationWebConfiguration.kt @@ -24,7 +24,7 @@ import org.panda_lang.reposilite.auth.infrastructure.AuthenticationEndpoint import org.panda_lang.reposilite.auth.infrastructure.PostAuthHandler import org.panda_lang.reposilite.maven.MavenFacade import org.panda_lang.reposilite.token.AccessTokenFacade -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler internal object AuthenticationWebConfiguration { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/AuthenticationEndpoint.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/AuthenticationEndpoint.kt index 9c07b1ddd..bf9366916 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/AuthenticationEndpoint.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/AuthenticationEndpoint.kt @@ -24,8 +24,8 @@ import io.javalin.http.Context import org.panda_lang.reposilite.auth.AuthenticationFacade import org.panda_lang.reposilite.auth.api.AuthenticationResponse import org.panda_lang.reposilite.failure.api.ErrorResponse -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.GET +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.api.RouteMethod.GET private const val ROUTE = "/api/auth" diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/PostAuthHandler.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/PostAuthHandler.kt index 832b18030..6124ac3d8 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/PostAuthHandler.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/auth/infrastructure/PostAuthHandler.kt @@ -17,8 +17,8 @@ package org.panda_lang.reposilite.auth.infrastructure import io.javalin.http.Context import io.javalin.http.HttpCode -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.AFTER +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.api.RouteMethod.AFTER private const val WWW_AUTHENTICATE = "www-authenticate" private const val WWW_BASIC_REALM = """Basic realm="Reposilite", charset="UTF-8" """ diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/StatusCommand.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/StatusCommand.kt index f3b1643bf..2a6ce4f36 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/StatusCommand.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/StatusCommand.kt @@ -39,7 +39,7 @@ internal class StatusCommand( else getVersion() output.add("Reposilite $VERSION Status") - output.add(" Active: $GREEN_BOLD${reposilite.httpServer.isAlive()}$RESET") + output.add(" Active: $GREEN_BOLD${reposilite.webServer.isAlive()}$RESET") output.add(" Uptime: ${format(reposilite.getUptime() / 1000.0 / 60.0)}min") output.add(" Memory usage of process: ${memoryUsage()}") output.add(" Exceptions: ${reposilite.failureFacade.getFailures().size}") diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/application/ConsoleWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/application/ConsoleWebConfiguration.kt index 4f9462f49..0ea1785e2 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/application/ConsoleWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/application/ConsoleWebConfiguration.kt @@ -27,7 +27,7 @@ import org.panda_lang.reposilite.console.VersionCommand import org.panda_lang.reposilite.console.infrastructure.CliEndpoint import org.panda_lang.reposilite.console.infrastructure.RemoteExecutionEndpoint import org.panda_lang.reposilite.failure.FailureFacade -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler private const val REMOTE_VERSION = "https://repo.panda-lang.org/org/panda-lang/reposilite/latest" @@ -51,12 +51,13 @@ internal object ConsoleWebConfiguration { } } - fun routing(javalin: Javalin, reposilite: Reposilite): List { - javalin.ws("/api/cli", CliEndpoint(reposilite.contextFactory, reposilite.authenticationFacade, reposilite.consoleFacade, reposilite.cachedLogger)) - - return listOf( + fun routing(reposilite: Reposilite): List = + listOf( RemoteExecutionEndpoint(reposilite.contextFactory, reposilite.consoleFacade) ) + + fun javalin(javalin: Javalin, reposilite: Reposilite) { + javalin.ws("/api/cli", CliEndpoint(reposilite.contextFactory, reposilite.authenticationFacade, reposilite.consoleFacade, reposilite.cachedLogger)) } fun dispose(consoleFacade: ConsoleFacade) { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/infrastructure/RemoteExecutionEndpoint.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/infrastructure/RemoteExecutionEndpoint.kt index fe70f39c1..c84d11cdc 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/infrastructure/RemoteExecutionEndpoint.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/console/infrastructure/RemoteExecutionEndpoint.kt @@ -28,8 +28,8 @@ import org.panda_lang.reposilite.console.api.ExecutionResponse import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.reposilite.failure.api.errorResponse import org.panda_lang.reposilite.web.ReposiliteContextFactory -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.POST +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.api.RouteMethod.POST import org.panda_lang.reposilite.web.context private const val ROUTE = "/api/execute" diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/failure/application/FailureWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/failure/application/FailureWebConfiguration.kt index f5fb62ca1..bb1253eba 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/failure/application/FailureWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/failure/application/FailureWebConfiguration.kt @@ -22,7 +22,7 @@ import org.panda_lang.reposilite.console.ConsoleFacade import org.panda_lang.reposilite.failure.FailureFacade import org.panda_lang.reposilite.failure.FailuresCommand import org.panda_lang.reposilite.failure.infrastructure.FailureHandler -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler internal object FailureWebConfiguration { @@ -33,9 +33,11 @@ internal object FailureWebConfiguration { consoleFacade.registerCommand(FailuresCommand(failureFacade)) } - fun routing(javalin: Javalin, failureFacade: FailureFacade): List { + fun routing(): List = + listOf() + + fun javalin(javalin: Javalin, failureFacade: FailureFacade) { javalin.exception(Exception::class.java, FailureHandler(failureFacade)) - return emptyList() } } \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/application/FrontendWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/application/FrontendWebConfiguration.kt index 0f615b071..c3b8de494 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/application/FrontendWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/application/FrontendWebConfiguration.kt @@ -19,7 +19,7 @@ package org.panda_lang.reposilite.frontend.application import org.panda_lang.reposilite.config.Configuration import org.panda_lang.reposilite.frontend.FrontendFacade import org.panda_lang.reposilite.frontend.infrastructure.FrontendHandler -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler internal object FrontendWebConfiguration { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/infrastructure/FrontendHandler.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/infrastructure/FrontendHandler.kt index 89a8fceaf..b1cb244b7 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/infrastructure/FrontendHandler.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/frontend/infrastructure/FrontendHandler.kt @@ -20,8 +20,8 @@ import com.dzikoysk.openapi.annotations.OpenApi import com.dzikoysk.openapi.annotations.OpenApiResponse import io.javalin.http.Context import org.panda_lang.reposilite.frontend.FrontendFacade -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.GET +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.api.RouteMethod.GET internal class FrontendHandler(private val frontendFacade: FrontendFacade) : RouteHandler { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/LookupService.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/LookupService.kt index e0f0e847b..4df5574df 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/LookupService.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/LookupService.kt @@ -16,26 +16,34 @@ package org.panda_lang.reposilite.maven import io.javalin.http.HttpCode.NOT_FOUND +import io.javalin.http.HttpCode.UNAUTHORIZED import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.reposilite.failure.api.errorResponse import org.panda_lang.reposilite.maven.api.LookupRequest import org.panda_lang.reposilite.maven.api.LookupResponse import org.panda_lang.reposilite.maven.api.Repository +import org.panda_lang.reposilite.web.orNull import org.panda_lang.utilities.commons.function.Result +import java.nio.file.Path internal class LookupService( - private val repositoryService: RepositoryService + private val repositoryService: RepositoryService, + private val repositorySecurityProvider: RepositorySecurityProvider ) { fun lookup(lookupRequest: LookupRequest): Result { val repository = repositoryService.getRepository(lookupRequest.repository) ?: return errorResponse(NOT_FOUND, "Repository not found") + val gav = Path.of(lookupRequest.gav) - return repository.getFileDetails(lookupRequest.gav) - .flatMap { - repository.getFile(lookupRequest.gav) - .map { data -> Pair(it, data) } - } - .map { LookupResponse(it.first, it.second) } + if (repository.isDirectory(gav) && repositorySecurityProvider.canBrowseResource(lookupRequest.accessToken, repository, gav).not()) { + return errorResponse(UNAUTHORIZED, "Unauthorized indexing request") + } + else if (repositorySecurityProvider.canAccessResource(lookupRequest.accessToken, repository, gav).not()) { + return errorResponse(UNAUTHORIZED, "Unauthorized access request") + } + + return repository.getFileDetails(gav) + .map { LookupResponse(it, repository.getFile(lookupRequest.gav).orNull()) } } fun findAllRepositories(): Collection = diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/ProxyService.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/ProxyService.kt index ae6a9df9e..66fd871a2 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/ProxyService.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/ProxyService.kt @@ -25,6 +25,7 @@ import org.panda_lang.reposilite.failure.FailureFacade import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.reposilite.failure.api.errorResponse import org.panda_lang.reposilite.maven.api.FileDetails +import org.panda_lang.reposilite.maven.api.FileType import org.panda_lang.reposilite.maven.api.LookupResponse import org.panda_lang.reposilite.maven.api.Repository import org.panda_lang.reposilite.maven.api.RepositoryVisibility.PRIVATE @@ -123,7 +124,7 @@ internal class ProxyService( val contentLength = Option.of(remoteResponse.headers.contentLength).orElseGet(0L) val path = remoteUri.split("/").toTypedArray() - val fileDetails = FileDetails(FileDetails.FILE, path.last(), "", remoteResponse.contentType, contentLength) + val fileDetails = FileDetails(FileType.FILE, path.last(), remoteResponse.contentType, contentLength) val lookupResponse = LookupResponse(fileDetails, remoteResponse.content) Result.ok(lookupResponse) diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/RepositorySecurityProvider.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/RepositorySecurityProvider.kt new file mode 100644 index 000000000..1cf62125a --- /dev/null +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/RepositorySecurityProvider.kt @@ -0,0 +1,30 @@ +package org.panda_lang.reposilite.maven + +import org.panda_lang.reposilite.maven.api.Repository +import org.panda_lang.reposilite.maven.api.RepositoryVisibility.HIDDEN +import org.panda_lang.reposilite.maven.api.RepositoryVisibility.PRIVATE +import org.panda_lang.reposilite.maven.api.RepositoryVisibility.PUBLIC +import org.panda_lang.reposilite.token.api.AccessToken +import org.panda_lang.reposilite.token.api.Permission.READ +import java.nio.file.Path + +internal class RepositorySecurityProvider { + + fun canAccessResource(accessToken: AccessToken?, repository: Repository, gav: Path): Boolean = + when (repository.visibility) { + PUBLIC -> true + HIDDEN -> true + PRIVATE -> hasPermissionTo(accessToken, gav) + } + + fun canBrowseResource(accessToken: AccessToken?, repository: Repository, gav: Path): Boolean = + when (repository.visibility) { + PUBLIC -> true + HIDDEN -> hasPermissionTo(accessToken, gav) + PRIVATE -> hasPermissionTo(accessToken, gav) + } + + private fun hasPermissionTo(accessToken: AccessToken?, gav: Path): Boolean = + accessToken?.hasPermissionTo(gav.toString(), READ) ?: false + +} \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/FileDetails.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/FileDetails.kt index 3af2c4f6b..0806d3def 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/FileDetails.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/FileDetails.kt @@ -15,32 +15,25 @@ */ package org.panda_lang.reposilite.maven.api +import com.fasterxml.jackson.annotation.JsonIgnore import org.panda_lang.reposilite.shared.FilesUtils -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.util.Locale + +enum class FileType { + FILE, + DIRECTORY +} data class FileListResponse( val files: List ) data class FileDetails( - val type: String, + val type: FileType, val name: String, - val date: String, - val contentType: String, - val contentLength: Long + val contentType: String? = null, + val contentLength: Long? = null ) : Comparable { - companion object { - const val FILE = "file" - const val DIRECTORY = "directory" - - val DATE_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy") - .withLocale(Locale.getDefault()) - .withZone(ZoneId.systemDefault()) - } - override fun compareTo(other: FileDetails): Int { var result = type.compareTo(other.type) @@ -51,10 +44,8 @@ data class FileDetails( return result } + @JsonIgnore fun isReadable(): Boolean = FilesUtils.isReadable(name) - fun isDirectory(): Boolean = - DIRECTORY == type - } \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/LookupRequest.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/LookupRequest.kt index ccebe5028..3dc29159f 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/LookupRequest.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/api/LookupRequest.kt @@ -28,5 +28,5 @@ data class LookupRequest( class LookupResponse( val fileDetails: FileDetails, - val data: InputStream + val data: InputStream? ) \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/application/MavenWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/application/MavenWebConfiguration.kt index 9aa223fe1..85fb1fba1 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/application/MavenWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/application/MavenWebConfiguration.kt @@ -23,19 +23,20 @@ import org.panda_lang.reposilite.maven.DeploymentService import org.panda_lang.reposilite.maven.LookupService import org.panda_lang.reposilite.maven.MavenFacade import org.panda_lang.reposilite.maven.MetadataService +import org.panda_lang.reposilite.maven.RepositorySecurityProvider import org.panda_lang.reposilite.maven.RepositoryServiceFactory import org.panda_lang.reposilite.maven.infrastructure.DeploymentEndpoint import org.panda_lang.reposilite.maven.infrastructure.IndexEndpoint import org.panda_lang.reposilite.maven.infrastructure.LookupEndpoint import org.panda_lang.reposilite.web.ReposiliteContextFactory -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler internal object MavenWebConfiguration { fun createFacade(journalist: Journalist, failureFacade: FailureFacade, repositoriesConfiguration: Map): MavenFacade { val repositoryService = RepositoryServiceFactory.createRepositoryService(journalist, repositoriesConfiguration) val metadataService = MetadataService(failureFacade) - val lookupService = LookupService(repositoryService) + val lookupService = LookupService(repositoryService, RepositorySecurityProvider()) val deployService = DeploymentService(journalist, repositoryService, metadataService) return MavenFacade(journalist, metadataService, lookupService, deployService) diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/DeploymentEndpoint.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/DeploymentEndpoint.kt index 504479111..862adea66 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/DeploymentEndpoint.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/DeploymentEndpoint.kt @@ -25,9 +25,9 @@ import io.javalin.plugin.openapi.annotations.ContentType.FORM_DATA_MULTIPART import org.panda_lang.reposilite.maven.MavenFacade import org.panda_lang.reposilite.maven.api.DeployRequest import org.panda_lang.reposilite.web.ReposiliteContextFactory -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.POST -import org.panda_lang.reposilite.web.RouteMethod.PUT +import org.panda_lang.reposilite.web.api.RouteMethod.POST +import org.panda_lang.reposilite.web.api.RouteMethod.PUT +import org.panda_lang.reposilite.web.api.RouteHandler import org.panda_lang.reposilite.web.context private const val ROUTE = "/:repositoryName/*" @@ -72,9 +72,12 @@ internal class DeploymentEndpoint( context.logger.debug("DEPLOY ${context.uri} from ${context.address}") authorized { - response = DeployRequest(parameter("repositoryName"), wildcard(), getSessionIdentifier(), context.input()) - .let { mavenFacade.deployArtifact(it) } - .onError { context.logger.debug("Cannot deploy artifact due to: ${it.message}") } + val request = DeployRequest(parameter("repositoryName"), wildcard(), getSessionIdentifier(), context.input()) + + response = mavenFacade.deployArtifact(request) + .onError { + context.logger.debug("Cannot deploy artifact due to: ${it.message}") + } } } diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupApiEndpoint.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupApiEndpoint.kt index 807b1b2c3..666adece2 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupApiEndpoint.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupApiEndpoint.kt @@ -27,9 +27,9 @@ import org.panda_lang.reposilite.maven.api.FileDetails import org.panda_lang.reposilite.maven.api.FileListResponse import org.panda_lang.reposilite.maven.api.LookupRequest import org.panda_lang.reposilite.web.ReposiliteContextFactory -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.GET -import org.panda_lang.reposilite.web.RouteMethod.HEAD +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.api.RouteMethod.GET +import org.panda_lang.reposilite.web.api.RouteMethod.HEAD import org.panda_lang.reposilite.web.context private const val ROUTE = "/api/:repositoryName/*" diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupEndpoint.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupEndpoint.kt index 817acad03..172814ad1 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupEndpoint.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/maven/infrastructure/LookupEndpoint.kt @@ -10,8 +10,8 @@ import io.javalin.http.Context import org.panda_lang.reposilite.maven.MavenFacade import org.panda_lang.reposilite.maven.api.LookupRequest import org.panda_lang.reposilite.web.ReposiliteContextFactory -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.GET +import org.panda_lang.reposilite.web.api.RouteMethod.GET +import org.panda_lang.reposilite.web.api.RouteHandler import org.panda_lang.reposilite.web.context import org.panda_lang.reposilite.web.resultAttachment import org.panda_lang.utilities.commons.function.Result @@ -60,7 +60,11 @@ internal class LookupEndpoint( val request = LookupRequest(parameter("repositoryName"), wildcard(), this?.getSessionIdentifier() ?: context.address, this?.accessToken) mavenFacade.lookup(request) - .peek { ctx.resultAttachment(it.fileDetails, it.data) } + .peek { + it.data?.let { data -> + ctx.resultAttachment(it.fileDetails, data) + } + } .onError { response = Result.error(it) } } } diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/application/StatisticsWebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/application/StatisticsWebConfiguration.kt index c3621fea8..19b8a4335 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/application/StatisticsWebConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/application/StatisticsWebConfiguration.kt @@ -22,7 +22,7 @@ import org.panda_lang.reposilite.statistics.StatisticsFacade import org.panda_lang.reposilite.statistics.StatsCommand import org.panda_lang.reposilite.statistics.infrastructure.SqlStatisticsRepository import org.panda_lang.reposilite.statistics.infrastructure.StatisticsHandler -import org.panda_lang.reposilite.web.RouteHandler +import org.panda_lang.reposilite.web.api.RouteHandler import java.util.concurrent.Executors import java.util.concurrent.TimeUnit.MINUTES diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/infrastructure/StatisticsHandler.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/infrastructure/StatisticsHandler.kt index e54799f38..51d86a488 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/infrastructure/StatisticsHandler.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/statistics/infrastructure/StatisticsHandler.kt @@ -20,8 +20,8 @@ import io.javalin.http.Context import org.panda_lang.reposilite.statistics.StatisticsFacade import org.panda_lang.reposilite.statistics.api.MAX_IDENTIFIER_LENGTH import org.panda_lang.reposilite.statistics.api.RecordType -import org.panda_lang.reposilite.web.RouteHandler -import org.panda_lang.reposilite.web.RouteMethod.BEFORE +import org.panda_lang.reposilite.web.api.RouteHandler +import org.panda_lang.reposilite.web.api.RouteMethod.BEFORE internal class StatisticsHandler(private val statisticsFacade: StatisticsFacade) : RouteHandler { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/FileSystemStorageProvider.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/FileSystemStorageProvider.kt index 13995e998..95d9646c1 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/FileSystemStorageProvider.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/FileSystemStorageProvider.kt @@ -20,6 +20,8 @@ import io.javalin.http.HttpCode import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.reposilite.failure.api.errorResponse import org.panda_lang.reposilite.maven.api.FileDetails +import org.panda_lang.reposilite.maven.api.FileType +import org.panda_lang.reposilite.maven.api.FileType.FILE import org.panda_lang.reposilite.shared.FilesUtils import org.panda_lang.reposilite.shared.FilesUtils.getMimeType import org.panda_lang.reposilite.storage.StorageProvider @@ -36,7 +38,6 @@ import java.nio.file.Path import java.nio.file.StandardOpenOption.CREATE import java.nio.file.StandardOpenOption.WRITE import java.nio.file.attribute.FileTime -import java.time.LocalDate import java.util.concurrent.atomic.AtomicLong import java.util.stream.Collectors @@ -128,9 +129,8 @@ internal abstract class FileSystemStorageProvider private constructor( Result.ok( FileDetails( - FileDetails.FILE, + FILE, file.fileName.toString(), - FileDetails.DATE_FORMAT.format(LocalDate.now()), getMimeType(file.toString(), "application/octet-stream"), bytesWritten ) @@ -158,13 +158,14 @@ internal abstract class FileSystemStorageProvider private constructor( errorResponse(HttpCode.NOT_FOUND, "File not found: $file") } else try { + val isDirectory = Files.isDirectory(file) + Result.ok( FileDetails( - if (Files.isDirectory(file)) FileDetails.DIRECTORY else FileDetails.FILE, + if (isDirectory) FileType.DIRECTORY else FileType.FILE, file.fileName.toString(), - FileDetails.DATE_FORMAT.format(Files.getLastModifiedTime(file).toInstant()), // TOFIX: Verify if #toInstant() is the best way to do this - getMimeType(file.fileName.toString(), "application/octet-stream"), - Files.size(file) + if (isDirectory) null else getMimeType(file.fileName.toString(), "application/octet-stream"), + if (isDirectory) null else Files.size(file) ) ) } catch (ioException: IOException) { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/S3StorageProvider.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/S3StorageProvider.kt index cfac8347b..32c68c989 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/S3StorageProvider.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/storage/infrastructure/S3StorageProvider.kt @@ -20,6 +20,8 @@ import io.javalin.http.HttpCode import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.reposilite.failure.api.errorResponse import org.panda_lang.reposilite.maven.api.FileDetails +import org.panda_lang.reposilite.maven.api.FileType.DIRECTORY +import org.panda_lang.reposilite.maven.api.FileType.FILE import org.panda_lang.reposilite.shared.FilesUtils.getMimeType import org.panda_lang.reposilite.storage.StorageProvider import org.panda_lang.reposilite.web.api.MimeTypes.MIME_OCTET_STREAM @@ -41,7 +43,6 @@ import java.io.InputStream import java.nio.file.Path import java.nio.file.Paths import java.nio.file.attribute.FileTime -import java.time.LocalDate internal class S3StorageProvider(private val bucket: String, region: String) : StorageProvider { @@ -65,9 +66,8 @@ internal class S3StorageProvider(private val bucket: String, region: String) : S Result.ok( FileDetails( - FileDetails.FILE, + FILE, file.fileName.toString(), - FileDetails.DATE_FORMAT.format(LocalDate.now()), getMimeType(file.fileName.toString(), MIME_OCTET_STREAM), bytes.size.toLong() ) @@ -96,9 +96,8 @@ internal class S3StorageProvider(private val bucket: String, region: String) : S Result.ok( FileDetails( - FileDetails.FILE, + FILE, file.fileName.toString(), - FileDetails.DATE_FORMAT.format(LocalDate.now()), getMimeType(file.fileName.toString(), MIME_OCTET_STREAM), length ) @@ -134,11 +133,8 @@ internal class S3StorageProvider(private val bucket: String, region: String) : S if (file.toString() == "") { return Result.ok( FileDetails( - FileDetails.DIRECTORY, - "", - "WHATEVER", - "application/octet-stream", - 0 + DIRECTORY, + file.fileName.toString(), ) ) } @@ -146,9 +142,8 @@ internal class S3StorageProvider(private val bucket: String, region: String) : S return head(file) ?.let { FileDetails( - FileDetails.FILE, + FILE, file.fileName.toString(), - FileDetails.DATE_FORMAT.format(LocalDate.now()), getMimeType(file.fileName.toString(), "application/octet-stream"), it.contentLength() ) @@ -227,7 +222,7 @@ internal class S3StorageProvider(private val bucket: String, region: String) : S override fun isDirectory(file: Path): Boolean = getFileDetails(file) - .map { it.isDirectory() } + .map { it.type == DIRECTORY } .orElseGet { false } /* with(getFile(file)) { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/ContextDsl.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/ContextDsl.kt index a1c58e281..a5eb05cb6 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/ContextDsl.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/ContextDsl.kt @@ -22,10 +22,7 @@ import org.panda_lang.reposilite.auth.Session import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.utilities.commons.function.Result -internal class ContextDsl( - private val ctx: Context, - val context: ReposiliteContext -) { +internal class ContextDsl(private val ctx: Context, val context: ReposiliteContext) { /** * JSON response to send at the end of the dsl call diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebExtensions.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebExtensions.kt index 3af22a7ee..113c9576b 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebExtensions.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebExtensions.kt @@ -17,16 +17,19 @@ package org.panda_lang.reposilite.web import io.javalin.http.Context +import org.apache.commons.io.IOUtils +import org.eclipse.jetty.server.HttpOutput import org.panda_lang.reposilite.failure.api.ErrorResponse import org.panda_lang.reposilite.maven.api.FileDetails import org.panda_lang.utilities.commons.function.Result import java.io.InputStream +import java.io.OutputStream /** * Extends Javalin's context with a support for [ErrorResponse] results */ fun Context.error(error: ErrorResponse): Context = - this.status(error.status).json(error) + status(error.status).json(error) fun Context.contentLength(length: Long): Context = also { res.setContentLengthLong(length) } @@ -34,14 +37,39 @@ fun Context.contentLength(length: Long): Context = fun Context.encoding(encoding: String): Context = also { res.characterEncoding = encoding } -fun Context.resultAttachment(fileDetailsResponse: FileDetails, data: InputStream): Context = - this.also { - if (method() != "HEAD") data.transferTo(res.outputStream) - else data.close() - } - .contentType(fileDetailsResponse.contentType) - .contentLength(fileDetailsResponse.contentLength) - .header("Content-Disposition", if (fileDetailsResponse.isReadable()) "" else """"attachment; filename="${fileDetailsResponse.name}" """) +fun Context.contentDisposition(disposition: String): Context = + header("Content-Disposition", disposition) + +fun Context.resultAttachment(fileDetailsResponse: FileDetails, data: InputStream): Context { + if (method() != "HEAD") { + data.transferLargeTo(res.outputStream) + } + else { + data.close() + } + + if (fileDetailsResponse.isReadable()) { + contentDisposition(""""attachment; filename="${fileDetailsResponse.name}" """) + } + + fileDetailsResponse.contentType?.also { contentType(it) } + fileDetailsResponse.contentLength?.also { contentLength(it) } + + return this +} + +fun InputStream.transferLargeTo(outputStream: OutputStream): Boolean = + if (outputStream.isProbablyOpen()) { + IOUtils.copyLarge(this, outputStream) + true + } + else false + +fun OutputStream.isProbablyOpen(): Boolean = + when (this) { + is HttpOutput -> !isClosed + else -> true + } /** * Project non-existing value of errored [Result] to simplify error handling by convenient way to match expected signatures. @@ -49,3 +77,9 @@ fun Context.resultAttachment(fileDetailsResponse: FileDetails, data: InputStream */ fun Result.projectToError(): Result = if (this.isErr) this.map { null } else throw IllegalArgumentException("") + +fun Result.orNull(): VALUE? = + this.orElseGet { null } + +fun VALUE.toOk(): Result = + Result.ok(this) \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebServer.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebServer.kt new file mode 100644 index 000000000..270282577 --- /dev/null +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/WebServer.kt @@ -0,0 +1,13 @@ +package org.panda_lang.reposilite.web + +import org.panda_lang.reposilite.Reposilite + +interface WebServer { + + fun start(reposilite: Reposilite) + + fun stop() + + fun isAlive(): Boolean + +} \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/api/RouteHandler.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/api/RouteHandler.kt index 83002ebff..200d7b073 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/api/RouteHandler.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/api/RouteHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.panda_lang.reposilite.web +package org.panda_lang.reposilite.web.api import io.javalin.http.Handler diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/WebConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/WebConfiguration.kt new file mode 100644 index 000000000..48d1ab926 --- /dev/null +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/WebConfiguration.kt @@ -0,0 +1,11 @@ +package org.panda_lang.reposilite.web.application + +import org.panda_lang.reposilite.web.WebServer +import org.panda_lang.reposilite.web.infrastructure.JavalinWebServer + +object WebConfiguration { + + fun createWebServer(): WebServer = + JavalinWebServer() + +} \ No newline at end of file diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/HttpServletConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/infrastructure/JavalinServlet.kt similarity index 89% rename from reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/HttpServletConfiguration.kt rename to reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/infrastructure/JavalinServlet.kt index 19e724c2c..a2314dfc9 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/HttpServletConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/infrastructure/JavalinServlet.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.panda_lang.reposilite.web.application +package org.panda_lang.reposilite.web.infrastructure import org.panda_lang.reposilite.ReposiliteLauncher import javax.servlet.annotation.MultipartConfig @@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletResponse @MultipartConfig @WebServlet(urlPatterns = ["/*"], name = "ReposiliteServlet", asyncSupported = true) -internal class HttpServletConfiguration : HttpServlet() { +internal class JavalinServlet : HttpServlet() { private val reposilite = ReposiliteLauncher.create("", "", /* servlet = true, */ false) @@ -39,7 +39,7 @@ internal class HttpServletConfiguration : HttpServlet() { } override fun service(req: HttpServletRequest, res: HttpServletResponse) { - reposilite.httpServer.javalin?.servlet()?.service(req, res) + // reposilite.httpServer.javalin?.servlet()?.service(req, res) } override fun destroy() { diff --git a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/HttpServerConfiguration.kt b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/infrastructure/JavalinWebServer.kt similarity index 56% rename from reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/HttpServerConfiguration.kt rename to reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/infrastructure/JavalinWebServer.kt index 2bd55e0ce..aee84f1d5 100644 --- a/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/application/HttpServerConfiguration.kt +++ b/reposilite-backend/src/main/kotlin/org/panda_lang/reposilite/web/infrastructure/JavalinWebServer.kt @@ -1,24 +1,12 @@ -/* - * Copyright (c) 2021 dzikoysk - * - * 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. - */ -package org.panda_lang.reposilite.web.application +package org.panda_lang.reposilite.web.infrastructure import com.dzikoysk.openapi.javalin.OpenApiConfiguration import com.dzikoysk.openapi.javalin.OpenApiPlugin +import com.fasterxml.jackson.annotation.JsonInclude.Include +import com.fasterxml.jackson.databind.ObjectMapper import io.javalin.Javalin import io.javalin.core.JavalinConfig +import io.javalin.plugin.json.JavalinJackson import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.util.ssl.SslContextFactory @@ -26,29 +14,30 @@ import org.panda_lang.reposilite.Reposilite import org.panda_lang.reposilite.ReposiliteWebConfiguration import org.panda_lang.reposilite.VERSION import org.panda_lang.reposilite.config.Configuration -import org.panda_lang.reposilite.web.RouteMethod.AFTER -import org.panda_lang.reposilite.web.RouteMethod.BEFORE -import org.panda_lang.reposilite.web.RouteMethod.DELETE -import org.panda_lang.reposilite.web.RouteMethod.GET -import org.panda_lang.reposilite.web.RouteMethod.HEAD -import org.panda_lang.reposilite.web.RouteMethod.POST -import org.panda_lang.reposilite.web.RouteMethod.PUT - -internal class HttpServerConfiguration internal constructor( - private val reposilite: Reposilite, - private val servlet: Boolean -) { - - var javalin: Javalin? = null - private set - - fun start(configuration: Configuration) { - val javalin = create(configuration) +import org.panda_lang.reposilite.web.WebServer +import org.panda_lang.reposilite.web.api.RouteMethod.AFTER +import org.panda_lang.reposilite.web.api.RouteMethod.BEFORE +import org.panda_lang.reposilite.web.api.RouteMethod.DELETE +import org.panda_lang.reposilite.web.api.RouteMethod.GET +import org.panda_lang.reposilite.web.api.RouteMethod.HEAD +import org.panda_lang.reposilite.web.api.RouteMethod.POST +import org.panda_lang.reposilite.web.api.RouteMethod.PUT + +internal class JavalinWebServer : WebServer { + + private val servlet = false + private var javalin: Javalin? = null + + override fun start(reposilite: Reposilite) { + val configuration = reposilite.configuration + + val javalin = create(reposilite, configuration) .events { listener -> listener.serverStopping { reposilite.logger.info("Server stopping...") } listener.serverStopped { reposilite.logger.info("Bye! Uptime: " + reposilite.getPrettyUptime()) } } - .also { this.javalin = it } + + ReposiliteWebConfiguration.javalin(reposilite, javalin) ReposiliteWebConfiguration.routing(reposilite).forEach { handler -> handler.methods.forEach { method -> @@ -69,21 +58,38 @@ internal class HttpServerConfiguration internal constructor( } } - private fun create(configuration: Configuration): Javalin = - if (servlet) - Javalin.createStandalone { configure(configuration, it) } - else - Javalin.create { configure(configuration, it) } - private fun configure(configuration: Configuration, config: JavalinConfig) { + private fun create(reposilite: Reposilite, configuration: Configuration): Javalin = + if (servlet) { + Javalin.createStandalone { configure(reposilite, configuration, it) } + } else { + Javalin.create { configure(reposilite, configuration, it) } + } + + private fun configure(reposilite: Reposilite, configuration: Configuration, config: JavalinConfig) { val server = Server() + configureJsonSerialization() + configureSSL(reposilite, configuration, config, server) + configureCors(config) + configureOpenApi(configuration, config) + configureDebug(reposilite, configuration, config) + + config.server { server } + } + + private fun configureJsonSerialization() { + val objectMapper = ObjectMapper() + objectMapper.setSerializationInclusion(Include.NON_NULL) + JavalinJackson.configure(objectMapper) + } + + private fun configureSSL(reposilite: Reposilite, configuration: Configuration, config: JavalinConfig, server: Server) { if (configuration.sslEnabled) { reposilite.logger.info("Enabling SSL connector at ::" + configuration.sslPort) val sslContextFactory: SslContextFactory = SslContextFactory.Server() - sslContextFactory.keyStorePath = - configuration.keyStorePath.replace("\${WORKING_DIRECTORY}", reposilite.workingDirectory.toAbsolutePath().toString()) + sslContextFactory.keyStorePath = configuration.keyStorePath.replace("\${WORKING_DIRECTORY}", reposilite.workingDirectory.toAbsolutePath().toString()) sslContextFactory.setKeyStorePassword(configuration.keyStorePassword) val sslConnector = ServerConnector(server, sslContextFactory) @@ -98,9 +104,13 @@ internal class HttpServerConfiguration internal constructor( } config.enforceSsl = configuration.enforceSsl + } + + private fun configureCors(config: JavalinConfig) { config.enableCorsForAllOrigins() - config.showJavalinBanner = false + } + private fun configureOpenApi(configuration: Configuration, config: JavalinConfig) { if (configuration.swagger) { val openApiConfiguration = OpenApiConfiguration() openApiConfiguration.title = configuration.title @@ -108,21 +118,22 @@ internal class HttpServerConfiguration internal constructor( openApiConfiguration.version = VERSION config.registerPlugin(OpenApiPlugin(openApiConfiguration)) } + } + private fun configureDebug(reposilite: Reposilite, configuration: Configuration, config: JavalinConfig) { if (configuration.debugEnabled) { // config.requestCacheSize = FilesUtils.displaySizeToBytesCount(System.getProperty("reposilite.requestCacheSize", "8MB")); // Reposilite.getLogger().debug("requestCacheSize set to " + config.requestCacheSize + " bytes"); reposilite.logger.info("Debug enabled") config.enableDevLogging() } - - config.server { server } } - fun stop(): Javalin? = + override fun stop() { javalin?.stop() + } - fun isAlive(): Boolean = + override fun isAlive(): Boolean = javalin?.server()?.server()?.isStarted ?: false } \ No newline at end of file diff --git a/reposilite-backend/src/main/resources/WEB-INF/web.xml b/reposilite-backend/src/main/resources/WEB-INF/web.xml index 4e2f644df..72c0de1bd 100644 --- a/reposilite-backend/src/main/resources/WEB-INF/web.xml +++ b/reposilite-backend/src/main/resources/WEB-INF/web.xml @@ -22,7 +22,7 @@ ReposiliteServlet - org.panda_lang.reposilite.web.application.HttpServletConfiguration + org.panda_lang.reposilite.web.infrastructure.JavalinServlet