Skip to content

Commit

Permalink
GH-552 Support repository listing and modify overview layout
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed Aug 19, 2021
1 parent f20c1c5 commit 1da5570
Show file tree
Hide file tree
Showing 31 changed files with 193 additions and 158 deletions.
101 changes: 16 additions & 85 deletions reposilite-backend/src/main/kotlin/com/reposilite/maven/MavenFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.reposilite.maven

import com.reposilite.maven.api.DeleteRequest
import com.reposilite.maven.api.DeployRequest
import com.reposilite.maven.api.DirectoryInfo
import com.reposilite.maven.api.DocumentInfo
import com.reposilite.maven.api.FileDetails
import com.reposilite.maven.api.LookupRequest
Expand All @@ -26,6 +27,7 @@ import com.reposilite.maven.api.Metadata
import com.reposilite.shared.getSimpleName
import com.reposilite.shared.toNormalizedPath
import com.reposilite.shared.toPath
import com.reposilite.token.api.AccessToken
import com.reposilite.web.http.ErrorResponse
import com.reposilite.web.http.errorResponse
import io.javalin.http.HttpCode
Expand All @@ -36,6 +38,7 @@ import io.javalin.http.HttpCode.UNAUTHORIZED
import net.dzikoysk.dynamiclogger.Journalist
import net.dzikoysk.dynamiclogger.Logger
import panda.std.Result
import panda.std.asSuccess
import java.nio.file.Path
import java.nio.file.Paths

Expand All @@ -52,6 +55,10 @@ class MavenFacade internal constructor(
}

