Skip to content

Commit

Permalink
Reduce timeout on dependencies and return a degraded value when there…
Browse files Browse the repository at this point in the history
… is no access (#5219)

Co-authored-by: Simon Dumas <[email protected]>
  • Loading branch information
imsdu and Simon Dumas authored Nov 4, 2024
1 parent 68728b6 commit 4448ec7
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import ch.epfl.bluebrain.nexus.delta.kernel.dependency.ServiceDependency
import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors
import doobie.syntax.all._

import scala.concurrent.duration._

/**
* Describes the postgres [[ServiceDependency]] providing a way to extract the [[ServiceDescription]] from a ''select
* version();'' SQL command
Expand All @@ -19,6 +21,7 @@ class PostgresServiceDependency(xas: Transactors) extends ServiceDependency {
.query[String]
.to[List]
.transact(xas.read)
.timeout(1.second)
.map {
case versionString :: _ =>
versionString match {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
package ch.epfl.bluebrain.nexus.delta.routes

import akka.http.scaladsl.server.Route
import cats.effect.IO
import cats.syntax.all._
import ch.epfl.bluebrain.nexus.delta.config.DescriptionConfig
import ch.epfl.bluebrain.nexus.delta.kernel.dependency.ComponentDescription.ServiceDescription.UnresolvedServiceDescription
import ch.epfl.bluebrain.nexus.delta.kernel.dependency.ComponentDescription.{PluginDescription, ServiceDescription}
import ch.epfl.bluebrain.nexus.delta.kernel.dependency.{ComponentDescription, ServiceDependency}
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution}
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder
import ch.epfl.bluebrain.nexus.delta.rdf.utils.JsonKeyOrdering
import ch.epfl.bluebrain.nexus.delta.routes.VersionRoutes.VersionBundle
import ch.epfl.bluebrain.nexus.delta.routes.VersionRoutes.{emtyVersionBundle, VersionBundle}
import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck
import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.AclAddress
import ch.epfl.bluebrain.nexus.delta.sdk.directives.AuthDirectives
import ch.epfl.bluebrain.nexus.delta.sdk.directives.DeltaDirectives._
import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller
import ch.epfl.bluebrain.nexus.delta.sdk.marshalling.{HttpResponseFields, RdfMarshalling}
import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, Name}
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.version
import io.circe.{Encoder, JsonObject}
import io.circe.syntax._
import io.circe.{Encoder, JsonObject}

import scala.collection.immutable.Iterable

Expand All @@ -37,14 +40,17 @@ class VersionRoutes(
) extends AuthDirectives(identities, aclCheck)
with RdfMarshalling {

private def fullOrDegraded(implicit caller: Caller) = aclCheck.authorizeFor(AclAddress.Root, version.read).flatMap {
case true => dependencies.traverse(_.serviceDescription).map(VersionBundle(main, _, plugins, env))
case false => IO.pure(emtyVersionBundle)
}

def routes: Route =
baseUriPrefix(baseUri.prefix) {
pathPrefix("version") {
extractCaller { implicit caller =>
(get & pathEndOrSingleSlash) {
authorizeFor(AclAddress.Root, version.read).apply {
emit(dependencies.traverse(_.serviceDescription).map(VersionBundle(main, _, plugins, env)))
}
emit(fullOrDegraded)
}
}
}
Expand All @@ -54,6 +60,13 @@ class VersionRoutes(

object VersionRoutes {

private val emtyVersionBundle = VersionBundle(
UnresolvedServiceDescription("delta"),
List.empty,
List.empty,
Name.unsafe("unknown")
)

final private[routes] case class VersionBundle(
main: ServiceDescription,
dependencies: Iterable[ServiceDescription],
Expand Down
13 changes: 0 additions & 13 deletions delta/app/src/test/resources/version-response.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.AclAddress
import ch.epfl.bluebrain.nexus.delta.sdk.identities.IdentitiesDummy
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller
import ch.epfl.bluebrain.nexus.delta.sdk.model.Name
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.{events, version}
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.version
import ch.epfl.bluebrain.nexus.delta.sdk.utils.BaseRouteSpec
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Authenticated, Group}

Expand All @@ -36,7 +36,10 @@ class VersionRoutesSpec extends BaseRouteSpec {

private val descriptionConfig = DescriptionConfig(Name.unsafe("delta"), Name.unsafe("dev"))

private val aclCheck = AclSimpleCheck().accepted
private val aclCheck = AclSimpleCheck(
(caller.subject, AclAddress.Root, Set(version.read))
).accepted

private lazy val routes = Route.seal(
VersionRoutes(
identities,
Expand All @@ -49,20 +52,44 @@ class VersionRoutesSpec extends BaseRouteSpec {

"The version route" should {

"fail fetching plugins information without version/read permission" in {
aclCheck.append(AclAddress.Root, Anonymous -> Set(events.read)).accepted
"return a default value without version/read permission" in {
Get("/v1/version") ~> routes ~> check {
response.shouldBeForbidden
}
}
val expected =
json"""
{
"@context" : "https://bluebrain.github.io/nexus/contexts/version.json",
"delta" : "unknown",
"dependencies" : {

"fetch plugins information" in {
aclCheck.append(AclAddress.Root, Anonymous -> Set(version.read), caller.subject -> Set(version.read)).accepted
val expected = jsonContentOf("version-response.json", "version" -> descriptionConfig.version)
Get("/v1/version") ~> routes ~> check {
},
"environment" : "unknown",
"plugins" : {

}
}
"""
response.status shouldEqual StatusCodes.OK
response.asJson shouldEqual expected
}
}

"fetch plugins information" in {
aclCheck.append(AclAddress.Root, Anonymous -> Set(version.read), caller.subject -> Set(version.read))
val expected =
json"""
{
"@context": "https://bluebrain.github.io/nexus/contexts/version.json",
"delta": "${descriptionConfig.version}",
"dependencies": {
"elasticsearch": "unknown",
"remoteStorage": "1.0.0"
},
"plugins": {
"pluginA": "1.0",
"pluginB": "2.0"
},
"environment": "dev"
} """

Get("/v1/version") ~> asAlice ~> routes ~> check {
response.status shouldEqual StatusCodes.OK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class BlazegraphClient(
def serviceDescription: IO[ServiceDescription] =
client
.fromEntityTo[ResolvedServiceDescription](Get(endpoint / "status"))
.timeout(5.seconds)
.timeout(1.second)
.redeem(_ => ServiceDescription.unresolved(serviceName), _.copy(name = serviceName))

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class ElasticSearchClient(client: HttpClient, endpoint: Uri, maxIndexPathLength:
def serviceDescription: IO[ServiceDescription] =
client
.fromJsonTo[ResolvedServiceDescription](Get(endpoint).withHttpCredentials)
.timeout(3.seconds)
.timeout(1.second)
.redeem(
_ => ServiceDescription.unresolved(serviceName),
_.copy(name = serviceName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ object RemoteDiskStorageClient {
.fromJsonTo[ResolvedServiceDescription](Get(baseUri.base))
.map(_.copy(name = serviceName))
.widen[ServiceDescription]
.timeout(3.seconds)
.timeout(1.second)
.recover(_ => ServiceDescription.unresolved(serviceName))

def exists(bucket: Label): IO[Unit] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
package ch.epfl.bluebrain.nexus.tests.kg

import akka.http.scaladsl.model.StatusCodes
import cats.effect.unsafe.implicits._
import ch.epfl.bluebrain.nexus.tests.iam.types.Permission
import ch.epfl.bluebrain.nexus.tests.BaseIntegrationSpec
import ch.epfl.bluebrain.nexus.tests.Identity.ServiceAccount
import ch.epfl.bluebrain.nexus.tests.kg.VersionSpec.VersionBundle
import ch.epfl.bluebrain.nexus.tests.{BaseIntegrationSpec, Identity}
import io.circe.generic.semiauto.deriveDecoder
import io.circe.{Decoder, Json}

class VersionSpec extends BaseIntegrationSpec {

"The /version endpoint" should {
s"be protected by ${Permission.Version.Read.value}" in {
deltaClient.get[Json]("/version", Identity.Anonymous) { (_, response) =>
response.status shouldEqual StatusCodes.Forbidden
}
}

"return the dependencies and plugin versions" in {
aclDsl.addPermissionAnonymous("/", Permission.Version.Read).unsafeRunSync()

deltaClient.get[Json]("/version", Identity.Anonymous) { (json, response) =>
deltaClient.get[Json]("/version", ServiceAccount) { (json, response) =>
response.status shouldEqual StatusCodes.OK
json.as[VersionBundle].rightValue
succeed
Expand Down

0 comments on commit 4448ec7

Please sign in to comment.