From eea3fc003372b09a3e26c2c645daa404aef6e358 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 19 Jan 2024 17:11:36 +0000 Subject: [PATCH] Add mandatory SLULA acceptance flag (#150) --- config/config.minimal.hocon | 5 +++ config/config.reference.hocon | 5 +++ src/main/resources/application.conf | 5 +++ .../iglu/server/Config.scala | 33 ++++++++++++++++--- .../snowplowanalytics/iglu/server/Main.scala | 8 +++++ src/test/resources/valid-dummy-config.conf | 3 ++ .../iglu/server/ConfigSpec.scala | 17 +++++++--- .../iglu/server/ServerSpec.scala | 13 +++++++- 8 files changed, 79 insertions(+), 10 deletions(-) diff --git a/config/config.minimal.hocon b/config/config.minimal.hocon index 7bc90a2..a8ef7a4 100644 --- a/config/config.minimal.hocon +++ b/config/config.minimal.hocon @@ -1,4 +1,9 @@ { + # Full license text available in LICENSE.md + "license": { + "accept": true + } + "database": { "host": "postgres" "dbname": "igludb" diff --git a/config/config.reference.hocon b/config/config.reference.hocon index f79e9a4..6009de7 100644 --- a/config/config.reference.hocon +++ b/config/config.reference.hocon @@ -1,4 +1,9 @@ { + # Full license text available in LICENSE.md + "license": { + "accept": true + } + # Http server settings "repoServer": { "interface": "0.0.0.0" diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 5ee411e..8ef932d 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -8,6 +8,11 @@ "iglu": { + "license": { + "accept": false + "accept": ${?ACCEPT_LIMITED_USE_LICENSE} + } + "repoServer": { "interface": "0.0.0.0" "port": 8080 diff --git a/src/main/scala/com/snowplowanalytics/iglu/server/Config.scala b/src/main/scala/com/snowplowanalytics/iglu/server/Config.scala index c98e455..c8a66d4 100644 --- a/src/main/scala/com/snowplowanalytics/iglu/server/Config.scala +++ b/src/main/scala/com/snowplowanalytics/iglu/server/Config.scala @@ -50,7 +50,8 @@ case class Config( swagger: Config.Swagger, superApiKey: Option[UUID], preTerminationPeriod: FiniteDuration, - preTerminationUnhealthy: Boolean + preTerminationUnhealthy: Boolean, + license: Config.License ) object Config { @@ -61,9 +62,12 @@ object Config { implicitly[Encoder[String]].contramap(_.toString) sealed trait ThreadPool extends Product with Serializable + object ThreadPool { - case object Global extends ThreadPool - case object Cached extends ThreadPool + case object Global extends ThreadPool + + case object Cached extends ThreadPool + case class Fixed(size: Int) extends ThreadPool implicit val threadPoolReader: ConfigReader[ThreadPool] = @@ -102,12 +106,15 @@ object Config { } sealed trait StorageConfig + object StorageConfig { /** Frequently used HikariCP settings */ sealed trait ConnectionPool extends Product with Serializable + object ConnectionPool { case class NoPool(threadPool: ThreadPool) extends ConnectionPool + case class Hikari( connectionTimeout: Option[FiniteDuration], maxLifetime: Option[FiniteDuration], @@ -205,7 +212,7 @@ object Config { * Configuration options for the Iglu server. * * @param interface The server's host. - * @param port The server's port. + * @param port The server's port. */ case class Http( interface: String, @@ -227,6 +234,13 @@ object Config { implicit val hstsConfigCirceEncoder: Encoder[Hsts] = deriveEncoder[Hsts] + case class License( + accept: Boolean + ) + + implicit val licenseConfigEncoder: Encoder[License] = + deriveEncoder[License] + implicit val pureWebhookReader: ConfigReader[Webhook] = ConfigReader.fromCursor { cur => for { objCur <- cur.asObjectCursor @@ -272,6 +286,17 @@ object Config { } yield webhooks } + implicit val pureLicenseReader: ConfigReader[License] = { + val truthy = Set("true", "yes", "on", "1") + ConfigReader.fromCursor { cur => + for { + objCur <- cur.asObjectCursor + acceptCur <- objCur.atKey("accept") + value <- acceptCur.asString + } yield License(truthy(value.toLowerCase)) + } + } + implicit val pureConfigReader: ConfigReader[Config] = deriveReader[Config] implicit val mainConfigCirceEncoder: Encoder[Config] = diff --git a/src/main/scala/com/snowplowanalytics/iglu/server/Main.scala b/src/main/scala/com/snowplowanalytics/iglu/server/Main.scala index 0f3a7a4..879dad0 100644 --- a/src/main/scala/com/snowplowanalytics/iglu/server/Main.scala +++ b/src/main/scala/com/snowplowanalytics/iglu/server/Main.scala @@ -20,6 +20,7 @@ object Main extends IOApp { val cli = for { command <- EitherT.fromEither[IO](Config.serverCommand.parse(args).leftMap(_.toString)) config <- EitherT.fromEither[IO](command.read) + _ <- EitherT.fromEither[IO](checkLicense(config)) result <- command match { case _: Config.ServerCommand.Run => EitherT.liftF[IO, String, ExitCode](Server.run(config)) @@ -33,4 +34,11 @@ object Main extends IOApp { case Left(cliError) => IO(System.err.println(cliError)).as(ExitCode.Error) } } + + def checkLicense(config: Config): Either[String, Unit] = + if (config.license.accept) Right(()) + else + Left( + "Please accept the terms of the Snowplow Limited Use License Agreement to proceed. See https://docs.snowplow.io/docs/pipeline-components-and-applications/iglu/iglu-repositories/iglu-server/reference/#license for more information on the license and how to configure this." + ) } diff --git a/src/test/resources/valid-dummy-config.conf b/src/test/resources/valid-dummy-config.conf index 0d135fd..3101607 100644 --- a/src/test/resources/valid-dummy-config.conf +++ b/src/test/resources/valid-dummy-config.conf @@ -7,6 +7,9 @@ # OF THE SOFTWARE, YOU AGREE TO THE TERMS OF SUCH LICENSE AGREEMENT. # This file (application.conf) contains all necessary configuration options for the Iglu Server. +license { + accept = yes +} # 'repoServer' contains configuration options for the repoServer - # interface and port on which the server will be running diff --git a/src/test/scala/com/snowplowanalytics/iglu/server/ConfigSpec.scala b/src/test/scala/com/snowplowanalytics/iglu/server/ConfigSpec.scala index 6fb500f..b7104bc 100644 --- a/src/test/scala/com/snowplowanalytics/iglu/server/ConfigSpec.scala +++ b/src/test/scala/com/snowplowanalytics/iglu/server/ConfigSpec.scala @@ -84,7 +84,8 @@ class ConfigSpec extends org.specs2.Specification { Config.Swagger("/custom/prefix"), None, 42.seconds, - true + true, + Config.License(false) ) val result = Config.serverCommand.parse(input.split(" ").toList).leftMap(_.toString).flatMap(_.read) result must beRight(expected) @@ -104,7 +105,8 @@ class ConfigSpec extends org.specs2.Specification { Config.Swagger(""), None, 1.seconds, - false + false, + Config.License(true) ) val result = Config.serverCommand.parse(input.split(" ").toList).leftMap(_.toString).flatMap(_.read) result must beRight(expected) @@ -139,7 +141,8 @@ class ConfigSpec extends org.specs2.Specification { Config.Swagger("/custom/prefix"), Some(UUID.fromString("a71aa7d9-6cde-40f7-84b1-046d65dedf9e")), 10.seconds, - true + true, + Config.License(true) ) val expected = json"""{ @@ -199,7 +202,10 @@ class ConfigSpec extends org.specs2.Specification { }, "superApiKey": "******", "preTerminationPeriod": "10 seconds", - "preTerminationUnhealthy": true + "preTerminationUnhealthy": true, + "license": { + "accept": true + } }""" input.asJson must beEqualTo(expected) @@ -231,7 +237,8 @@ class ConfigSpec extends org.specs2.Specification { Config.Swagger(""), None, 1.seconds, - false + false, + Config.License(false) ) val result = Config.serverCommand.parse(input.split(" ").toList).leftMap(_.toString).flatMap(_.read) result must beRight(expected) diff --git a/src/test/scala/com/snowplowanalytics/iglu/server/ServerSpec.scala b/src/test/scala/com/snowplowanalytics/iglu/server/ServerSpec.scala index e533af7..b81149d 100644 --- a/src/test/scala/com/snowplowanalytics/iglu/server/ServerSpec.scala +++ b/src/test/scala/com/snowplowanalytics/iglu/server/ServerSpec.scala @@ -205,7 +205,18 @@ object ServerSpec { true ) def config(hsts: Config.Hsts) = - Config(storageConfig, httpConfig(hsts), false, true, Nil, Config.Swagger(""), None, 10.seconds, false) + Config( + storageConfig, + httpConfig(hsts), + false, + true, + Nil, + Config.Swagger(""), + None, + 10.seconds, + false, + Config.License(true) + ) private def runServer(hsts: Config.Hsts) = Server.buildServer(config(hsts), IO.pure(true)).flatMap(_.resource) private val client = BlazeClientBuilder[IO](global).resource