suspend fun findFile(lookupRequest: LookupRequest): Result<out FileDetails, ErrorResponse> {
if (lookupRequest.repository == null) {
return findRepositories(lookupRequest.accessToken).asSuccess()
}

val repository = repositoryService.getRepository(lookupRequest.repository) ?: return errorResponse(NOT_FOUND, "Repository not found")
val gav = lookupRequest.gav.toPath()

Expand All @@ -73,11 +80,13 @@ class MavenFacade internal constructor(
internal fun saveMetadata(repository: String, gav: String, metadata: Metadata) =
metadataService.saveMetadata(repository, gav, metadata)

fun findVersions(lookupRequest: LookupRequest): Result<Collection<String>, ErrorResponse> =
metadataService.findVersions(lookupRequest)
fun findVersions(lookupRequest: LookupRequest): Result<List<String>, ErrorResponse> =
repositoryService.findRepository(lookupRequest.repository)
.flatMap { metadataService.findVersions(it, lookupRequest.gav) }

fun findLatest(lookupRequest: LookupRequest): Result<String, ErrorResponse> =
metadataService.findLatest(lookupRequest)
repositoryService.findRepository(lookupRequest.repository)
.flatMap { metadataService.findLatest(it, lookupRequest.gav) }

fun deployFile(deployRequest: DeployRequest): Result<DocumentInfo, ErrorResponse> {
val repository = repositoryService.getRepository(deployRequest.repository) ?: return errorResponse(NOT_FOUND, "Repository not found")
Expand Down Expand Up @@ -106,91 +115,13 @@ class MavenFacade internal constructor(
return repository.removeFile(path)
}

fun findRepositories(accessToken: AccessToken?): DirectoryInfo =
repositoryService.getRootDirectory(accessToken)

fun getRepositories(): Collection<Repository> =
repositoryService.getRepositories()

override fun getLogger(): Logger =
journalist.logger

}

/*
fun exists(context: ReposiliteContext): Boolean {
val uri: String = context.uri
val result = repositoryAuthenticator.authDefaultRepository(context.header, uri)
if (result.isErr) {
// Maven requests maven-metadata.xml file during deploy for snapshot releases without specifying credentials
// https://github.com/dzikoysk/reposilite/issues/184
return if (uri.contains("-SNAPSHOT") && uri.endsWith(METADATA_FILE)) {
false
} else false
}
val path = result.get().key
// discard invalid requests (less than 'group/(artifact OR metadata)')
if (path.nameCount < 2) {
return false
}
val repository = result.get().value
return repository.exists(path)
}
fun find(context: ReposiliteContext): Result<LookupResponse, ErrorResponse> {
val uri: String = context.uri
val result = repositoryAuthenticator.authDefaultRepository(context.header, uri)
if (result.isErr) {
// Maven requests maven-metadata.xml file during deploy for snapshot releases without specifying credentials
// https://github.com/dzikoysk/reposilite/issues/184
return if (uri.contains("-SNAPSHOT") && uri.endsWith(METADATA_FILE)) {
ResponseUtils.error(HttpStatus.SC_NOT_FOUND, result.error.message)
} else Result.error(result.error)
}
var path = result.get().key
// discard invalid requests (less than 'group/(artifact OR metadata)')
if (path!!.nameCount < 2) {
return ResponseUtils.error(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, "Missing artifact identifier")
}
val repository = result.get().value
val requestedFileName = path.fileName.toString()
if (requestedFileName == "maven-metadata.xml") {
return repository.getFile(path).map { LookupResponse.of("text/xml", Arrays.toString(it)) }
}
// resolve requests for latest version of artifact
if (requestedFileName.equals("latest", ignoreCase = true)) {
val requestDirectory = path.parent
val versions: Result<List<Path>, ErrorResponse> = toSortedVersions(repository, requestDirectory)
if (versions.isErr) {
return versions.map { null }
}
val version = versions.get().firstOrNull()
?: return ResponseUtils.error(HttpStatus.SC_NOT_FOUND, "Latest version not found")
return Result.ok(LookupResponse.of("text/plain", version.fileName.toString()))
}
// resolve snapshot requests
if (requestedFileName.contains("-SNAPSHOT")) {
path = repositoryService.resolveSnapshot(repository, path)
if (path == null) {
return Result.error(ErrorResponse(HttpStatus.SC_NOT_FOUND, "Latest version not found"))
}
}
if (repository.isDirectory(path)) {
return ResponseUtils.error(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, "Directory access")
}
}
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import com.reposilite.maven.api.DocumentInfo
import com.reposilite.maven.api.LookupRequest
import com.reposilite.maven.api.METADATA_FILE
import com.reposilite.maven.api.Metadata
import com.reposilite.shared.safeResolve
Expand All @@ -36,16 +35,16 @@ internal class MetadataService(
.flatMap { it.putFile(gav.toPath().safeResolve(METADATA_FILE), xml.writeValueAsBytes(metadata)) }
}

fun findVersions(lookupRequest: LookupRequest): Result<Collection<String>, ErrorResponse> =
repositoryService.findRepository(lookupRequest.repository)
.flatMap { it.getFileDetails(lookupRequest.gav.toPath().safeResolve(METADATA_FILE)) }
fun findVersions(repository: Repository, gav: String): Result<List<String>, ErrorResponse> =
repository.getFileDetails(gav.toPath().safeResolve(METADATA_FILE))
.filter({ it is DocumentInfo }, { ErrorResponse(NOT_ACCEPTABLE, "Maven metadata file cannot be directory") })
.map { it as DocumentInfo }
.map { xml.readValue<Metadata>(it.content()) }
.map { it.versioning?.versions ?: emptyList() }
.map { VersionComparator.sortStrings(it) }

fun findLatest(lookupRequest: LookupRequest): Result<String, ErrorResponse> =
findVersions(lookupRequest).map { it.last() }
fun findLatest(repository: Repository, gav: String): Result<String, ErrorResponse> =
findVersions(repository, gav)
.map { it.last() }

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.reposilite.maven

import com.reposilite.maven.api.DirectoryInfo
import com.reposilite.maven.api.FileDetails
import com.reposilite.maven.api.RepositoryVisibility.HIDDEN
import com.reposilite.maven.api.RepositoryVisibility.PRIVATE
import com.reposilite.maven.api.RepositoryVisibility.PUBLIC
Expand All @@ -11,6 +13,12 @@ import java.nio.file.Path

internal class RepositorySecurityProvider {

fun canAccessRepository(accessToken: AccessToken?, repository: Repository): Boolean =
when(repository.visibility) {
PUBLIC -> true
HIDDEN, PRIVATE -> accessToken?.canSee("/${repository.name}") ?: false
}

fun canAccessResource(accessToken: AccessToken?, repository: Repository, gav: Path): Boolean =
when (repository.visibility) {
PUBLIC -> true
Expand All @@ -31,4 +39,12 @@ internal class RepositorySecurityProvider {
private fun hasPermissionTo(accessToken: AccessToken?, repository: Repository, gav: Path, permission: RoutePermission): Boolean =
accessToken?.hasPermissionTo("/" + repository.name + "/" + gav.toString().replace("\\", "/"), permission) ?: false

fun filterFile(accessToken: AccessToken?, fileDetails: FileDetails): FileDetails =
when(fileDetails) {
is DirectoryInfo -> {
fileDetails.filter { accessToken?.canSee(it.name) ?: false }
}
else -> fileDetails
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
*/
package com.reposilite.maven

import com.reposilite.maven.api.DirectoryInfo
import com.reposilite.maven.api.SimpleDirectoryInfo
import com.reposilite.token.api.AccessToken
import com.reposilite.web.http.ErrorResponse
import com.reposilite.web.http.errorResponse
import io.javalin.http.HttpCode.BAD_REQUEST
import io.javalin.http.HttpCode.NOT_FOUND
import net.dzikoysk.dynamiclogger.Journalist
import net.dzikoysk.dynamiclogger.Logger
Expand All @@ -25,15 +29,27 @@ import panda.std.asSuccess

internal class RepositoryService(
private val journalist: Journalist,
private val repositories: Map<String, Repository>
private val repositories: Map<String, Repository>,
private val securityProvider: RepositorySecurityProvider
) : Journalist {

fun findRepository(name: String): Result<Repository, ErrorResponse> =
getRepository(name)?.asSuccess() ?: errorResponse(NOT_FOUND, "Repository $name not found")
fun findRepository(name: String?): Result<Repository, ErrorResponse> =
name?.let {
getRepository(it)
?.asSuccess()
?: errorResponse(NOT_FOUND, "Repository $name not found")
}
?: errorResponse(BAD_REQUEST, "")

fun getRepository(name: String): Repository? =
repositories[name]

fun getRootDirectory(accessToken: AccessToken?): DirectoryInfo =
getRepositories()
.filter { securityProvider.canAccessRepository(accessToken, it) }
.map { SimpleDirectoryInfo(it.name) }
.let { DirectoryInfo("/", it) }

fun getRepositories(): Collection<Repository> =
repositories.values

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import com.reposilite.shared.exists
import com.reposilite.shared.getExtension
import com.reposilite.shared.getSimpleName
import com.reposilite.shared.type
import com.reposilite.web.http.ErrorResponse
import io.javalin.http.ContentType
import io.javalin.http.ContentType.APPLICATION_OCTET_STREAM
import com.reposilite.web.http.ErrorResponse
import panda.std.Result
import panda.std.asSuccess
import java.io.IOException
Expand All @@ -37,16 +37,12 @@ import kotlin.streams.toList

sealed class FileDetails(
val type: FileType,
val name: String
val name: String,
) : Comparable<FileDetails> {

override fun compareTo(other: FileDetails): Int =
type.compareTo(other.type).takeIf { it != 0 } ?: name.compareTo(other.name)

// @JsonIgnore
// fun isReadable(): Boolean =
// FilesUtils.isReadable(name)

}

const val UNKNOWN_LENGTH = -1L
Expand All @@ -70,7 +66,12 @@ class SimpleDirectoryInfo(
class DirectoryInfo(
name: String,
val files: List<FileDetails>
) : AbstractDirectoryInfo(name)
) : AbstractDirectoryInfo(name) {

fun filter(predicate: (FileDetails) -> Boolean): DirectoryInfo =
DirectoryInfo(name, files.filter { predicate(it) })

}

fun toFileDetails(file: Path): Result<out FileDetails, ErrorResponse> =
file.exists()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.reposilite.maven.api
import com.reposilite.token.api.AccessToken

data class LookupRequest(
val repository: String,
val repository: String?,
val gav: String,
val accessToken: AccessToken?
)
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ internal object MavenWebConfiguration {

fun createFacade(journalist: Journalist, workingDirectory: Path, remoteClient: RemoteClient, repositories: Map<String, RepositoryConfiguration>): MavenFacade {
val repositoryFactory = RepositoryFactory(journalist, workingDirectory)
val securityProvider = RepositorySecurityProvider()

val repositoryService = repositories
.mapValues { (repositoryName, repositoryConfiguration) -> repositoryFactory.createRepository(repositoryName, repositoryConfiguration) }
.let { RepositoryService(journalist, it) }
.let { RepositoryService(journalist, it, securityProvider) }

return MavenFacade(
journalist,
RepositorySecurityProvider(),
securityProvider,
repositoryService,
ProxyService(remoteClient),
MetadataService(repositoryService)
Expand Down
Loading

0 comments on commit 1da5570

Please sign in to comment.