Skip to content

Commit

Permalink
GH-458 Respond with a file content in the lookup endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed Jun 26, 2021
1 parent e9fd8a8 commit 3434d97
Show file tree
Hide file tree
Showing 28 changed files with 350 additions and 223 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@ import org.panda_lang.reposilite.frontend.FrontendFacade
import org.panda_lang.reposilite.maven.MavenFacade
import org.panda_lang.reposilite.shared.CachedLogger
import org.panda_lang.reposilite.shared.TimeUtils
import org.panda_lang.reposilite.stats.StatisticsFacade
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.utilities.commons.console.Effect
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicBoolean

const val NAME = "Reposilite"
const val VERSION = "3.0.0-SNAPSHOT"

class Reposilite(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import org.panda_lang.reposilite.console.application.ConsoleWebConfiguration
import org.panda_lang.reposilite.failure.application.FailureWebConfiguration
import org.panda_lang.reposilite.frontend.application.FrontendWebConfiguration
import org.panda_lang.reposilite.maven.application.MavenWebConfiguration
import org.panda_lang.reposilite.stats.application.StatisticsWebConfiguration
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,28 @@
*/
package org.panda_lang.reposilite.maven

import io.javalin.http.HttpCode.NOT_FOUND
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.utilities.commons.function.Result

internal class LookupService(
private val repositoryService: RepositoryService
) {

fun lookup(lookupRequest: LookupRequest): Result<LookupResponse, ErrorResponse> {
val repository = repositoryService.getRepository(lookupRequest.repository) ?: return errorResponse(NOT_FOUND, "Repository not found")

return repository.getFileDetails(lookupRequest.gav)
.flatMap {
repository.getFile(lookupRequest.gav)
.map { data -> Pair(it, data) }
}
.map { LookupResponse(it.first, it.second) }
}

/*
fun exists(context: ReposiliteContext): Boolean {
val uri: String = context.uri
Expand Down Expand Up @@ -119,6 +137,6 @@ internal class LookupService(
Result.error(fileDetailsResult.error)
}
}
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,21 @@ import net.dzikoysk.dynamiclogger.Logger
import org.panda_lang.reposilite.failure.api.ErrorResponse
import org.panda_lang.reposilite.maven.api.DeployRequest
import org.panda_lang.reposilite.maven.api.FileDetailsResponse
import org.panda_lang.reposilite.maven.api.LookupRequest
import org.panda_lang.reposilite.maven.api.LookupResponse
import org.panda_lang.utilities.commons.function.Result

class MavenFacade internal constructor(
internal val journalist: Journalist,
internal val repositoryService: RepositoryService,
internal val metadataService: MetadataService,
internal val deployService: DeployService,
private val journalist: Journalist,
private val repositoryService: RepositoryService,
private val metadataService: MetadataService,
private val lookupService: LookupService,
private val deployService: DeployService,
) : Journalist {

fun lookup(lookupRequest: LookupRequest): Result<LookupResponse, ErrorResponse> =
lookupService.lookup(lookupRequest)

fun deployArtifact(deployRequest: DeployRequest): Result<FileDetailsResponse, ErrorResponse> =
deployService.deployArtifact(deployRequest)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import java.nio.file.Path
import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.Arrays
import java.util.Locale
import java.util.TreeSet

internal object MetadataUtils {

Expand Down Expand Up @@ -170,4 +172,33 @@ internal object MetadataUtils {
return true
}

/**
* Process uri applying following changes:
*
*
* * Remove root slash
* * Remove illegal path modifiers like .. and ~
*
*
* @param uri the uri to process
* @return the normalized uri
*/
fun normalizeUri(uri: String): String? {
var normalizedUri = uri

if (normalizedUri.contains("..") || normalizedUri.contains("~") || normalizedUri.contains(":") || normalizedUri.contains("\\")) {
return null
}

while (normalizedUri.contains("//")) {
normalizedUri = normalizedUri.replace("//", "/")
}

if (normalizedUri.startsWith("/")) {
normalizedUri = normalizedUri.substring(1)
}

return normalizedUri
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import com.google.api.client.http.HttpRequestFactory
import com.google.api.client.http.HttpResponse
import com.google.api.client.http.javanet.NetHttpTransport
import io.javalin.http.HttpCode
import org.apache.commons.io.IOUtils.copyLarge
import org.panda_lang.reposilite.ReposiliteException
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.FileDetailsResponse
import org.panda_lang.reposilite.maven.api.LookupResponse
import org.panda_lang.reposilite.maven.api.RepositoryVisibility.PRIVATE
import org.panda_lang.reposilite.storage.StorageProvider
import org.panda_lang.reposilite.web.ReposiliteContext
import org.panda_lang.utilities.commons.StringUtils
Expand All @@ -35,7 +35,7 @@ import org.panda_lang.utilities.commons.function.Result
import java.io.IOException
import java.net.SocketTimeoutException
import java.nio.file.Paths
import java.util.*
import java.util.Collections
import java.util.concurrent.CompletableFuture

internal class ProxyService(
Expand Down Expand Up @@ -67,7 +67,7 @@ internal class ProxyService(
return errorResponse(HttpCode.NOT_FOUND, "Unknown repository")
}

if (!proxyPrivate && repository.isPrivate()) {
if (!proxyPrivate && repository.visibility == PRIVATE) {
return errorResponse(HttpCode.NOT_FOUND, "Proxying is disabled in private repositories")
}

Expand Down Expand Up @@ -113,26 +113,24 @@ internal class ProxyService(

return if (responses.isNotEmpty()) {
val remoteResponse = responses[0]
val contentLength = Option.of(remoteResponse.headers.contentLength).orElseGet(0L)
val path = remoteUri.split("/").toTypedArray()
val fileDetails = FileDetailsResponse(FileDetailsResponse.FILE, path.last(), "", remoteResponse.contentType, contentLength)
val lookupResponse = LookupResponse.of(fileDetails)

if (context.method != "HEAD") {
if (storeProxied) {
return store(remoteUri, remoteResponse, context).map { lookupResponse }
}
else {
context.output { copyLarge(remoteResponse.content, it) }
return store(remoteUri, remoteResponse, context)
}
}

val contentLength = Option.of(remoteResponse.headers.contentLength).orElseGet(0L)
val path = remoteUri.split("/").toTypedArray()
val fileDetails = FileDetailsResponse(FileDetailsResponse.FILE, path.last(), "", remoteResponse.contentType, contentLength)
val lookupResponse = LookupResponse(fileDetails, remoteResponse.content)

Result.ok(lookupResponse)
}
else errorResponse(HttpCode.NOT_FOUND, "Artifact $uri not found")
}

private fun store(uri: String, remoteResponse: HttpResponse, context: ReposiliteContext): Result<FileDetailsResponse, ErrorResponse> {
private fun store(uri: String, remoteResponse: HttpResponse, context: ReposiliteContext): Result<LookupResponse, ErrorResponse> {
var uri = uri

if (storageProvider.isFull()) {
Expand All @@ -149,13 +147,11 @@ internal class ProxyService(
val proxiedFile = Paths.get(uri)

return try {
val result: Result<FileDetailsResponse, ErrorResponse> = storageProvider.putFile(proxiedFile, remoteResponse.content)

if (result.isOk) {
context.logger.info("Stored proxied $proxiedFile from ${remoteResponse.request.url}")
context.output { it.write(storageProvider.getFile(proxiedFile).get()) }
}
result
storageProvider.putFile(proxiedFile, remoteResponse.content)
.map {
context.logger.info("Stored proxied $proxiedFile from ${remoteResponse.request.url}")
LookupResponse(it, storageProvider.getFile(proxiedFile).get())
}
}
catch (ioException: IOException) {
errorResponse(HttpCode.UNPROCESSABLE_ENTITY, "Cannot process artifact")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
*/
package org.panda_lang.reposilite.maven

import io.javalin.http.HttpCode.NOT_FOUND
import org.panda_lang.reposilite.failure.api.ErrorResponse
import org.panda_lang.reposilite.failure.api.errorResponse
import org.panda_lang.reposilite.maven.api.FileDetailsResponse
import org.panda_lang.reposilite.maven.api.RepositoryVisibility
import org.panda_lang.reposilite.maven.api.RepositoryVisibility.PRIVATE
import org.panda_lang.reposilite.maven.api.RepositoryVisibility.PUBLIC
import org.panda_lang.reposilite.storage.StorageProvider
import org.panda_lang.reposilite.web.normalizeUri
import org.panda_lang.utilities.commons.function.Result
import java.io.InputStream
import java.nio.file.Path
Expand All @@ -39,33 +38,53 @@ class Repository internal constructor(
private val REPOSITORIES_PATH = Paths.get("repositories")
}

private fun <R> relativize(file: String, consumer: (Path) -> Result<R, ErrorResponse>): Result<R, ErrorResponse> =
relativize(file)
?.let { consumer(it) }
?: errorResponse(NOT_FOUND, "Invalid GAV")

override fun compare(path: Path, toPath: Path): Int =
relativize(path).compareTo(relativize(toPath))

fun isPublic(): Boolean =
visibility == PUBLIC

fun isPrivate(): Boolean =
visibility == PRIVATE
fun putFile(file: String, bytes: ByteArray): Result<FileDetailsResponse, ErrorResponse> =
relativize(file) { putFile(it, bytes) }

fun putFile(file: Path, bytes: ByteArray): Result<FileDetailsResponse, ErrorResponse> =
storageProvider.putFile(relativize(file), bytes)

fun putFile(file: String, inputStream: InputStream): Result<FileDetailsResponse, ErrorResponse> =
relativize(file) { putFile(it, inputStream) }

fun putFile(file: Path, inputStream: InputStream): Result<FileDetailsResponse, ErrorResponse> =
storageProvider.putFile(relativize(file), inputStream)

fun getFile(file: Path): Result<ByteArray, ErrorResponse> =
fun getFile(file: String): Result<InputStream, ErrorResponse> =
relativize(file) { getFile(it) }

fun getFile(file: Path): Result<InputStream, ErrorResponse> =
storageProvider.getFile(relativize(file))

fun getFileDetails(file: String): Result<FileDetailsResponse, ErrorResponse> =
relativize(file) { getFileDetails(it) }

fun getFileDetails(file: Path): Result<FileDetailsResponse, ErrorResponse> =
storageProvider.getFileDetails(relativize(file))

fun removeFile(file: String): Result<Unit, ErrorResponse> =
relativize(file) { removeFile(it) }

fun removeFile(file: Path): Result<Unit, ErrorResponse> =
storageProvider.removeFile(relativize(file))

fun getFiles(directory: String): Result<List<Path>, ErrorResponse> =
relativize(directory) { getFiles(directory) }

fun getFiles(directory: Path): Result<List<Path>, ErrorResponse> =
storageProvider.getFiles(relativize(directory))

fun getLastModifiedTime(file: String): Result<FileTime, ErrorResponse> =
relativize(file) { getLastModifiedTime(file) }

fun getLastModifiedTime(file: Path): Result<FileTime, ErrorResponse> =
storageProvider.getLastModifiedTime(relativize(file))

Expand Down Expand Up @@ -111,8 +130,6 @@ class Repository internal constructor(
}

fun relativize(gav: String): Path? =
normalizeUri(gav)
.map { relativize(Paths.get(it)) }
.orNull
MetadataUtils.normalizeUri(gav)?.let { relativize(Paths.get(it)) }

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*/
package org.panda_lang.reposilite.maven.api

import org.panda_lang.reposilite.shared.FilesUtils
import java.io.Serializable
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Locale

data class FileDetailsResponse(
val type: String,
Expand All @@ -27,11 +30,12 @@ data class FileDetailsResponse(
) : Serializable, Comparable<FileDetailsResponse> {

companion object {

const val FILE = "file"
const val DIRECTORY = "directory"
val DATE_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")

val DATE_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
.withLocale(Locale.getDefault())
.withZone(ZoneId.systemDefault())
}

override fun compareTo(other: FileDetailsResponse): Int {
Expand All @@ -44,6 +48,9 @@ data class FileDetailsResponse(
return result
}

fun isReadable(): Boolean =
FilesUtils.isReadable(name)

fun isDirectory(): Boolean =
DIRECTORY == type

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.maven.api

import org.panda_lang.reposilite.token.api.AccessToken

data class LookupRequest(
val repository: String,
val gav: String,
val by: String,
val accessToken: AccessToken?
)
Loading

0 comments on commit 3434d97

Please sign in to comment.