diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/dependency/PostgresServiceDependency.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/dependency/PostgresServiceDependency.scala index 85347ee27f..855a034adf 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/dependency/PostgresServiceDependency.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/dependency/PostgresServiceDependency.scala @@ -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 @@ -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 { diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutes.scala index 6ef0d9b882..f9de1f9394 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutes.scala @@ -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 @@ -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) } } } @@ -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], diff --git a/delta/app/src/test/resources/version-response.json b/delta/app/src/test/resources/version-response.json deleted file mode 100644 index 0163d59905..0000000000 --- a/delta/app/src/test/resources/version-response.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "@context": "https://bluebrain.github.io/nexus/contexts/version.json", - "delta": "{{version}}", - "dependencies": { - "elasticsearch": "unknown", - "remoteStorage": "1.0.0" - }, - "plugins": { - "pluginA": "1.0", - "pluginB": "2.0" - }, - "environment": "dev" -} \ No newline at end of file diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutesSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutesSpec.scala index 4ccf3bc1eb..0d6ec9776a 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutesSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/VersionRoutesSpec.scala @@ -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} @@ -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, @@ -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 diff --git a/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClient.scala b/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClient.scala index f641b03cee..d99be01bc1 100644 --- a/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClient.scala +++ b/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClient.scala @@ -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)) /** diff --git a/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClient.scala b/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClient.scala index 5ab70669b6..2b7575eb0e 100644 --- a/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClient.scala +++ b/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClient.scala @@ -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) diff --git a/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/operations/remote/client/RemoteDiskStorageClient.scala b/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/operations/remote/client/RemoteDiskStorageClient.scala index 456e38b5cc..0bdf95dbb5 100644 --- a/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/operations/remote/client/RemoteDiskStorageClient.scala +++ b/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/operations/remote/client/RemoteDiskStorageClient.scala @@ -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] = { diff --git a/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/VersionSpec.scala b/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/VersionSpec.scala index 54ad611dc4..8ed95e2dbc 100644 --- a/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/VersionSpec.scala +++ b/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/VersionSpec.scala @@ -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