From 03aa2ef27f897192e868f592f87fb31dbabec065 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 13:32:04 +0200 Subject: [PATCH 01/16] Add cats to build, remove uscala --- build.sbt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index a665c01..5250fbc 100644 --- a/build.sbt +++ b/build.sbt @@ -2,9 +2,12 @@ import sbt.Keys._ name := "vault" -lazy val uscalaVersion = "0.5.1" +lazy val scalaVersion211 = "2.11.11" +lazy val scalaVersion212 = "2.12.4" + lazy val specs2Version = "3.8.8" -lazy val circeVersion = "0.7.0" +lazy val catsVersion = "1.0.0-RC1" +lazy val circeVersion = "0.9.0-M2" lazy val dispatchVersion = "0.11.3" lazy val startVaultTask = TaskKey[Unit]( "startVaultTask", @@ -38,7 +41,8 @@ val pomInfo = ( lazy val commonSettings = Seq( version := "0.4.2-SNAPSHOT", - scalaVersion := "2.11.11", + scalaVersion := scalaVersion211, + crossScalaVersions := Seq(scalaVersion211, scalaVersion212), organization := "janstenpickle.vault", pomExtra := pomInfo, autoAPIMappings := true, @@ -50,11 +54,9 @@ lazy val commonSettings = Seq( url("https://github.com/janstenpickle/scala-vault/blob/master/LICENSE") ), resolvers ++= Seq(Resolver.sonatypeRepo("releases"), Resolver.jcenterRepo), + libraryDependencies += "org.typelevel" %% "cats-core" % catsVersion, libraryDependencies ++= Seq( "net.databinder.dispatch" %% "dispatch-core" % dispatchVersion, - "org.uscala" %% "uscala-result" % uscalaVersion, - "org.uscala" %% "uscala-result-async" % uscalaVersion, - "org.uscala" %% "uscala-result-specs2" % uscalaVersion % "it,test", "org.specs2" %% "specs2-core" % specs2Version % "it,test", "org.specs2" %% "specs2-scalacheck" % specs2Version % "it,test", "org.specs2" %% "specs2-junit" % specs2Version % "it,test" @@ -77,7 +79,9 @@ lazy val commonSettings = Seq( "-unchecked", "-deprecation", "-feature", - "-language:implicitConversions"), + "-language:implicitConversions", + "-Ypartial-unification" + ), javacOptions in Compile ++= Seq( "-source", "1.8", "-target", "1.8", From 69535e0fdd74daa4c75dba2b4329667b5417e518 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 13:32:26 +0200 Subject: [PATCH 02/16] wip: Add intermediary Result type aliases with cats impl The purpose of this is to preserve the syntax of AsyncResult and Result as much as possible. To get compiled code faster. Next step is to remove the redundant String type parameter on the left side of the Either, and make it fixed. --- .../scala/janstenpickle/scala/Result.scala | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 core/src/main/scala/janstenpickle/scala/Result.scala diff --git a/core/src/main/scala/janstenpickle/scala/Result.scala b/core/src/main/scala/janstenpickle/scala/Result.scala new file mode 100644 index 0000000..c822344 --- /dev/null +++ b/core/src/main/scala/janstenpickle/scala/Result.scala @@ -0,0 +1,36 @@ +package janstenpickle.scala + +import cats.data.EitherT + +import scala.concurrent.{ExecutionContext, Future} +import cats.implicits._ + +object Result { + type Result[F, R] = Either[F, R] + + def pure[F, R](r: R): Either[F, R] = Either right r + + def fail[F, R](f: F): Either[F, R] = Either left f + + type AsyncResult[F, R] = Future[Result[F, R]] + + type AsyncEitherT[F, R] = cats.data.EitherT[Future, F, R] + + object AsyncResult { + def pure[F, R](r: R): AsyncResult[F, R] = { + Future.successful(Either right r) + } + } + + implicit class AsyncResultOps[F, R](f: AsyncResult[F, R]) { + def eiT: AsyncEitherT[F, R] = EitherT[Future, F, R](f) + } + + implicit class AsyncResultOpsEitherT[F, R](f: Either[F, R]) { + def eiT(implicit ec: ExecutionContext): AsyncEitherT[F, R] = EitherT.fromEither[Future](f) + } + + implicit class AsyncResultOpsAny[F, R](r: R) { + def eiT: AsyncEitherT[F, R] = EitherT.apply(Future.successful(pure(r))) + } +} From ae875e9864b969c6b77f5c34e4ee9bf84b2d2a3f Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 13:34:40 +0200 Subject: [PATCH 03/16] Migrate from uscala AsyncResult to cats backed AsyncResult --- .../janstenpickle/vault/auth/UserPass.scala | 2 +- .../janstenpickle/scala/syntax/syntax.scala | 88 ++++++++++--------- .../janstenpickle/vault/core/Secrets.scala | 19 ++-- .../vault/core/VaultConfig.scala | 4 +- .../janstenpickle/vault/manage/UserPass.scala | 2 +- .../janstenpickle/vault/manage/manage.scala | 8 +- 6 files changed, 68 insertions(+), 55 deletions(-) diff --git a/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala b/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala index daa4c6a..a3a8363 100644 --- a/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala +++ b/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala @@ -5,7 +5,7 @@ import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.vault.core.WSClient -import uscala.concurrent.result.AsyncResult +import janstenpickle.scala.Result._ import scala.concurrent.ExecutionContext diff --git a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala index 2413efd..be89764 100644 --- a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala +++ b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala @@ -6,16 +6,11 @@ import io.circe._ import io.circe.parser._ import io.circe.syntax._ import janstenpickle.vault.core.VaultConfig -import uscala.concurrent.result.AsyncResult -import uscala.result.Result +import janstenpickle.scala.Result._ +import cats.implicits._ import scala.concurrent.{ExecutionContext, Future} - -object ConversionSyntax { - implicit def toResult[A, B](xor: Either[A, B]): Result[A, B] = xor.fold( - Result.fail, Result.ok - ) -} +import scala.language.postfixOps object OptionSyntax { implicit class ToTuple[T](opt: Option[T]) { @@ -27,9 +22,7 @@ object OptionSyntax { object AsyncResultSyntax { implicit class FutureToAsyncResult[T](future: Future[T]) (implicit ec: ExecutionContext) { - def toAsyncResult: AsyncResult[String, T] = AsyncResult( - future.map(Result.ok) - ) + def toAsyncResult: AsyncResult[String, T] = ??? } implicit class ReqToAsyncResult(req: Req) @@ -48,52 +41,62 @@ object VaultConfigSyntax { implicit class RequestHelper(config: VaultConfig) { def authenticatedRequest(path: String)(req: Req => Req) - (implicit ec: ExecutionContext): AsyncResult[String, Req] = - config.token.map[Req](token => - req(config.wsClient.path(path).setHeader(VaultTokenHeader, token)) - ) + (implicit ec: ExecutionContext): AsyncResult[String, Req] ={ + val r = for { + token <- config.token.eiT + } yield req(config.wsClient.path(path).setHeader(VaultTokenHeader, token)) + r.value + } } } object JsonSyntax { - import ConversionSyntax._ implicit class JsonHandler(json: AsyncResult[String, Json]) { def extractFromJson[T](jsonPath: HCursor => ACursor = _.downArray) ( implicit decode: Decoder[T], ec: ExecutionContext - ): AsyncResult[String, T] = - json.flatMapR(j => decode.tryDecode( - jsonPath(j.hcursor) - ).leftMap(_.message)) + ): AsyncResult[String, T] = { + // json.flatMapR(j => decode.tryDecode( + // jsonPath(j.hcursor) + // ).leftMap(_.message)) + ??? + } + } } object ResponseSyntax { - import ConversionSyntax._ import JsonSyntax._ implicit class ResponseHandler(resp: AsyncResult[String, Response]) { def acceptStatusCodes(codes: Int*) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = - resp.flatMapR( - response => - if (codes.contains(response.getStatusCode)) { - Result.ok(response) - } - else { - Result.fail( - s"Received failure response from server:" + - s" ${response.getStatusCode}\n ${response.getResponseBody}" - ) - } - ) - - def extractJson(implicit ec: ExecutionContext): AsyncResult[String, Json] = - resp.flatMapR(response => - parse(response.getResponseBody).leftMap(_.message) - ) + (implicit ec: ExecutionContext): AsyncResult[String, Response] = { + //resp.flatMapR( + // response => + // if (codes.contains(response.getStatusCode)) { + // Result.ok(response) + // } + // else { + // Result.fail( + // s"Received failure response from server:" + + // s" ${response.getStatusCode}\n ${response.getResponseBody}" + // ) + // } + //) + + ??? + } + + def extractJson(implicit ec: ExecutionContext): + AsyncResult[String, Json] = { +// resp.flatMapR(response => +// parse(response.getResponseBody).leftMap(_.message) +// ) + ??? + } + def extractFromJson[T](jsonPath: HCursor => ACursor = _.downArray) ( @@ -108,8 +111,11 @@ object SyntaxRequest { implicit class ExecuteRequest(req: AsyncResult[String, Req]) (implicit ec: ExecutionContext) { - def execute: AsyncResult[String, Response] = - req.flatMapF(Http(_)) + def execute: AsyncResult[String, Response] = { + //req.flatMapF(Http(_)) + ??? + } + } implicit class HttpOps(req: Req) { diff --git a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala index 32f53a0..ce3a7af 100644 --- a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala +++ b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala @@ -4,18 +4,25 @@ import com.ning.http.client.Response import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ -import uscala.concurrent.result.AsyncResult -import uscala.result.Result +import janstenpickle.scala.Result._ +import cats.implicits._ import scala.concurrent.ExecutionContext // scalastyle:off magic.number case class Secrets(config: VaultConfig, backend: String) { def get(key: String, subKey: String = "value") - (implicit ec: ExecutionContext): AsyncResult[String, String] = - getAll(key).flatMapR(x => - Result.fromOption(x.get(subKey), - s"Cannot find sub-key $subKey in secret $key")) + (implicit ec: ExecutionContext): AsyncResult[String, String] = { + val r = for { + x <- getAll(key).eiT + r <- Either.fromOption( + x.get(subKey), + s"Cannot find sub-key $subKey in secret $key" + ).eiT + } yield r + r.value + } + def getAll(key: String) (implicit ec: ExecutionContext): AsyncResult[String, Map[String, String]] = diff --git a/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala b/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala index 67544a9..cd77af1 100644 --- a/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala +++ b/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala @@ -8,7 +8,7 @@ import io.circe.syntax._ import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ -import uscala.concurrent.result.AsyncResult +import janstenpickle.scala.Result._ import scala.concurrent.ExecutionContext @@ -50,7 +50,7 @@ object VaultConfig { def apply(wsClient: WSClient, token: String) (implicit ec: ExecutionContext): VaultConfig = - VaultConfig(wsClient, AsyncResult.ok[String, String](token)) + VaultConfig(wsClient, AsyncResult.pure(token)) } diff --git a/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala b/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala index 7e1211b..343fed6 100644 --- a/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala +++ b/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala @@ -6,7 +6,7 @@ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ import janstenpickle.vault.core.VaultConfig -import uscala.concurrent.result.AsyncResult +import janstenpickle.scala.Result._ import scala.concurrent.ExecutionContext diff --git a/manage/src/main/scala/janstenpickle/vault/manage/manage.scala b/manage/src/main/scala/janstenpickle/vault/manage/manage.scala index a681b8a..3cac21f 100644 --- a/manage/src/main/scala/janstenpickle/vault/manage/manage.scala +++ b/manage/src/main/scala/janstenpickle/vault/manage/manage.scala @@ -9,8 +9,8 @@ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ import janstenpickle.vault.core.VaultConfig import janstenpickle.vault.manage.Model._ -import uscala.concurrent.result.AsyncResult -import uscala.result.Result +import janstenpickle.scala.Result +import janstenpickle.scala.Result._ import scala.concurrent.ExecutionContext @@ -150,10 +150,10 @@ object Model { ) ) if (decoded.isEmpty) { - Result.fail(s"Could not find any valid rules in string: $ruleString") + Result fail s"Could not find any valid rules in string: $ruleString" } else { - Result.ok(decoded) + Result pure decoded } } } From 8857e5bcb14b5d0f2dd19a8a49231daf70a5d86a Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 13:34:52 +0200 Subject: [PATCH 04/16] wip: Temporarily comment tests --- .../janstenpickle/vault/auth/UserPassIT.scala | 156 ++++++++-------- .../janstenpickle/vault/core/SecretsIT.scala | 168 +++++++++--------- .../janstenpickle/vault/core/VaultSpec.scala | 9 +- .../janstenpickle/vault/manage/AuthIT.scala | 76 ++++---- .../janstenpickle/vault/manage/MountIT.scala | 166 ++++++++--------- .../janstenpickle/vault/manage/PolicyIT.scala | 114 ++++++------ .../vault/manage/UserPassIT.scala | 102 +++++------ .../janstenpickle/vault/manage/RuleSpec.scala | 102 +++++------ 8 files changed, 446 insertions(+), 447 deletions(-) diff --git a/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala b/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala index 7d9c8c7..0a1aa00 100644 --- a/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala +++ b/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala @@ -1,78 +1,78 @@ -package janstenpickle.vault.auth - -import janstenpickle.vault.core.VaultSpec -import janstenpickle.vault.manage.Auth -import org.scalacheck.{Gen, Prop} -import org.specs2.ScalaCheck -import org.specs2.matcher.MatchResult - -class UserPassIT extends VaultSpec with ScalaCheck { - import VaultSpec._ - - override def is = - s2""" - Can authenticate a user against a specific "client" path $authPass - Fails to authenticate a user $end - against a bad "client" path $badClient - with a non-existent username $badUser - with a bad password $badPassword - """ - - lazy val underTest = UserPass(config.wsClient) - lazy val authAdmin = Auth(config) - lazy val userAdmin = janstenpickle.vault.manage.UserPass(config) - - def setupClient(client: String) = authAdmin.enable("userpass", Some(client)) - .attemptRun(_.getMessage()) must beOk - - def setupUser(username: String, password: String, client: String) = - userAdmin.create(username, password, 30, None, client) - .attemptRun(_.getMessage()) - - def removeClient(client: String) = - authAdmin.disable(client).attemptRun(_.getMessage()) must beOk - - def authPass = test((username, password, client, ttl) => - setupClient(client) and - (setupUser(username, password, client) must beOk) and - (underTest.authenticate(username, password, ttl, client) - .attemptRun(_.getMessage()) must beOk) and - removeClient(client) - ) - - // TODO: test below may fail rarely (e.g. client is same as badClientName) - - def badClient = test{ (username, password, client, ttl) => - val badClientName = "nic-kim-cage-client" - setupClient(badClientName) and - (setupUser(username, password, client) must beFail) and - (underTest.authenticate(username, password, ttl, client) - .attemptRun(_.getMessage()) must beFail) and - removeClient(badClientName) - } - - def badUser = test{ (username, password, client, ttl) => - val badUserName = "nic-kim-cage-user" - setupClient(client) and - (setupUser(username, password, client) must beOk) and - (underTest.authenticate(badUserName, password, ttl, client) - .attemptRun(_.getMessage()) must beFail) and - removeClient(client) - } - - def badPassword = test{ (username, password, client, ttl) => - val badPasswordValue = "nic-kim-cage-password" - setupClient(client) and - (setupUser(username, password, client) must beOk) and - (underTest.authenticate(username, badPasswordValue, ttl, client) - .attemptRun(_.getMessage()) must beFail) and - removeClient(client) - } - - def test(op: (String, String, String, Int) => MatchResult[Any]) = - Prop.forAllNoShrink( - longerStrGen, - longerStrGen, - Gen.numStr.suchThat(_.nonEmpty), Gen.posNum[Int] - )(op) -} +//package janstenpickle.vault.auth +// +//import janstenpickle.vault.core.VaultSpec +//import janstenpickle.vault.manage.Auth +//import org.scalacheck.{Gen, Prop} +//import org.specs2.ScalaCheck +//import org.specs2.matcher.MatchResult +// +//class UserPassIT extends VaultSpec with ScalaCheck { +// import VaultSpec._ +// +// override def is = +// s2""" +// Can authenticate a user against a specific "client" path $authPass +// Fails to authenticate a user $end +// against a bad "client" path $badClient +// with a non-existent username $badUser +// with a bad password $badPassword +// """ +// +// lazy val underTest = UserPass(config.wsClient) +// lazy val authAdmin = Auth(config) +// lazy val userAdmin = janstenpickle.vault.manage.UserPass(config) +// +// def setupClient(client: String) = authAdmin.enable("userpass", Some(client)) +// .attemptRun(_.getMessage()) must beOk +// +// def setupUser(username: String, password: String, client: String) = +// userAdmin.create(username, password, 30, None, client) +// .attemptRun(_.getMessage()) +// +// def removeClient(client: String) = +// authAdmin.disable(client).attemptRun(_.getMessage()) must beOk +// +// def authPass = test((username, password, client, ttl) => +// setupClient(client) and +// (setupUser(username, password, client) must beOk) and +// (underTest.authenticate(username, password, ttl, client) +// .attemptRun(_.getMessage()) must beOk) and +// removeClient(client) +// ) +// +// // TODO: test below may fail rarely (e.g. client is same as badClientName) +// +// def badClient = test{ (username, password, client, ttl) => +// val badClientName = "nic-kim-cage-client" +// setupClient(badClientName) and +// (setupUser(username, password, client) must beFail) and +// (underTest.authenticate(username, password, ttl, client) +// .attemptRun(_.getMessage()) must beFail) and +// removeClient(badClientName) +// } +// +// def badUser = test{ (username, password, client, ttl) => +// val badUserName = "nic-kim-cage-user" +// setupClient(client) and +// (setupUser(username, password, client) must beOk) and +// (underTest.authenticate(badUserName, password, ttl, client) +// .attemptRun(_.getMessage()) must beFail) and +// removeClient(client) +// } +// +// def badPassword = test{ (username, password, client, ttl) => +// val badPasswordValue = "nic-kim-cage-password" +// setupClient(client) and +// (setupUser(username, password, client) must beOk) and +// (underTest.authenticate(username, badPasswordValue, ttl, client) +// .attemptRun(_.getMessage()) must beFail) and +// removeClient(client) +// } +// +// def test(op: (String, String, String, Int) => MatchResult[Any]) = +// Prop.forAllNoShrink( +// longerStrGen, +// longerStrGen, +// Gen.numStr.suchThat(_.nonEmpty), Gen.posNum[Int] +// )(op) +//} diff --git a/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala b/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala index 7cbdca1..3a8a090 100644 --- a/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala +++ b/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala @@ -1,84 +1,84 @@ -package janstenpickle.vault.core - -import org.scalacheck.Prop -import org.specs2.ScalaCheck - -class GenericIT extends SecretsTests { - override def backend: String = "secret" -} - -class CubbyHoleIT extends SecretsTests { - override def backend: String = "cubbyhole" -} - -trait SecretsTests extends VaultSpec with ScalaCheck { - import VaultSpec._ - - override def is = - s2""" - Can set a secret in vault $set - Can set and get a secret in vault $get - Can list keys $list - Can set multiple subkeys $setMulti - Can set and get multiple subKeys $getSetMulti - Cannot get non-existent key $failGet - Fails to perform actions on a non-vault server $failSetBadServer - Fails to perform actions with a bad token $failSetBadToken - """ - - def backend: String - - lazy val good = Secrets(config, backend) - lazy val badToken = Secrets(badTokenConfig, backend) - lazy val badServer = Secrets(badServerConfig, backend) - - def set = Prop.forAllNoShrink(strGen, strGen) { (key, value) => - good.set(key, value).attemptRun(_.getMessage()) must beOk - } - - def get = Prop.forAllNoShrink(strGen, strGen) { (key, value) => - (good.set(key, value).attemptRun(_.getMessage()) must beOk) and - (good.get(key).attemptRun(_.getMessage()) must beOk.like { - case a => a === value - }) - } - - def list = Prop.forAllNoShrink(strGen, strGen, strGen) { (key1, key2, value) => - (good.set(key1, value).attemptRun(_.getMessage()) must beOk) and - (good.set(key2, value).attemptRun(_.getMessage()) must beOk) and - (good.list.attemptRun(_.getMessage()) must beOk[List[String]].like { - case a => a must containAllOf(Seq(key1, key2)) - }) - } - - def setMulti = Prop.forAllNoShrink(strGen, strGen, strGen, strGen) { - (key1, key2, value1, value2) => - good.set( - "nicolas-cage", - Map(key1 -> value1, key2 -> value2) - ).attemptRun(_.getMessage()) must beOk - } - - def getSetMulti = Prop.forAllNoShrink( - strGen, strGen, strGen, strGen, strGen - ) { (key1, key2, value1, value2, mainKey) => - val testData = Map(key1 -> value1, key2 -> value2) - (good.set(mainKey, testData).attemptRun(_.getMessage()) must beOk) and - (good.getAll(mainKey).attemptRun(_.getMessage()) must beOk.like { - case a => a === testData - }) - } - - def failGet = good.get("john").attemptRun(_.getMessage()) must beFail. - like { case err => - err must contain("Received failure response from server: 404") - } - - def failSetBadServer = badServer.set( - "nic", "cage" - ).attemptRun(_.getMessage()) must beFail - - def failSetBadToken = badToken.set( - "nic", "cage" - ).attemptRun(_.getMessage()) must beFail -} +//package janstenpickle.vault.core +// +//import org.scalacheck.Prop +//import org.specs2.ScalaCheck +// +//class GenericIT extends SecretsTests { +// override def backend: String = "secret" +//} +// +//class CubbyHoleIT extends SecretsTests { +// override def backend: String = "cubbyhole" +//} +// +//trait SecretsTests extends VaultSpec with ScalaCheck { +// import VaultSpec._ +// +// override def is = +// s2""" +// Can set a secret in vault $set +// Can set and get a secret in vault $get +// Can list keys $list +// Can set multiple subkeys $setMulti +// Can set and get multiple subKeys $getSetMulti +// Cannot get non-existent key $failGet +// Fails to perform actions on a non-vault server $failSetBadServer +// Fails to perform actions with a bad token $failSetBadToken +// """ +// +// def backend: String +// +// lazy val good = Secrets(config, backend) +// lazy val badToken = Secrets(badTokenConfig, backend) +// lazy val badServer = Secrets(badServerConfig, backend) +// +// def set = Prop.forAllNoShrink(strGen, strGen) { (key, value) => +// good.set(key, value).attemptRun(_.getMessage()) must beOk +// } +// +// def get = Prop.forAllNoShrink(strGen, strGen) { (key, value) => +// (good.set(key, value).attemptRun(_.getMessage()) must beOk) and +// (good.get(key).attemptRun(_.getMessage()) must beOk.like { +// case a => a === value +// }) +// } +// +// def list = Prop.forAllNoShrink(strGen, strGen, strGen) { (key1, key2, value) => +// (good.set(key1, value).attemptRun(_.getMessage()) must beOk) and +// (good.set(key2, value).attemptRun(_.getMessage()) must beOk) and +// (good.list.attemptRun(_.getMessage()) must beOk[List[String]].like { +// case a => a must containAllOf(Seq(key1, key2)) +// }) +// } +// +// def setMulti = Prop.forAllNoShrink(strGen, strGen, strGen, strGen) { +// (key1, key2, value1, value2) => +// good.set( +// "nicolas-cage", +// Map(key1 -> value1, key2 -> value2) +// ).attemptRun(_.getMessage()) must beOk +// } +// +// def getSetMulti = Prop.forAllNoShrink( +// strGen, strGen, strGen, strGen, strGen +// ) { (key1, key2, value1, value2, mainKey) => +// val testData = Map(key1 -> value1, key2 -> value2) +// (good.set(mainKey, testData).attemptRun(_.getMessage()) must beOk) and +// (good.getAll(mainKey).attemptRun(_.getMessage()) must beOk.like { +// case a => a === testData +// }) +// } +// +// def failGet = good.get("john").attemptRun(_.getMessage()) must beFail. +// like { case err => +// err must contain("Received failure response from server: 404") +// } +// +// def failSetBadServer = badServer.set( +// "nic", "cage" +// ).attemptRun(_.getMessage()) must beFail +// +// def failSetBadToken = badToken.set( +// "nic", "cage" +// ).attemptRun(_.getMessage()) must beFail +//} diff --git a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala index 43f31cd..f7e4675 100644 --- a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala +++ b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala @@ -8,7 +8,6 @@ import janstenpickle.scala.syntax.VaultConfigSyntax._ import org.scalacheck.Gen import org.specs2.Specification import org.specs2.specification.core.Fragments -import uscala.result.specs2.ResultMatchers import scala.concurrent.ExecutionContext import scala.io.Source @@ -38,12 +37,12 @@ trait VaultSpec extends Specification with ResultMatchers { "con-air" ) - def check = config.token.attemptRun(_.getMessage) must beOk +// def check = config.token.attemptRun(_.getMessage) must beOk override def map(fs: => Fragments) = - s2""" - Can receive a token for an AppRole $check - """ ^ +// s2""" +// Can receive a token for an AppRole $check +// """ ^ fs } diff --git a/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala index 0e112a6..7656369 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala @@ -1,38 +1,38 @@ -package janstenpickle.vault.manage - -import janstenpickle.vault.core.VaultSpec -import org.scalacheck.{Prop, Gen} -import org.specs2.ScalaCheck - -class AuthIT extends VaultSpec with ScalaCheck { - import AuthIT._ - import VaultSpec._ - - def is = - s2""" - Can enable and disable valid auth mount $happy - Cannot enable an invalid auth type $enableFail - """ - - lazy val underTest = new Auth(config) - - def happy = Prop.forAllNoShrink( - backends, longerStrGen, Gen.option(longerStrGen))((backend, mount, desc) => - (underTest.enable(backend, Some(mount), desc) - .attemptRun(_.getMessage()) must beOk) and - (underTest.disable(mount).attemptRun(_.getMessage()) must beOk) - ) - - def enableFail = Prop.forAllNoShrink( - longerStrGen.suchThat(!backendNames.contains(_)), - longerStrGen, - Gen.option(longerStrGen))((backend, mount, desc) => - underTest.enable(mount).attemptRun(_.getMessage()) must beFail - ) - -} - -object AuthIT { - val backendNames = List("github", "app-id", "ldap", "userpass") - val backends = Gen.oneOf(backendNames) -} +//package janstenpickle.vault.manage +// +//import janstenpickle.vault.core.VaultSpec +//import org.scalacheck.{Prop, Gen} +//import org.specs2.ScalaCheck +// +//class AuthIT extends VaultSpec with ScalaCheck { +// import AuthIT._ +// import VaultSpec._ +// +// def is = +// s2""" +// Can enable and disable valid auth mount $happy +// Cannot enable an invalid auth type $enableFail +// """ +// +// lazy val underTest = new Auth(config) +// +// def happy = Prop.forAllNoShrink( +// backends, longerStrGen, Gen.option(longerStrGen))((backend, mount, desc) => +// (underTest.enable(backend, Some(mount), desc) +// .attemptRun(_.getMessage()) must beOk) and +// (underTest.disable(mount).attemptRun(_.getMessage()) must beOk) +// ) +// +// def enableFail = Prop.forAllNoShrink( +// longerStrGen.suchThat(!backendNames.contains(_)), +// longerStrGen, +// Gen.option(longerStrGen))((backend, mount, desc) => +// underTest.enable(mount).attemptRun(_.getMessage()) must beFail +// ) +// +//} +// +//object AuthIT { +// val backendNames = List("github", "app-id", "ldap", "userpass") +// val backends = Gen.oneOf(backendNames) +//} diff --git a/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala index 449a418..d70e5ed 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala @@ -1,83 +1,83 @@ -package janstenpickle.vault.manage - -import com.ning.http.client.Response -import com.ning.http.client.providers.jdk.JDKResponse -import janstenpickle.vault.core.VaultSpec -import janstenpickle.vault.manage.Model.{Mount, MountConfig} -import org.scalacheck.{Gen, Prop} -import org.specs2.ScalaCheck -import uscala.result.Result - -class MountIT extends VaultSpec with ScalaCheck { - import MountIT._ - import VaultSpec._ - - def is = - s2""" - Can enable, remount and disable a valid mount $happy - Can enable, list and then disable valid mounts $listSuccess - Cannot enable an invalid mount type $enableFail - """ - - lazy val underTest = new Mounts(config) - - def happy = Prop.forAllNoShrink( - mountGen, - longerStrGen, - longerStrGen, - Gen.option(longerStrGen))((mount, mountPoint, remountPoint, desc) => { - (underTest.mount(mount.`type`, Some(mountPoint), desc, Some(mount)) - .attemptRun(_.getMessage()) must beOk) and - (underTest.remount(mountPoint, remountPoint) - .attemptRun(_.getMessage()) must beOk) and - (underTest.delete(remountPoint) - .attemptRun(_.getMessage()) must beOk) and - (underTest.delete(mountPoint) - .attemptRun(_.getMessage()) must beOk) - }) - - def listSuccess = (processMountTypes((acc, mount) => - acc.flatMap(_ => underTest.mount(mount) - .attemptRun(_.getMessage()))) must beOk) and - (underTest.list.attemptRun(_.getMessage()) must beOk.like { - case a => a.map(_._2.`type`) must containAllOf(mountTypes) - }) and - (processMountTypes((acc, mount) => - acc.flatMap(_ => - underTest.delete(mount).attemptRun(_.getMessage())) - ) must beOk) - - def enableFail = Prop.forAllNoShrink( - longerStrGen.suchThat(!mountTypes.contains(_)), - longerStrGen, - Gen.option(longerStrGen))((`type`, mount, desc) => - underTest.mount(`type`, Some(mount), desc) - .attemptRun(_.getMessage()) must beFail - ) - -} - -object MountIT { - import VaultSpec._ - - val mountTypes = List( - "aws", "cassandra", "consul", "generic", - "mssql", "mysql", "pki", "postgresql", "ssh", "transit" - ) - val mount = Gen.oneOf(mountTypes) - val mounts = Gen.listOf(mountTypes).suchThat(_.nonEmpty) - - val mountGen = for { - mountType <- mount - description <- Gen.option(longerStrGen) - defaultTtl <- Gen.option(Gen.posNum[Int]) - maxTtl <- Gen.option(Gen.posNum[Int]) - forceNoCache <- Gen.option(Gen.oneOf(true, false)) - } yield Mount(mountType, description, Some(MountConfig(defaultTtl, maxTtl, forceNoCache))) - - def processMountTypes(op: (Result[String, Response], String) => Result[String, - Response]) = - mountTypes.foldLeft[Result[String, Response]](Result.ok(new - JDKResponse(null, null, null)))(op) - -} +//package janstenpickle.vault.manage +// +//import com.ning.http.client.Response +//import com.ning.http.client.providers.jdk.JDKResponse +//import janstenpickle.vault.core.VaultSpec +//import janstenpickle.vault.manage.Model.{Mount, MountConfig} +//import org.scalacheck.{Gen, Prop} +//import org.specs2.ScalaCheck +//import janstenpickle.scala.Result +// +//class MountIT extends VaultSpec with ScalaCheck { +// import MountIT._ +// import VaultSpec._ +// +// def is = +// s2""" +// Can enable, remount and disable a valid mount $happy +// Can enable, list and then disable valid mounts $listSuccess +// Cannot enable an invalid mount type $enableFail +// """ +// +// lazy val underTest = new Mounts(config) +// +// def happy = Prop.forAllNoShrink( +// mountGen, +// longerStrGen, +// longerStrGen, +// Gen.option(longerStrGen))((mount, mountPoint, remountPoint, desc) => { +// (underTest.mount(mount.`type`, Some(mountPoint), desc, Some(mount)) +// .attemptRun(_.getMessage()) must beOk) and +// (underTest.remount(mountPoint, remountPoint) +// .attemptRun(_.getMessage()) must beOk) and +// (underTest.delete(remountPoint) +// .attemptRun(_.getMessage()) must beOk) and +// (underTest.delete(mountPoint) +// .attemptRun(_.getMessage()) must beOk) +// }) +// +// def listSuccess = (processMountTypes((acc, mount) => +// acc.flatMap(_ => underTest.mount(mount) +// .attemptRun(_.getMessage()))) must beOk) and +// (underTest.list.attemptRun(_.getMessage()) must beOk.like { +// case a => a.map(_._2.`type`) must containAllOf(mountTypes) +// }) and +// (processMountTypes((acc, mount) => +// acc.flatMap(_ => +// underTest.delete(mount).attemptRun(_.getMessage())) +// ) must beOk) +// +// def enableFail = Prop.forAllNoShrink( +// longerStrGen.suchThat(!mountTypes.contains(_)), +// longerStrGen, +// Gen.option(longerStrGen))((`type`, mount, desc) => +// underTest.mount(`type`, Some(mount), desc) +// .attemptRun(_.getMessage()) must beFail +// ) +// +//} +// +//object MountIT { +// import VaultSpec._ +// +// val mountTypes = List( +// "aws", "cassandra", "consul", "generic", +// "mssql", "mysql", "pki", "postgresql", "ssh", "transit" +// ) +// val mount = Gen.oneOf(mountTypes) +// val mounts = Gen.listOf(mountTypes).suchThat(_.nonEmpty) +// +// val mountGen = for { +// mountType <- mount +// description <- Gen.option(longerStrGen) +// defaultTtl <- Gen.option(Gen.posNum[Int]) +// maxTtl <- Gen.option(Gen.posNum[Int]) +// forceNoCache <- Gen.option(Gen.oneOf(true, false)) +// } yield Mount(mountType, description, Some(MountConfig(defaultTtl, maxTtl, forceNoCache))) +// +// def processMountTypes(op: (Result[String, Response], String) => Result[String, +// Response]) = +// mountTypes.foldLeft[Result[String, Response]](Result.ok(new +// JDKResponse(null, null, null)))(op) +// +//} diff --git a/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala index c8a4b38..1abd4e6 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala @@ -1,57 +1,57 @@ -package janstenpickle.vault.manage - -import janstenpickle.vault.core.VaultSpec -import janstenpickle.vault.manage.Model.Rule -import org.scalacheck.{Gen, Prop} -import org.specs2.ScalaCheck -import uscala.result.Result - -class PolicyIT extends VaultSpec with ScalaCheck { - import PolicyIT._ - import VaultSpec._ - - override def is = - s2""" - Can successfully set and get policies $happy - Cannot set an invalid policy $sad - """ - - lazy val underTest = Policy(config) - - def happy = Prop.forAllNoShrink( - longerStrGen, - Gen.listOf(ruleGen(longerStrGen, policyGen, capabilitiesGen)). - suchThat(_.nonEmpty)) { (name, rules) => - (underTest.set(name.toLowerCase, rules) - .attemptRun(_.getMessage()) must beOk) and - (underTest.inspect(name.toLowerCase) - .attemptRun(_.getMessage()) must beOk) and - (underTest.delete(name.toLowerCase).attemptRun(_.getMessage()) must beOk) - } - - // cannot use generated values here as - // vault seems to have a failure rate limit - def sad = underTest.set( - "nic", List(Rule("cage", Some(List("kim", "copolla")))) - ).attemptRun(_.getMessage()) must beFail -} - -object PolicyIT { - val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) - val capabilitiesGen = - Gen.listOf(Gen.oneOf( - "create", "read", "update", "delete", "list", "sudo", "deny")). - suchThat(_.nonEmpty). - map(_.distinct) - - def ruleGen( - pathGen: Gen[String], - polGen: Gen[Option[String]], - capGen: Gen[List[String]] - ) = for { - path <- pathGen - policy <- polGen - capabilities <- capGen - } yield Rule(path, Some(capabilities), policy) -} - +//package janstenpickle.vault.manage +// +//import janstenpickle.vault.core.VaultSpec +//import janstenpickle.vault.manage.Model.Rule +//import org.scalacheck.{Gen, Prop} +//import org.specs2.ScalaCheck +//import uscala.result.Result +// +//class PolicyIT extends VaultSpec with ScalaCheck { +// import PolicyIT._ +// import VaultSpec._ +// +// override def is = +// s2""" +// Can successfully set and get policies $happy +// Cannot set an invalid policy $sad +// """ +// +// lazy val underTest = Policy(config) +// +// def happy = Prop.forAllNoShrink( +// longerStrGen, +// Gen.listOf(ruleGen(longerStrGen, policyGen, capabilitiesGen)). +// suchThat(_.nonEmpty)) { (name, rules) => +// (underTest.set(name.toLowerCase, rules) +// .attemptRun(_.getMessage()) must beOk) and +// (underTest.inspect(name.toLowerCase) +// .attemptRun(_.getMessage()) must beOk) and +// (underTest.delete(name.toLowerCase).attemptRun(_.getMessage()) must beOk) +// } +// +// // cannot use generated values here as +// // vault seems to have a failure rate limit +// def sad = underTest.set( +// "nic", List(Rule("cage", Some(List("kim", "copolla")))) +// ).attemptRun(_.getMessage()) must beFail +//} +// +//object PolicyIT { +// val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) +// val capabilitiesGen = +// Gen.listOf(Gen.oneOf( +// "create", "read", "update", "delete", "list", "sudo", "deny")). +// suchThat(_.nonEmpty). +// map(_.distinct) +// +// def ruleGen( +// pathGen: Gen[String], +// polGen: Gen[Option[String]], +// capGen: Gen[List[String]] +// ) = for { +// path <- pathGen +// policy <- polGen +// capabilities <- capGen +// } yield Rule(path, Some(capabilities), policy) +//} +// diff --git a/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala index d8795fc..c2db978 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala @@ -1,51 +1,51 @@ -package janstenpickle.vault.manage - -import janstenpickle.vault.core.VaultSpec -import org.scalacheck.{Gen, Prop} -import org.specs2.ScalaCheck - -class UserPassIT extends VaultSpec with ScalaCheck { - import UserPassIT._ - import VaultSpec._ - - def is = - s2""" - Can create, update and delete a user $good - Cannot create a user for a non-existent client $badClient - Cannot create user with a bad policy $badPolicy - """ - - lazy val underTest = UserPass(config) - lazy val authAdmin = Auth(config) - - def good = Prop.forAllNoShrink(longerStrGen, longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen, policyGen)( - (username, password, newPassword, ttl, client, policy) => - (authAdmin.enable("userpass", Some(client)).attemptRun(_.getMessage()) must beOk) and - (underTest.create(username, password, ttl, None, client).attemptRun(_.getMessage()) must beOk) and - (underTest.setPassword(username, newPassword, client).attemptRun(_.getMessage()) must beOk) and - (underTest.setPolicies(username, policy, client).attemptRun(_.getMessage()) must beOk) and - (underTest.delete(username, client).attemptRun(_.getMessage()) must beOk) and - (authAdmin.disable(client).attemptRun(_.getMessage()) must beOk) - ) - - def badClient = Prop.forAllNoShrink(longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen)( - (username, password, ttl, client) => - underTest.create(username, password, ttl, None, client).attemptRun(_.getMessage()) must beFail - ) - - def badPolicy = Prop.forAllNoShrink(longerStrGen, - longerStrGen, - Gen.posNum[Int], - longerStrGen, - Gen.listOf(longerStrGen.suchThat(!policies.contains(_))))( - (username, password, ttl, client, policy) => - (authAdmin.enable("userpass", Some(client)).attemptRun(_.getMessage()) must beOk) and - (underTest.create(username, password, ttl, Some(policy), client).attemptRun(_.getMessage()) must beOk) and - (authAdmin.disable(client).attemptRun(_.getMessage()) must beOk) - ) -} - -object UserPassIT { - val policies = List("default", "root") - val policyGen = Gen.listOf(Gen.oneOf(policies)) -} \ No newline at end of file +//package janstenpickle.vault.manage +// +//import janstenpickle.vault.core.VaultSpec +//import org.scalacheck.{Gen, Prop} +//import org.specs2.ScalaCheck +// +//class UserPassIT extends VaultSpec with ScalaCheck { +// import UserPassIT._ +// import VaultSpec._ +// +// def is = +// s2""" +// Can create, update and delete a user $good +// Cannot create a user for a non-existent client $badClient +// Cannot create user with a bad policy $badPolicy +// """ +// +// lazy val underTest = UserPass(config) +// lazy val authAdmin = Auth(config) +// +// def good = Prop.forAllNoShrink(longerStrGen, longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen, policyGen)( +// (username, password, newPassword, ttl, client, policy) => +// (authAdmin.enable("userpass", Some(client)).attemptRun(_.getMessage()) must beOk) and +// (underTest.create(username, password, ttl, None, client).attemptRun(_.getMessage()) must beOk) and +// (underTest.setPassword(username, newPassword, client).attemptRun(_.getMessage()) must beOk) and +// (underTest.setPolicies(username, policy, client).attemptRun(_.getMessage()) must beOk) and +// (underTest.delete(username, client).attemptRun(_.getMessage()) must beOk) and +// (authAdmin.disable(client).attemptRun(_.getMessage()) must beOk) +// ) +// +// def badClient = Prop.forAllNoShrink(longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen)( +// (username, password, ttl, client) => +// underTest.create(username, password, ttl, None, client).attemptRun(_.getMessage()) must beFail +// ) +// +// def badPolicy = Prop.forAllNoShrink(longerStrGen, +// longerStrGen, +// Gen.posNum[Int], +// longerStrGen, +// Gen.listOf(longerStrGen.suchThat(!policies.contains(_))))( +// (username, password, ttl, client, policy) => +// (authAdmin.enable("userpass", Some(client)).attemptRun(_.getMessage()) must beOk) and +// (underTest.create(username, password, ttl, Some(policy), client).attemptRun(_.getMessage()) must beOk) and +// (authAdmin.disable(client).attemptRun(_.getMessage()) must beOk) +// ) +//} +// +//object UserPassIT { +// val policies = List("default", "root") +// val policyGen = Gen.listOf(Gen.oneOf(policies)) +//} \ No newline at end of file diff --git a/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala b/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala index c6ec359..50e1db0 100644 --- a/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala +++ b/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala @@ -1,51 +1,51 @@ -package janstenpickle.vault.manage - -import janstenpickle.vault.manage.Model.Rule -import org.scalacheck.{Gen, Prop} -import org.specs2.{ScalaCheck, Specification} -import uscala.result.specs2.ResultMatchers - -class RuleSpec extends Specification with ScalaCheck with ResultMatchers { - import RuleSpec._ - - override def is = - s2""" - Can encode and decode policy strings $passes - Cannot decode bad policy strings $fails - """ - - def passes = Prop.forAllNoShrink(Gen.listOf(ruleGen).suchThat(_.nonEmpty)) (rules => - Rule.decode(rules.map(_.encode).mkString("\n")) must beOk.like { - case a => a must containAllOf(rules) - } - ) - - def fails = Prop.forAllNoShrink(Gen.listOf(badRuleGen).suchThat(_.nonEmpty)) (rules => - Rule.decode(rules.mkString("\n")) must beFail - ) -} - -object RuleSpec { - val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) - val capabilitiesGen = Gen.option( - Gen.listOf(Gen.oneOf("create", "read", "update", "delete", "list", "sudo", "deny")). - suchThat(_.nonEmpty). - map(_.distinct) - ) - - val ruleGen = for { - path <- Gen.alphaStr.suchThat(_.nonEmpty) - policy <- policyGen - capabilities <- capabilitiesGen - } yield Rule(path, capabilities, policy) - - val badRuleGen = for { - path <- Gen.alphaStr.suchThat(_.nonEmpty) - policy <- policyGen - capabilities <- capabilitiesGen - } yield - s""" - |path "$path" - | $policy cage - | $capabilities }""".stripMargin('|') -} +//package janstenpickle.vault.manage +// +//import janstenpickle.vault.manage.Model.Rule +//import org.scalacheck.{Gen, Prop} +//import org.specs2.{ScalaCheck, Specification} +//import uscala.result.specs2.ResultMatchers +// +//class RuleSpec extends Specification with ScalaCheck with ResultMatchers { +// import RuleSpec._ +// +// override def is = +// s2""" +// Can encode and decode policy strings $passes +// Cannot decode bad policy strings $fails +// """ +// +// def passes = Prop.forAllNoShrink(Gen.listOf(ruleGen).suchThat(_.nonEmpty)) (rules => +// Rule.decode(rules.map(_.encode).mkString("\n")) must beOk.like { +// case a => a must containAllOf(rules) +// } +// ) +// +// def fails = Prop.forAllNoShrink(Gen.listOf(badRuleGen).suchThat(_.nonEmpty)) (rules => +// Rule.decode(rules.mkString("\n")) must beFail +// ) +//} +// +//object RuleSpec { +// val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) +// val capabilitiesGen = Gen.option( +// Gen.listOf(Gen.oneOf("create", "read", "update", "delete", "list", "sudo", "deny")). +// suchThat(_.nonEmpty). +// map(_.distinct) +// ) +// +// val ruleGen = for { +// path <- Gen.alphaStr.suchThat(_.nonEmpty) +// policy <- policyGen +// capabilities <- capabilitiesGen +// } yield Rule(path, capabilities, policy) +// +// val badRuleGen = for { +// path <- Gen.alphaStr.suchThat(_.nonEmpty) +// policy <- policyGen +// capabilities <- capabilitiesGen +// } yield +// s""" +// |path "$path" +// | $policy cage +// | $capabilities }""".stripMargin('|') +//} From 0ec2ed3211d1cac37dde3d797fcca29e1f772991 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 13:45:25 +0200 Subject: [PATCH 05/16] Move Result type aliases to package object scala.result --- .../janstenpickle/vault/auth/UserPass.scala | 2 +- .../janstenpickle/vault/core/VaultSpec.scala | 104 +++++++++--------- .../{Result.scala => result/package.scala} | 14 ++- .../janstenpickle/scala/syntax/syntax.scala | 2 +- .../janstenpickle/vault/core/Secrets.scala | 2 +- .../vault/core/VaultConfig.scala | 2 +- .../janstenpickle/vault/manage/UserPass.scala | 2 +- .../janstenpickle/vault/manage/manage.scala | 3 +- 8 files changed, 67 insertions(+), 64 deletions(-) rename core/src/main/scala/janstenpickle/scala/{Result.scala => result/package.scala} (76%) diff --git a/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala b/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala index a3a8363..e60d4c8 100644 --- a/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala +++ b/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala @@ -5,7 +5,7 @@ import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.vault.core.WSClient -import janstenpickle.scala.Result._ +import janstenpickle.scala.result._ import scala.concurrent.ExecutionContext diff --git a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala index f7e4675..06d05be 100644 --- a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala +++ b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala @@ -1,52 +1,52 @@ -package janstenpickle.vault.core - -import java.net.URL - -import janstenpickle.scala.syntax.SyntaxRequest._ -import janstenpickle.scala.syntax.ResponseSyntax._ -import janstenpickle.scala.syntax.VaultConfigSyntax._ -import org.scalacheck.Gen -import org.specs2.Specification -import org.specs2.specification.core.Fragments - -import scala.concurrent.ExecutionContext -import scala.io.Source - - -trait VaultSpec extends Specification with ResultMatchers { - implicit val errConverter: Throwable => String = _.getMessage - implicit val ec: ExecutionContext = ExecutionContext.global - - lazy val rootToken = Source.fromFile("/tmp/.root-token").mkString.trim - lazy val roleId = Source.fromFile("/tmp/.role-id").mkString.trim - lazy val secretId = Source.fromFile("/tmp/.secret-id").mkString.trim - - lazy val rootConfig: VaultConfig = VaultConfig( - WSClient(new URL("http://localhost:8200")), rootToken - ) - lazy val badTokenConfig = VaultConfig( - rootConfig.wsClient, - "face-off" - ) - lazy val config = VaultConfig( - rootConfig.wsClient, - AppRole(roleId, secretId) - ) - lazy val badServerConfig = VaultConfig( - WSClient(new URL("http://nic-cage.xyz")), - "con-air" - ) - -// def check = config.token.attemptRun(_.getMessage) must beOk - - override def map(fs: => Fragments) = -// s2""" -// Can receive a token for an AppRole $check -// """ ^ - fs -} - -object VaultSpec { - val longerStrGen = Gen.alphaStr.suchThat(_.length >= 3) - val strGen = Gen.alphaStr.suchThat(_.nonEmpty) -} +//package janstenpickle.vault.core +// +//import java.net.URL +// +//import janstenpickle.scala.syntax.SyntaxRequest._ +//import janstenpickle.scala.syntax.ResponseSyntax._ +//import janstenpickle.scala.syntax.VaultConfigSyntax._ +//import org.scalacheck.Gen +//import org.specs2.Specification +//import org.specs2.specification.core.Fragments +// +//import scala.concurrent.ExecutionContext +//import scala.io.Source +// +// +//trait VaultSpec extends Specification with ResultMatchers { +// implicit val errConverter: Throwable => String = _.getMessage +// implicit val ec: ExecutionContext = ExecutionContext.global +// +// lazy val rootToken = Source.fromFile("/tmp/.root-token").mkString.trim +// lazy val roleId = Source.fromFile("/tmp/.role-id").mkString.trim +// lazy val secretId = Source.fromFile("/tmp/.secret-id").mkString.trim +// +// lazy val rootConfig: VaultConfig = VaultConfig( +// WSClient(new URL("http://localhost:8200")), rootToken +// ) +// lazy val badTokenConfig = VaultConfig( +// rootConfig.wsClient, +// "face-off" +// ) +// lazy val config = VaultConfig( +// rootConfig.wsClient, +// AppRole(roleId, secretId) +// ) +// lazy val badServerConfig = VaultConfig( +// WSClient(new URL("http://nic-cage.xyz")), +// "con-air" +// ) +// +//// def check = config.token.attemptRun(_.getMessage) must beOk +// +// override def map(fs: => Fragments) = +//// s2""" +//// Can receive a token for an AppRole $check +//// """ ^ +// fs +//} +// +//object VaultSpec { +// val longerStrGen = Gen.alphaStr.suchThat(_.length >= 3) +// val strGen = Gen.alphaStr.suchThat(_.nonEmpty) +//} diff --git a/core/src/main/scala/janstenpickle/scala/Result.scala b/core/src/main/scala/janstenpickle/scala/result/package.scala similarity index 76% rename from core/src/main/scala/janstenpickle/scala/Result.scala rename to core/src/main/scala/janstenpickle/scala/result/package.scala index c822344..203476c 100644 --- a/core/src/main/scala/janstenpickle/scala/Result.scala +++ b/core/src/main/scala/janstenpickle/scala/result/package.scala @@ -5,12 +5,16 @@ import cats.data.EitherT import scala.concurrent.{ExecutionContext, Future} import cats.implicits._ -object Result { +package object result { type Result[F, R] = Either[F, R] + val Result: Either.type = Either - def pure[F, R](r: R): Either[F, R] = Either right r + implicit class ResultCompanionOps(dc: Either.type ){ + def pure[F, R](r: R): Either[F, R] = Either right r + + def fail[F, R](f: F): Either[F, R] = Either left f + } - def fail[F, R](f: F): Either[F, R] = Either left f type AsyncResult[F, R] = Future[Result[F, R]] @@ -31,6 +35,6 @@ object Result { } implicit class AsyncResultOpsAny[F, R](r: R) { - def eiT: AsyncEitherT[F, R] = EitherT.apply(Future.successful(pure(r))) + def eiT: AsyncEitherT[F, R] = EitherT.apply(Future.successful(Either right r)) } -} +} \ No newline at end of file diff --git a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala index be89764..80d7765 100644 --- a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala +++ b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala @@ -6,7 +6,7 @@ import io.circe._ import io.circe.parser._ import io.circe.syntax._ import janstenpickle.vault.core.VaultConfig -import janstenpickle.scala.Result._ +import janstenpickle.scala.result._ import cats.implicits._ import scala.concurrent.{ExecutionContext, Future} diff --git a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala index ce3a7af..77efc0d 100644 --- a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala +++ b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala @@ -4,7 +4,7 @@ import com.ning.http.client.Response import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ -import janstenpickle.scala.Result._ +import janstenpickle.scala.result._ import cats.implicits._ import scala.concurrent.ExecutionContext diff --git a/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala b/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala index cd77af1..15b77c4 100644 --- a/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala +++ b/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala @@ -8,7 +8,7 @@ import io.circe.syntax._ import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ -import janstenpickle.scala.Result._ +import janstenpickle.scala.result._ import scala.concurrent.ExecutionContext diff --git a/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala b/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala index 343fed6..03ae7ae 100644 --- a/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala +++ b/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala @@ -6,7 +6,7 @@ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ import janstenpickle.vault.core.VaultConfig -import janstenpickle.scala.Result._ +import janstenpickle.scala.result._ import scala.concurrent.ExecutionContext diff --git a/manage/src/main/scala/janstenpickle/vault/manage/manage.scala b/manage/src/main/scala/janstenpickle/vault/manage/manage.scala index 3cac21f..bc59fc7 100644 --- a/manage/src/main/scala/janstenpickle/vault/manage/manage.scala +++ b/manage/src/main/scala/janstenpickle/vault/manage/manage.scala @@ -9,8 +9,7 @@ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ import janstenpickle.vault.core.VaultConfig import janstenpickle.vault.manage.Model._ -import janstenpickle.scala.Result -import janstenpickle.scala.Result._ +import janstenpickle.scala.result._ import scala.concurrent.ExecutionContext From d84fa4a5e61e55c1c9a2a7128fa7eb6fe9b1ac01 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 14:19:47 +0200 Subject: [PATCH 06/16] Provide implementation for syntax --- .../janstenpickle/scala/syntax/syntax.scala | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala index 80d7765..6ade559 100644 --- a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala +++ b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala @@ -11,6 +11,7 @@ import cats.implicits._ import scala.concurrent.{ExecutionContext, Future} import scala.language.postfixOps +import scala.util.control.NonFatal object OptionSyntax { implicit class ToTuple[T](opt: Option[T]) { @@ -22,7 +23,11 @@ object OptionSyntax { object AsyncResultSyntax { implicit class FutureToAsyncResult[T](future: Future[T]) (implicit ec: ExecutionContext) { - def toAsyncResult: AsyncResult[String, T] = ??? + def toAsyncResult: AsyncResult[String, T] = { + future.map(Result pure[String, T] _).recover { + case NonFatal(e) => Result fail[String, T] e.getMessage + } + } } implicit class ReqToAsyncResult(req: Req) @@ -58,10 +63,11 @@ object JsonSyntax { implicit decode: Decoder[T], ec: ExecutionContext ): AsyncResult[String, T] = { - // json.flatMapR(j => decode.tryDecode( - // jsonPath(j.hcursor) - // ).leftMap(_.message)) - ??? + val r = for { + j <- json.eiT + e <- decode.tryDecode(jsonPath(j.hcursor)).leftMap(_.message).eiT + } yield e + r.value } } @@ -73,28 +79,25 @@ object ResponseSyntax { implicit class ResponseHandler(resp: AsyncResult[String, Response]) { def acceptStatusCodes(codes: Int*) (implicit ec: ExecutionContext): AsyncResult[String, Response] = { - //resp.flatMapR( - // response => - // if (codes.contains(response.getStatusCode)) { - // Result.ok(response) - // } - // else { - // Result.fail( - // s"Received failure response from server:" + - // s" ${response.getStatusCode}\n ${response.getResponseBody}" - // ) - // } - //) - - ??? + val r = for { + response <- resp.eiT + r <- Result.cond( + test = codes.contains(response.getStatusCode), + right = response, + left = s"Received failure response from server:" + + s" ${response.getStatusCode}\n ${response.getResponseBody}" + ).eiT + } yield r + r.value } def extractJson(implicit ec: ExecutionContext): AsyncResult[String, Json] = { -// resp.flatMapR(response => -// parse(response.getResponseBody).leftMap(_.message) -// ) - ??? + val r = for { + response <- resp.eiT + r <- parse(response.getResponseBody).leftMap(_.message).eiT + } yield r + r.value } @@ -111,9 +114,13 @@ object SyntaxRequest { implicit class ExecuteRequest(req: AsyncResult[String, Req]) (implicit ec: ExecutionContext) { + import AsyncResultSyntax._ def execute: AsyncResult[String, Response] = { - //req.flatMapF(Http(_)) - ??? + val r = for { + request <- req.eiT + response <- Http(request).toAsyncResult.eiT + } yield response + r.value } } From e806da9e315fe64d6e2419cbe720a2e35ef4065a Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 14:31:39 +0200 Subject: [PATCH 07/16] Add attemptRun on AsyncResult --- .../scala/janstenpickle/scala/result/package.scala | 14 +++++++++++--- .../scala/janstenpickle/scala/syntax/syntax.scala | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/janstenpickle/scala/result/package.scala b/core/src/main/scala/janstenpickle/scala/result/package.scala index 203476c..626d3bf 100644 --- a/core/src/main/scala/janstenpickle/scala/result/package.scala +++ b/core/src/main/scala/janstenpickle/scala/result/package.scala @@ -2,9 +2,12 @@ package janstenpickle.scala import cats.data.EitherT -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.{Await, ExecutionContext, Future} import cats.implicits._ +import scala.concurrent.duration._ +import scala.language.postfixOps + package object result { type Result[F, R] = Either[F, R] val Result: Either.type = Either @@ -28,13 +31,18 @@ package object result { implicit class AsyncResultOps[F, R](f: AsyncResult[F, R]) { def eiT: AsyncEitherT[F, R] = EitherT[Future, F, R](f) + + def attemptRun(implicit ec: ExecutionContext): Result[F, R] = + Await.result(f, 1 minute) } implicit class AsyncResultOpsEitherT[F, R](f: Either[F, R]) { - def eiT(implicit ec: ExecutionContext): AsyncEitherT[F, R] = EitherT.fromEither[Future](f) + def eiT(implicit ec: ExecutionContext): AsyncEitherT[F, R] = + EitherT.fromEither[Future](f) } implicit class AsyncResultOpsAny[F, R](r: R) { - def eiT: AsyncEitherT[F, R] = EitherT.apply(Future.successful(Either right r)) + def eiT: AsyncEitherT[F, R] = + EitherT.apply(Future.successful(Either right r)) } } \ No newline at end of file diff --git a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala index 6ade559..268f3ce 100644 --- a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala +++ b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala @@ -21,6 +21,7 @@ object OptionSyntax { } object AsyncResultSyntax { + implicit class FutureToAsyncResult[T](future: Future[T]) (implicit ec: ExecutionContext) { def toAsyncResult: AsyncResult[String, T] = { From 27b72b6fb2505ba51d42d9f6b3f80c3168aead8a Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 14:31:58 +0200 Subject: [PATCH 08/16] Update tests in core --- .../janstenpickle/vault/core/SecretsIT.scala | 169 +++++++++--------- .../janstenpickle/vault/core/VaultSpec.scala | 106 +++++------ 2 files changed, 139 insertions(+), 136 deletions(-) diff --git a/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala b/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala index 3a8a090..a3a3e2e 100644 --- a/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala +++ b/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala @@ -1,84 +1,85 @@ -//package janstenpickle.vault.core -// -//import org.scalacheck.Prop -//import org.specs2.ScalaCheck -// -//class GenericIT extends SecretsTests { -// override def backend: String = "secret" -//} -// -//class CubbyHoleIT extends SecretsTests { -// override def backend: String = "cubbyhole" -//} -// -//trait SecretsTests extends VaultSpec with ScalaCheck { -// import VaultSpec._ -// -// override def is = -// s2""" -// Can set a secret in vault $set -// Can set and get a secret in vault $get -// Can list keys $list -// Can set multiple subkeys $setMulti -// Can set and get multiple subKeys $getSetMulti -// Cannot get non-existent key $failGet -// Fails to perform actions on a non-vault server $failSetBadServer -// Fails to perform actions with a bad token $failSetBadToken -// """ -// -// def backend: String -// -// lazy val good = Secrets(config, backend) -// lazy val badToken = Secrets(badTokenConfig, backend) -// lazy val badServer = Secrets(badServerConfig, backend) -// -// def set = Prop.forAllNoShrink(strGen, strGen) { (key, value) => -// good.set(key, value).attemptRun(_.getMessage()) must beOk -// } -// -// def get = Prop.forAllNoShrink(strGen, strGen) { (key, value) => -// (good.set(key, value).attemptRun(_.getMessage()) must beOk) and -// (good.get(key).attemptRun(_.getMessage()) must beOk.like { -// case a => a === value -// }) -// } -// -// def list = Prop.forAllNoShrink(strGen, strGen, strGen) { (key1, key2, value) => -// (good.set(key1, value).attemptRun(_.getMessage()) must beOk) and -// (good.set(key2, value).attemptRun(_.getMessage()) must beOk) and -// (good.list.attemptRun(_.getMessage()) must beOk[List[String]].like { -// case a => a must containAllOf(Seq(key1, key2)) -// }) -// } -// -// def setMulti = Prop.forAllNoShrink(strGen, strGen, strGen, strGen) { -// (key1, key2, value1, value2) => -// good.set( -// "nicolas-cage", -// Map(key1 -> value1, key2 -> value2) -// ).attemptRun(_.getMessage()) must beOk -// } -// -// def getSetMulti = Prop.forAllNoShrink( -// strGen, strGen, strGen, strGen, strGen -// ) { (key1, key2, value1, value2, mainKey) => -// val testData = Map(key1 -> value1, key2 -> value2) -// (good.set(mainKey, testData).attemptRun(_.getMessage()) must beOk) and -// (good.getAll(mainKey).attemptRun(_.getMessage()) must beOk.like { -// case a => a === testData -// }) -// } -// -// def failGet = good.get("john").attemptRun(_.getMessage()) must beFail. -// like { case err => -// err must contain("Received failure response from server: 404") -// } -// -// def failSetBadServer = badServer.set( -// "nic", "cage" -// ).attemptRun(_.getMessage()) must beFail -// -// def failSetBadToken = badToken.set( -// "nic", "cage" -// ).attemptRun(_.getMessage()) must beFail -//} +package janstenpickle.vault.core + +import janstenpickle.scala.result._ +import org.scalacheck.Prop +import org.specs2.ScalaCheck + +class GenericIT extends SecretsTests { + override def backend: String = "secret" +} + +class CubbyHoleIT extends SecretsTests { + override def backend: String = "cubbyhole" +} + +trait SecretsTests extends VaultSpec with ScalaCheck { + import VaultSpec._ + + override def is = + s2""" + Can set a secret in vault $set + Can set and get a secret in vault $get + Can list keys $list + Can set multiple subkeys $setMulti + Can set and get multiple subKeys $getSetMulti + Cannot get non-existent key $failGet + Fails to perform actions on a non-vault server $failSetBadServer + Fails to perform actions with a bad token $failSetBadToken + """ + + def backend: String + + lazy val good = Secrets(config, backend) + lazy val badToken = Secrets(badTokenConfig, backend) + lazy val badServer = Secrets(badServerConfig, backend) + + def set = Prop.forAllNoShrink(strGen, strGen) { (key, value) => + good.set(key, value).attemptRun must beRight + } + + def get = Prop.forAllNoShrink(strGen, strGen) { (key, value) => + (good.set(key, value).attemptRun must beRight) and + (good.get(key).attemptRun must beRight.like { + case a => a === value + }) + } + + def list = Prop.forAllNoShrink(strGen, strGen, strGen) { (key1, key2, value) => + (good.set(key1, value).attemptRun must beRight) and + (good.set(key2, value).attemptRun must beRight) and + (good.list.attemptRun must beRight[List[String]].like { + case a => a must containAllOf(Seq(key1, key2)) + }) + } + + def setMulti = Prop.forAllNoShrink(strGen, strGen, strGen, strGen) { + (key1, key2, value1, value2) => + good.set( + "nicolas-cage", + Map(key1 -> value1, key2 -> value2) + ).attemptRun must beRight + } + + def getSetMulti = Prop.forAllNoShrink( + strGen, strGen, strGen, strGen, strGen + ) { (key1, key2, value1, value2, mainKey) => + val testData = Map(key1 -> value1, key2 -> value2) + (good.set(mainKey, testData).attemptRun must beRight) and + (good.getAll(mainKey).attemptRun must beRight.like { + case a => a === testData + }) + } + + def failGet = good.get("john").attemptRun must beLeft. + like { case err => + err must contain("Received failure response from server: 404") + } + + def failSetBadServer = badServer.set( + "nic", "cage" + ).attemptRun must beLeft + + def failSetBadToken = badToken.set( + "nic", "cage" + ).attemptRun must beLeft +} diff --git a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala index 06d05be..a5645a7 100644 --- a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala +++ b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala @@ -1,52 +1,54 @@ -//package janstenpickle.vault.core -// -//import java.net.URL -// -//import janstenpickle.scala.syntax.SyntaxRequest._ -//import janstenpickle.scala.syntax.ResponseSyntax._ -//import janstenpickle.scala.syntax.VaultConfigSyntax._ -//import org.scalacheck.Gen -//import org.specs2.Specification -//import org.specs2.specification.core.Fragments -// -//import scala.concurrent.ExecutionContext -//import scala.io.Source -// -// -//trait VaultSpec extends Specification with ResultMatchers { -// implicit val errConverter: Throwable => String = _.getMessage -// implicit val ec: ExecutionContext = ExecutionContext.global -// -// lazy val rootToken = Source.fromFile("/tmp/.root-token").mkString.trim -// lazy val roleId = Source.fromFile("/tmp/.role-id").mkString.trim -// lazy val secretId = Source.fromFile("/tmp/.secret-id").mkString.trim -// -// lazy val rootConfig: VaultConfig = VaultConfig( -// WSClient(new URL("http://localhost:8200")), rootToken -// ) -// lazy val badTokenConfig = VaultConfig( -// rootConfig.wsClient, -// "face-off" -// ) -// lazy val config = VaultConfig( -// rootConfig.wsClient, -// AppRole(roleId, secretId) -// ) -// lazy val badServerConfig = VaultConfig( -// WSClient(new URL("http://nic-cage.xyz")), -// "con-air" -// ) -// -//// def check = config.token.attemptRun(_.getMessage) must beOk -// -// override def map(fs: => Fragments) = -//// s2""" -//// Can receive a token for an AppRole $check -//// """ ^ -// fs -//} -// -//object VaultSpec { -// val longerStrGen = Gen.alphaStr.suchThat(_.length >= 3) -// val strGen = Gen.alphaStr.suchThat(_.nonEmpty) -//} +package janstenpickle.vault.core + +import java.net.URL + +import janstenpickle.scala.result._ +import janstenpickle.scala.syntax.SyntaxRequest._ +import janstenpickle.scala.syntax.ResponseSyntax._ +import janstenpickle.scala.syntax.VaultConfigSyntax._ +import org.scalacheck.Gen +import org.specs2.Specification +import org.specs2.matcher.EitherMatchers +import org.specs2.specification.core.Fragments + +import scala.concurrent.ExecutionContext +import scala.io.Source + + +trait VaultSpec extends Specification with EitherMatchers { + implicit val errConverter: Throwable => String = _.getMessage + implicit val ec: ExecutionContext = ExecutionContext.global + + lazy val rootToken: String = Source.fromFile("/tmp/.root-token").mkString.trim + lazy val roleId: String = Source.fromFile("/tmp/.role-id").mkString.trim + lazy val secretId: String = Source.fromFile("/tmp/.secret-id").mkString.trim + + lazy val rootConfig: VaultConfig = VaultConfig( + WSClient(new URL("http://localhost:8200")), rootToken + ) + lazy val badTokenConfig = VaultConfig( + rootConfig.wsClient, + "face-off" + ) + lazy val config = VaultConfig( + rootConfig.wsClient, + AppRole(roleId, secretId) + ) + lazy val badServerConfig = VaultConfig( + WSClient(new URL("http://nic-cage.xyz")), + "con-air" + ) + + def check = config.token.attemptRun should beRight + + override def map(fs: => Fragments) = + s2""" + Can receive a token for an AppRole $check + """ ^ + fs +} + +object VaultSpec { + val longerStrGen = Gen.alphaStr.suchThat(_.length >= 3) + val strGen = Gen.alphaStr.suchThat(_.nonEmpty) +} From 24a3869a611957b1289eff0db275e01ef8ad6acc Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 14:46:44 +0200 Subject: [PATCH 09/16] Move AsyncResult syntax to syntax package --- .../janstenpickle/vault/core/SecretsIT.scala | 1 + .../janstenpickle/vault/core/VaultSpec.scala | 1 + .../janstenpickle/scala/result/package.scala | 22 ++----------- .../janstenpickle/scala/syntax/syntax.scala | 33 +++++++++++++++---- .../janstenpickle/vault/core/Secrets.scala | 1 + 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala b/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala index a3a3e2e..9873a50 100644 --- a/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala +++ b/core/src/it/scala/janstenpickle/vault/core/SecretsIT.scala @@ -1,5 +1,6 @@ package janstenpickle.vault.core +import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.result._ import org.scalacheck.Prop import org.specs2.ScalaCheck diff --git a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala index a5645a7..77b08a9 100644 --- a/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala +++ b/core/src/it/scala/janstenpickle/vault/core/VaultSpec.scala @@ -3,6 +3,7 @@ package janstenpickle.vault.core import java.net.URL import janstenpickle.scala.result._ +import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ diff --git a/core/src/main/scala/janstenpickle/scala/result/package.scala b/core/src/main/scala/janstenpickle/scala/result/package.scala index 626d3bf..10bdc46 100644 --- a/core/src/main/scala/janstenpickle/scala/result/package.scala +++ b/core/src/main/scala/janstenpickle/scala/result/package.scala @@ -2,10 +2,10 @@ package janstenpickle.scala import cats.data.EitherT -import scala.concurrent.{Await, ExecutionContext, Future} +import scala.concurrent.Future import cats.implicits._ -import scala.concurrent.duration._ + import scala.language.postfixOps package object result { @@ -18,7 +18,6 @@ package object result { def fail[F, R](f: F): Either[F, R] = Either left f } - type AsyncResult[F, R] = Future[Result[F, R]] type AsyncEitherT[F, R] = cats.data.EitherT[Future, F, R] @@ -29,20 +28,5 @@ package object result { } } - implicit class AsyncResultOps[F, R](f: AsyncResult[F, R]) { - def eiT: AsyncEitherT[F, R] = EitherT[Future, F, R](f) - - def attemptRun(implicit ec: ExecutionContext): Result[F, R] = - Await.result(f, 1 minute) - } - implicit class AsyncResultOpsEitherT[F, R](f: Either[F, R]) { - def eiT(implicit ec: ExecutionContext): AsyncEitherT[F, R] = - EitherT.fromEither[Future](f) - } - - implicit class AsyncResultOpsAny[F, R](r: R) { - def eiT: AsyncEitherT[F, R] = - EitherT.apply(Future.successful(Either right r)) - } -} \ No newline at end of file +} diff --git a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala index 268f3ce..b1f83a9 100644 --- a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala +++ b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala @@ -1,5 +1,6 @@ package janstenpickle.scala.syntax +import cats.data.EitherT import com.ning.http.client.Response import dispatch.{Http, Req} import io.circe._ @@ -8,8 +9,9 @@ import io.circe.syntax._ import janstenpickle.vault.core.VaultConfig import janstenpickle.scala.result._ import cats.implicits._ +import scala.concurrent.duration._ -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.{Await, ExecutionContext, Future} import scala.language.postfixOps import scala.util.control.NonFatal @@ -25,12 +27,29 @@ object AsyncResultSyntax { implicit class FutureToAsyncResult[T](future: Future[T]) (implicit ec: ExecutionContext) { def toAsyncResult: AsyncResult[String, T] = { - future.map(Result pure[String, T] _).recover { - case NonFatal(e) => Result fail[String, T] e.getMessage + future.map(Result.pure[String, T]).recover { + case NonFatal(e) => Result.fail[String, T](f = e.getMessage) } } } + implicit class AsyncResultOps[F, R](f: AsyncResult[F, R]) { + def eiT: AsyncEitherT[F, R] = EitherT[Future, F, R](f) + + def attemptRun(implicit ec: ExecutionContext): Result[F, R] = + Await.result(f, 1 minute) + } + + implicit class AsyncResultOpsEitherT[F, R](f: Either[F, R]) { + def eiT(implicit ec: ExecutionContext): AsyncEitherT[F, R] = + EitherT.fromEither[Future](f) + } + + implicit class AsyncResultOpsAny[F, R](r: R) { + def eiT: AsyncEitherT[F, R] = + EitherT.apply(Future.successful(Either right r)) + } + implicit class ReqToAsyncResult(req: Req) (implicit ec: ExecutionContext) { def toAsyncResult: AsyncResult[String, Response] = Http(req).toAsyncResult @@ -42,6 +61,7 @@ object AsyncResultSyntax { } object VaultConfigSyntax { + import AsyncResultSyntax._ final val VaultTokenHeader = "X-Vault-Token" @@ -57,10 +77,10 @@ object VaultConfigSyntax { } object JsonSyntax { + import AsyncResultSyntax._ implicit class JsonHandler(json: AsyncResult[String, Json]) { - def extractFromJson[T](jsonPath: HCursor => ACursor = _.downArray) - ( + def extractFromJson[T](jsonPath: HCursor => ACursor = _.downArray)( implicit decode: Decoder[T], ec: ExecutionContext ): AsyncResult[String, T] = { @@ -75,6 +95,7 @@ object JsonSyntax { } object ResponseSyntax { + import AsyncResultSyntax._ import JsonSyntax._ implicit class ResponseHandler(resp: AsyncResult[String, Response]) { @@ -112,10 +133,10 @@ object ResponseSyntax { } object SyntaxRequest { + import AsyncResultSyntax._ implicit class ExecuteRequest(req: AsyncResult[String, Req]) (implicit ec: ExecutionContext) { - import AsyncResultSyntax._ def execute: AsyncResult[String, Response] = { val r = for { request <- req.eiT diff --git a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala index 77efc0d..7c37c07 100644 --- a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala +++ b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala @@ -1,6 +1,7 @@ package janstenpickle.vault.core import com.ning.http.client.Response +import janstenpickle.scala.syntax.AsyncResultSyntax._ import janstenpickle.scala.syntax.SyntaxRequest._ import janstenpickle.scala.syntax.ResponseSyntax._ import janstenpickle.scala.syntax.VaultConfigSyntax._ From 563bd6aa4b979f55c1df3320aa16c547c5c6b259 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 15:19:27 +0200 Subject: [PATCH 10/16] Remove unused import --- core/src/main/scala/janstenpickle/scala/result/package.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/scala/janstenpickle/scala/result/package.scala b/core/src/main/scala/janstenpickle/scala/result/package.scala index 10bdc46..22cd325 100644 --- a/core/src/main/scala/janstenpickle/scala/result/package.scala +++ b/core/src/main/scala/janstenpickle/scala/result/package.scala @@ -1,7 +1,5 @@ package janstenpickle.scala -import cats.data.EitherT - import scala.concurrent.Future import cats.implicits._ From 155063d4a5707818620c72405952a56444f90e69 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 15:19:37 +0200 Subject: [PATCH 11/16] Update manage tests --- .../janstenpickle/vault/manage/AuthIT.scala | 77 ++++---- .../janstenpickle/vault/manage/MountIT.scala | 170 +++++++++--------- .../janstenpickle/vault/manage/PolicyIT.scala | 115 ++++++------ .../vault/manage/UserPassIT.scala | 104 +++++------ .../janstenpickle/vault/manage/RuleSpec.scala | 102 +++++------ 5 files changed, 288 insertions(+), 280 deletions(-) diff --git a/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala index 7656369..27a16f2 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/AuthIT.scala @@ -1,38 +1,39 @@ -//package janstenpickle.vault.manage -// -//import janstenpickle.vault.core.VaultSpec -//import org.scalacheck.{Prop, Gen} -//import org.specs2.ScalaCheck -// -//class AuthIT extends VaultSpec with ScalaCheck { -// import AuthIT._ -// import VaultSpec._ -// -// def is = -// s2""" -// Can enable and disable valid auth mount $happy -// Cannot enable an invalid auth type $enableFail -// """ -// -// lazy val underTest = new Auth(config) -// -// def happy = Prop.forAllNoShrink( -// backends, longerStrGen, Gen.option(longerStrGen))((backend, mount, desc) => -// (underTest.enable(backend, Some(mount), desc) -// .attemptRun(_.getMessage()) must beOk) and -// (underTest.disable(mount).attemptRun(_.getMessage()) must beOk) -// ) -// -// def enableFail = Prop.forAllNoShrink( -// longerStrGen.suchThat(!backendNames.contains(_)), -// longerStrGen, -// Gen.option(longerStrGen))((backend, mount, desc) => -// underTest.enable(mount).attemptRun(_.getMessage()) must beFail -// ) -// -//} -// -//object AuthIT { -// val backendNames = List("github", "app-id", "ldap", "userpass") -// val backends = Gen.oneOf(backendNames) -//} +package janstenpickle.vault.manage + +import janstenpickle.scala.syntax.AsyncResultSyntax._ +import janstenpickle.vault.core.VaultSpec +import org.scalacheck.{Prop, Gen} +import org.specs2.ScalaCheck + +class AuthIT extends VaultSpec with ScalaCheck { + import AuthIT._ + import VaultSpec._ + + def is = + s2""" + Can enable and disable valid auth mount $happy + Cannot enable an invalid auth type $enableFail + """ + + lazy val underTest = new Auth(config) + + def happy = Prop.forAllNoShrink( + backends, longerStrGen, Gen.option(longerStrGen))((backend, mount, desc) => + (underTest.enable(backend, Some(mount), desc) + .attemptRun must beRight) and + (underTest.disable(mount).attemptRun must beRight) + ) + + def enableFail = Prop.forAllNoShrink( + longerStrGen.suchThat(!backendNames.contains(_)), + longerStrGen, + Gen.option(longerStrGen))((backend, mount, desc) => + underTest.enable(mount).attemptRun must beLeft + ) + +} + +object AuthIT { + val backendNames = List("github", "app-id", "ldap", "userpass") + val backends = Gen.oneOf(backendNames) +} diff --git a/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala index d70e5ed..09ab6ff 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala @@ -1,83 +1,87 @@ -//package janstenpickle.vault.manage -// -//import com.ning.http.client.Response -//import com.ning.http.client.providers.jdk.JDKResponse -//import janstenpickle.vault.core.VaultSpec -//import janstenpickle.vault.manage.Model.{Mount, MountConfig} -//import org.scalacheck.{Gen, Prop} -//import org.specs2.ScalaCheck -//import janstenpickle.scala.Result -// -//class MountIT extends VaultSpec with ScalaCheck { -// import MountIT._ -// import VaultSpec._ -// -// def is = -// s2""" -// Can enable, remount and disable a valid mount $happy -// Can enable, list and then disable valid mounts $listSuccess -// Cannot enable an invalid mount type $enableFail -// """ -// -// lazy val underTest = new Mounts(config) -// -// def happy = Prop.forAllNoShrink( -// mountGen, -// longerStrGen, -// longerStrGen, -// Gen.option(longerStrGen))((mount, mountPoint, remountPoint, desc) => { -// (underTest.mount(mount.`type`, Some(mountPoint), desc, Some(mount)) -// .attemptRun(_.getMessage()) must beOk) and -// (underTest.remount(mountPoint, remountPoint) -// .attemptRun(_.getMessage()) must beOk) and -// (underTest.delete(remountPoint) -// .attemptRun(_.getMessage()) must beOk) and -// (underTest.delete(mountPoint) -// .attemptRun(_.getMessage()) must beOk) -// }) -// -// def listSuccess = (processMountTypes((acc, mount) => -// acc.flatMap(_ => underTest.mount(mount) -// .attemptRun(_.getMessage()))) must beOk) and -// (underTest.list.attemptRun(_.getMessage()) must beOk.like { -// case a => a.map(_._2.`type`) must containAllOf(mountTypes) -// }) and -// (processMountTypes((acc, mount) => -// acc.flatMap(_ => -// underTest.delete(mount).attemptRun(_.getMessage())) -// ) must beOk) -// -// def enableFail = Prop.forAllNoShrink( -// longerStrGen.suchThat(!mountTypes.contains(_)), -// longerStrGen, -// Gen.option(longerStrGen))((`type`, mount, desc) => -// underTest.mount(`type`, Some(mount), desc) -// .attemptRun(_.getMessage()) must beFail -// ) -// -//} -// -//object MountIT { -// import VaultSpec._ -// -// val mountTypes = List( -// "aws", "cassandra", "consul", "generic", -// "mssql", "mysql", "pki", "postgresql", "ssh", "transit" -// ) -// val mount = Gen.oneOf(mountTypes) -// val mounts = Gen.listOf(mountTypes).suchThat(_.nonEmpty) -// -// val mountGen = for { -// mountType <- mount -// description <- Gen.option(longerStrGen) -// defaultTtl <- Gen.option(Gen.posNum[Int]) -// maxTtl <- Gen.option(Gen.posNum[Int]) -// forceNoCache <- Gen.option(Gen.oneOf(true, false)) -// } yield Mount(mountType, description, Some(MountConfig(defaultTtl, maxTtl, forceNoCache))) -// -// def processMountTypes(op: (Result[String, Response], String) => Result[String, -// Response]) = -// mountTypes.foldLeft[Result[String, Response]](Result.ok(new -// JDKResponse(null, null, null)))(op) -// -//} +package janstenpickle.vault.manage + +import com.ning.http.client.Response +import com.ning.http.client.providers.jdk.JDKResponse +import janstenpickle.vault.core.VaultSpec +import janstenpickle.vault.manage.Model.{Mount, MountConfig} +import org.scalacheck.{Gen, Prop} +import org.specs2.ScalaCheck +import janstenpickle.scala.syntax.AsyncResultSyntax._ +import janstenpickle.scala.result._ + +class MountIT extends VaultSpec with ScalaCheck { + import MountIT._ + import VaultSpec._ + + def is = + s2""" + Can enable, remount and disable a valid mount $happy + Can enable, list and then disable valid mounts $listSuccess + Cannot enable an invalid mount type $enableFail + """ + + lazy val underTest = new Mounts(config) + + def happy = Prop.forAllNoShrink( + mountGen, + longerStrGen, + longerStrGen, + Gen.option(longerStrGen))((mount, mountPoint, remountPoint, desc) => { + (underTest.mount(mount.`type`, Some(mountPoint), desc, Some(mount)) + .attemptRun must beRight) and + (underTest.remount(mountPoint, remountPoint) + .attemptRun must beRight) and + (underTest.delete(remountPoint) + .attemptRun must beRight) and + (underTest.delete(mountPoint) + .attemptRun must beRight) + }) + + def listSuccess = { + processMountTypes((acc, mount) => + acc.right.flatMap(_ => underTest.mount(mount).attemptRun) + ) must beRight + } and { + underTest.list.attemptRun must beRight.like { + case a => a.map(_._2.`type`) must containAllOf(mountTypes) + } + } and { + processMountTypes((acc, mount) => + acc.right.flatMap(_ => underTest.delete(mount).attemptRun) + ) must beRight + } + + def enableFail = Prop.forAllNoShrink( + longerStrGen.suchThat(!mountTypes.contains(_)), + longerStrGen, + Gen.option(longerStrGen))((`type`, mount, desc) => + underTest.mount(`type`, Some(mount), desc) + .attemptRun must beLeft + ) + +} + +object MountIT { + import VaultSpec._ + + val mountTypes = List( + "aws", "cassandra", "consul", "generic", + "mssql", "mysql", "pki", "postgresql", "ssh", "transit" + ) + val mount = Gen.oneOf(mountTypes) + val mounts = Gen.listOf(mountTypes).suchThat(_.nonEmpty) + + val mountGen = for { + mountType <- mount + description <- Gen.option(longerStrGen) + defaultTtl <- Gen.option(Gen.posNum[Int]) + maxTtl <- Gen.option(Gen.posNum[Int]) + forceNoCache <- Gen.option(Gen.oneOf(true, false)) + } yield Mount(mountType, description, Some(MountConfig(defaultTtl, maxTtl, forceNoCache))) + + def processMountTypes(op: (Result[String, Response], String) => Result[String, + Response]) = + mountTypes.foldLeft[Result[String, Response]](Result.pure(new + JDKResponse(null, null, null)))(op) + +} diff --git a/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala index 1abd4e6..88824b8 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/PolicyIT.scala @@ -1,57 +1,58 @@ -//package janstenpickle.vault.manage -// -//import janstenpickle.vault.core.VaultSpec -//import janstenpickle.vault.manage.Model.Rule -//import org.scalacheck.{Gen, Prop} -//import org.specs2.ScalaCheck -//import uscala.result.Result -// -//class PolicyIT extends VaultSpec with ScalaCheck { -// import PolicyIT._ -// import VaultSpec._ -// -// override def is = -// s2""" -// Can successfully set and get policies $happy -// Cannot set an invalid policy $sad -// """ -// -// lazy val underTest = Policy(config) -// -// def happy = Prop.forAllNoShrink( -// longerStrGen, -// Gen.listOf(ruleGen(longerStrGen, policyGen, capabilitiesGen)). -// suchThat(_.nonEmpty)) { (name, rules) => -// (underTest.set(name.toLowerCase, rules) -// .attemptRun(_.getMessage()) must beOk) and -// (underTest.inspect(name.toLowerCase) -// .attemptRun(_.getMessage()) must beOk) and -// (underTest.delete(name.toLowerCase).attemptRun(_.getMessage()) must beOk) -// } -// -// // cannot use generated values here as -// // vault seems to have a failure rate limit -// def sad = underTest.set( -// "nic", List(Rule("cage", Some(List("kim", "copolla")))) -// ).attemptRun(_.getMessage()) must beFail -//} -// -//object PolicyIT { -// val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) -// val capabilitiesGen = -// Gen.listOf(Gen.oneOf( -// "create", "read", "update", "delete", "list", "sudo", "deny")). -// suchThat(_.nonEmpty). -// map(_.distinct) -// -// def ruleGen( -// pathGen: Gen[String], -// polGen: Gen[Option[String]], -// capGen: Gen[List[String]] -// ) = for { -// path <- pathGen -// policy <- polGen -// capabilities <- capGen -// } yield Rule(path, Some(capabilities), policy) -//} -// +package janstenpickle.vault.manage + +import janstenpickle.vault.core.VaultSpec +import janstenpickle.vault.manage.Model.Rule +import org.scalacheck.{Gen, Prop} +import org.specs2.ScalaCheck +import janstenpickle.scala.syntax.AsyncResultSyntax._ +import org.specs2.matcher.EitherMatchers + +class PolicyIT extends VaultSpec with ScalaCheck with EitherMatchers { + import PolicyIT._ + import VaultSpec._ + + override def is = + s2""" + Can successfully set and get policies $happy + Cannot set an invalid policy $sad + """ + + lazy val underTest = Policy(config) + + def happy = Prop.forAllNoShrink( + longerStrGen, + Gen.listOf(ruleGen(longerStrGen, policyGen, capabilitiesGen)). + suchThat(_.nonEmpty)) { (name, rules) => + (underTest.set(name.toLowerCase, rules) + .attemptRun must beRight) and + (underTest.inspect(name.toLowerCase) + .attemptRun must beRight) and + (underTest.delete(name.toLowerCase).attemptRun must beRight) + } + + // cannot use generated values here as + // vault seems to have a failure rate limit + def sad = underTest.set( + "nic", List(Rule("cage", Some(List("kim", "copolla")))) + ).attemptRun must beLeft +} + +object PolicyIT { + val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) + val capabilitiesGen = + Gen.listOf(Gen.oneOf( + "create", "read", "update", "delete", "list", "sudo", "deny")). + suchThat(_.nonEmpty). + map(_.distinct) + + def ruleGen( + pathGen: Gen[String], + polGen: Gen[Option[String]], + capGen: Gen[List[String]] + ) = for { + path <- pathGen + policy <- polGen + capabilities <- capGen + } yield Rule(path, Some(capabilities), policy) +} + diff --git a/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala index c2db978..6c5f2bb 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/UserPassIT.scala @@ -1,51 +1,53 @@ -//package janstenpickle.vault.manage -// -//import janstenpickle.vault.core.VaultSpec -//import org.scalacheck.{Gen, Prop} -//import org.specs2.ScalaCheck -// -//class UserPassIT extends VaultSpec with ScalaCheck { -// import UserPassIT._ -// import VaultSpec._ -// -// def is = -// s2""" -// Can create, update and delete a user $good -// Cannot create a user for a non-existent client $badClient -// Cannot create user with a bad policy $badPolicy -// """ -// -// lazy val underTest = UserPass(config) -// lazy val authAdmin = Auth(config) -// -// def good = Prop.forAllNoShrink(longerStrGen, longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen, policyGen)( -// (username, password, newPassword, ttl, client, policy) => -// (authAdmin.enable("userpass", Some(client)).attemptRun(_.getMessage()) must beOk) and -// (underTest.create(username, password, ttl, None, client).attemptRun(_.getMessage()) must beOk) and -// (underTest.setPassword(username, newPassword, client).attemptRun(_.getMessage()) must beOk) and -// (underTest.setPolicies(username, policy, client).attemptRun(_.getMessage()) must beOk) and -// (underTest.delete(username, client).attemptRun(_.getMessage()) must beOk) and -// (authAdmin.disable(client).attemptRun(_.getMessage()) must beOk) -// ) -// -// def badClient = Prop.forAllNoShrink(longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen)( -// (username, password, ttl, client) => -// underTest.create(username, password, ttl, None, client).attemptRun(_.getMessage()) must beFail -// ) -// -// def badPolicy = Prop.forAllNoShrink(longerStrGen, -// longerStrGen, -// Gen.posNum[Int], -// longerStrGen, -// Gen.listOf(longerStrGen.suchThat(!policies.contains(_))))( -// (username, password, ttl, client, policy) => -// (authAdmin.enable("userpass", Some(client)).attemptRun(_.getMessage()) must beOk) and -// (underTest.create(username, password, ttl, Some(policy), client).attemptRun(_.getMessage()) must beOk) and -// (authAdmin.disable(client).attemptRun(_.getMessage()) must beOk) -// ) -//} -// -//object UserPassIT { -// val policies = List("default", "root") -// val policyGen = Gen.listOf(Gen.oneOf(policies)) -//} \ No newline at end of file +package janstenpickle.vault.manage + +import janstenpickle.vault.core.VaultSpec +import org.scalacheck.{Gen, Prop} +import org.specs2.ScalaCheck +import janstenpickle.scala.syntax.AsyncResultSyntax._ +import org.specs2.matcher.EitherMatchers + +class UserPassIT extends VaultSpec with ScalaCheck with EitherMatchers { + import UserPassIT._ + import VaultSpec._ + + def is = + s2""" + Can create, update and delete a user $good + Cannot create a user for a non-existent client $badClient + Cannot create user with a bad policy $badPolicy + """ + + lazy val underTest = UserPass(config) + lazy val authAdmin = Auth(config) + + def good = Prop.forAllNoShrink(longerStrGen, longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen, policyGen)( + (username, password, newPassword, ttl, client, policy) => + (authAdmin.enable("userpass", Some(client)).attemptRun must beRight) and + (underTest.create(username, password, ttl, None, client).attemptRun must beRight) and + (underTest.setPassword(username, newPassword, client).attemptRun must beRight) and + (underTest.setPolicies(username, policy, client).attemptRun must beRight) and + (underTest.delete(username, client).attemptRun must beRight) and + (authAdmin.disable(client).attemptRun must beRight) + ) + + def badClient = Prop.forAllNoShrink(longerStrGen, longerStrGen, Gen.posNum[Int], longerStrGen)( + (username, password, ttl, client) => + underTest.create(username, password, ttl, None, client).attemptRun must beLeft + ) + + def badPolicy = Prop.forAllNoShrink(longerStrGen, + longerStrGen, + Gen.posNum[Int], + longerStrGen, + Gen.listOf(longerStrGen.suchThat(!policies.contains(_))))( + (username, password, ttl, client, policy) => + (authAdmin.enable("userpass", Some(client)).attemptRun must beRight) and + (underTest.create(username, password, ttl, Some(policy), client).attemptRun must beRight) and + (authAdmin.disable(client).attemptRun must beRight) + ) +} + +object UserPassIT { + val policies = List("default", "root") + val policyGen = Gen.listOf(Gen.oneOf(policies)) +} \ No newline at end of file diff --git a/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala b/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala index 50e1db0..a41e1b5 100644 --- a/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala +++ b/manage/src/test/scala/janstenpickle/vault/manage/RuleSpec.scala @@ -1,51 +1,51 @@ -//package janstenpickle.vault.manage -// -//import janstenpickle.vault.manage.Model.Rule -//import org.scalacheck.{Gen, Prop} -//import org.specs2.{ScalaCheck, Specification} -//import uscala.result.specs2.ResultMatchers -// -//class RuleSpec extends Specification with ScalaCheck with ResultMatchers { -// import RuleSpec._ -// -// override def is = -// s2""" -// Can encode and decode policy strings $passes -// Cannot decode bad policy strings $fails -// """ -// -// def passes = Prop.forAllNoShrink(Gen.listOf(ruleGen).suchThat(_.nonEmpty)) (rules => -// Rule.decode(rules.map(_.encode).mkString("\n")) must beOk.like { -// case a => a must containAllOf(rules) -// } -// ) -// -// def fails = Prop.forAllNoShrink(Gen.listOf(badRuleGen).suchThat(_.nonEmpty)) (rules => -// Rule.decode(rules.mkString("\n")) must beFail -// ) -//} -// -//object RuleSpec { -// val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) -// val capabilitiesGen = Gen.option( -// Gen.listOf(Gen.oneOf("create", "read", "update", "delete", "list", "sudo", "deny")). -// suchThat(_.nonEmpty). -// map(_.distinct) -// ) -// -// val ruleGen = for { -// path <- Gen.alphaStr.suchThat(_.nonEmpty) -// policy <- policyGen -// capabilities <- capabilitiesGen -// } yield Rule(path, capabilities, policy) -// -// val badRuleGen = for { -// path <- Gen.alphaStr.suchThat(_.nonEmpty) -// policy <- policyGen -// capabilities <- capabilitiesGen -// } yield -// s""" -// |path "$path" -// | $policy cage -// | $capabilities }""".stripMargin('|') -//} +package janstenpickle.vault.manage + +import janstenpickle.vault.manage.Model.Rule +import org.scalacheck.{Gen, Prop} +import org.specs2.matcher.EitherMatchers +import org.specs2.{ScalaCheck, Specification} + +class RuleSpec extends Specification with ScalaCheck with EitherMatchers { + import RuleSpec._ + + override def is = + s2""" + Can encode and decode policy strings $passes + Cannot decode bad policy strings $fails + """ + + def passes = Prop.forAllNoShrink(Gen.listOf(ruleGen).suchThat(_.nonEmpty)) (rules => + Rule.decode(rules.map(_.encode).mkString("\n")) must beRight.like { + case a => a must containAllOf(rules) + } + ) + + def fails = Prop.forAllNoShrink(Gen.listOf(badRuleGen).suchThat(_.nonEmpty)) (rules => + Rule.decode(rules.mkString("\n")) must beLeft + ) +} + +object RuleSpec { + val policyGen = Gen.option(Gen.oneOf("read", "write", "sudo", "deny")) + val capabilitiesGen = Gen.option( + Gen.listOf(Gen.oneOf("create", "read", "update", "delete", "list", "sudo", "deny")). + suchThat(_.nonEmpty). + map(_.distinct) + ) + + val ruleGen = for { + path <- Gen.alphaStr.suchThat(_.nonEmpty) + policy <- policyGen + capabilities <- capabilitiesGen + } yield Rule(path, capabilities, policy) + + val badRuleGen = for { + path <- Gen.alphaStr.suchThat(_.nonEmpty) + policy <- policyGen + capabilities <- capabilitiesGen + } yield + s""" + |path "$path" + | $policy cage + | $capabilities }""".stripMargin('|') +} From 4a3659b94bddf4897241694f980f6e4ba8bcf08e Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Thu, 21 Dec 2017 15:28:50 +0200 Subject: [PATCH 12/16] Update tests in auth --- .../janstenpickle/vault/auth/UserPassIT.scala | 157 +++++++++--------- 1 file changed, 79 insertions(+), 78 deletions(-) diff --git a/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala b/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala index 0a1aa00..e654e58 100644 --- a/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala +++ b/auth/src/it/scala/janstenpickle/vault/auth/UserPassIT.scala @@ -1,78 +1,79 @@ -//package janstenpickle.vault.auth -// -//import janstenpickle.vault.core.VaultSpec -//import janstenpickle.vault.manage.Auth -//import org.scalacheck.{Gen, Prop} -//import org.specs2.ScalaCheck -//import org.specs2.matcher.MatchResult -// -//class UserPassIT extends VaultSpec with ScalaCheck { -// import VaultSpec._ -// -// override def is = -// s2""" -// Can authenticate a user against a specific "client" path $authPass -// Fails to authenticate a user $end -// against a bad "client" path $badClient -// with a non-existent username $badUser -// with a bad password $badPassword -// """ -// -// lazy val underTest = UserPass(config.wsClient) -// lazy val authAdmin = Auth(config) -// lazy val userAdmin = janstenpickle.vault.manage.UserPass(config) -// -// def setupClient(client: String) = authAdmin.enable("userpass", Some(client)) -// .attemptRun(_.getMessage()) must beOk -// -// def setupUser(username: String, password: String, client: String) = -// userAdmin.create(username, password, 30, None, client) -// .attemptRun(_.getMessage()) -// -// def removeClient(client: String) = -// authAdmin.disable(client).attemptRun(_.getMessage()) must beOk -// -// def authPass = test((username, password, client, ttl) => -// setupClient(client) and -// (setupUser(username, password, client) must beOk) and -// (underTest.authenticate(username, password, ttl, client) -// .attemptRun(_.getMessage()) must beOk) and -// removeClient(client) -// ) -// -// // TODO: test below may fail rarely (e.g. client is same as badClientName) -// -// def badClient = test{ (username, password, client, ttl) => -// val badClientName = "nic-kim-cage-client" -// setupClient(badClientName) and -// (setupUser(username, password, client) must beFail) and -// (underTest.authenticate(username, password, ttl, client) -// .attemptRun(_.getMessage()) must beFail) and -// removeClient(badClientName) -// } -// -// def badUser = test{ (username, password, client, ttl) => -// val badUserName = "nic-kim-cage-user" -// setupClient(client) and -// (setupUser(username, password, client) must beOk) and -// (underTest.authenticate(badUserName, password, ttl, client) -// .attemptRun(_.getMessage()) must beFail) and -// removeClient(client) -// } -// -// def badPassword = test{ (username, password, client, ttl) => -// val badPasswordValue = "nic-kim-cage-password" -// setupClient(client) and -// (setupUser(username, password, client) must beOk) and -// (underTest.authenticate(username, badPasswordValue, ttl, client) -// .attemptRun(_.getMessage()) must beFail) and -// removeClient(client) -// } -// -// def test(op: (String, String, String, Int) => MatchResult[Any]) = -// Prop.forAllNoShrink( -// longerStrGen, -// longerStrGen, -// Gen.numStr.suchThat(_.nonEmpty), Gen.posNum[Int] -// )(op) -//} +package janstenpickle.vault.auth + +import janstenpickle.scala.syntax.AsyncResultSyntax._ +import janstenpickle.vault.core.VaultSpec +import janstenpickle.vault.manage.Auth +import org.scalacheck.{Gen, Prop} +import org.specs2.ScalaCheck +import org.specs2.matcher.MatchResult + +class UserPassIT extends VaultSpec with ScalaCheck { + import VaultSpec._ + + override def is = + s2""" + Can authenticate a user against a specific "client" path $authPass + Fails to authenticate a user $end + against a bad "client" path $badClient + with a non-existent username $badUser + with a bad password $badPassword + """ + + lazy val underTest = UserPass(config.wsClient) + lazy val authAdmin = Auth(config) + lazy val userAdmin = janstenpickle.vault.manage.UserPass(config) + + def setupClient(client: String) = authAdmin.enable("userpass", Some(client)) + .attemptRun must beRight + + def setupUser(username: String, password: String, client: String) = + userAdmin.create(username, password, 30, None, client) + .attemptRun + + def removeClient(client: String) = + authAdmin.disable(client).attemptRun must beRight + + def authPass = test((username, password, client, ttl) => + setupClient(client) and + (setupUser(username, password, client) must beRight) and + (underTest.authenticate(username, password, ttl, client) + .attemptRun must beRight) and + removeClient(client) + ) + + // TODO: test below may fail rarely (e.g. client is same as badClientName) + + def badClient = test{ (username, password, client, ttl) => + val badClientName = "nic-kim-cage-client" + setupClient(badClientName) and + (setupUser(username, password, client) must beLeft) and + (underTest.authenticate(username, password, ttl, client) + .attemptRun must beLeft) and + removeClient(badClientName) + } + + def badUser = test{ (username, password, client, ttl) => + val badUserName = "nic-kim-cage-user" + setupClient(client) and + (setupUser(username, password, client) must beRight) and + (underTest.authenticate(badUserName, password, ttl, client) + .attemptRun must beLeft) and + removeClient(client) + } + + def badPassword = test{ (username, password, client, ttl) => + val badPasswordValue = "nic-kim-cage-password" + setupClient(client) and + (setupUser(username, password, client) must beRight) and + (underTest.authenticate(username, badPasswordValue, ttl, client) + .attemptRun must beLeft) and + removeClient(client) + } + + def test(op: (String, String, String, Int) => MatchResult[Any]) = + Prop.forAllNoShrink( + longerStrGen, + longerStrGen, + Gen.numStr.suchThat(_.nonEmpty), Gen.posNum[Int] + )(op) +} From 0c2d929c421fe43a1f3c06079382cb831ebfd7c8 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Tue, 26 Dec 2017 11:02:09 +0200 Subject: [PATCH 13/16] Bump cats to 1.0.0 --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 5250fbc..67e7db7 100644 --- a/build.sbt +++ b/build.sbt @@ -6,8 +6,8 @@ lazy val scalaVersion211 = "2.11.11" lazy val scalaVersion212 = "2.12.4" lazy val specs2Version = "3.8.8" -lazy val catsVersion = "1.0.0-RC1" -lazy val circeVersion = "0.9.0-M2" +lazy val catsVersion = "1.0.0" +lazy val circeVersion = "0.9.0-M3" lazy val dispatchVersion = "0.11.3" lazy val startVaultTask = TaskKey[Unit]( "startVaultTask", From 96b57cbf32e28b6257915f978542a56cb32c650a Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Tue, 26 Dec 2017 11:30:14 +0200 Subject: [PATCH 14/16] Make AsyncResult lhs String, remove type parameter --- .../janstenpickle/vault/auth/UserPass.scala | 2 +- .../janstenpickle/scala/result/package.scala | 12 +++--- .../janstenpickle/scala/syntax/syntax.scala | 42 +++++++++---------- .../janstenpickle/vault/core/Secrets.scala | 12 +++--- .../vault/core/VaultConfig.scala | 8 ++-- .../janstenpickle/vault/manage/MountIT.scala | 5 +-- .../janstenpickle/vault/manage/UserPass.scala | 8 ++-- .../janstenpickle/vault/manage/manage.scala | 24 +++++------ 8 files changed, 57 insertions(+), 56 deletions(-) diff --git a/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala b/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala index e60d4c8..11c636a 100644 --- a/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala +++ b/auth/src/main/scala/janstenpickle/vault/auth/UserPass.scala @@ -16,7 +16,7 @@ case class UserPass(wsClient: WSClient) { password: String, ttl: Int, client: String = "userpass" - )(implicit ec: ExecutionContext): AsyncResult[String, UserPassResponse] = + )(implicit ec: ExecutionContext): AsyncResult[UserPassResponse] = wsClient.path(s"auth/$client/login/$username"). post(Map("password" -> password, "ttl" -> s"${ttl}s")). toAsyncResult. diff --git a/core/src/main/scala/janstenpickle/scala/result/package.scala b/core/src/main/scala/janstenpickle/scala/result/package.scala index 22cd325..bb85a98 100644 --- a/core/src/main/scala/janstenpickle/scala/result/package.scala +++ b/core/src/main/scala/janstenpickle/scala/result/package.scala @@ -7,21 +7,21 @@ import cats.implicits._ import scala.language.postfixOps package object result { - type Result[F, R] = Either[F, R] + type Result[R] = Either[String, R] val Result: Either.type = Either implicit class ResultCompanionOps(dc: Either.type ){ - def pure[F, R](r: R): Either[F, R] = Either right r + def pure[R](r: R): Either[String, R] = Either right r - def fail[F, R](f: F): Either[F, R] = Either left f + def fail[R](f: String): Either[String, R] = Either left f } - type AsyncResult[F, R] = Future[Result[F, R]] + type AsyncResult[R] = Future[Result[R]] - type AsyncEitherT[F, R] = cats.data.EitherT[Future, F, R] + type AsyncEitherT[R] = cats.data.EitherT[Future, String, R] object AsyncResult { - def pure[F, R](r: R): AsyncResult[F, R] = { + def pure[R](r: R): AsyncResult[R] = { Future.successful(Either right r) } } diff --git a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala index b1f83a9..b9880a1 100644 --- a/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala +++ b/core/src/main/scala/janstenpickle/scala/syntax/syntax.scala @@ -26,37 +26,37 @@ object AsyncResultSyntax { implicit class FutureToAsyncResult[T](future: Future[T]) (implicit ec: ExecutionContext) { - def toAsyncResult: AsyncResult[String, T] = { - future.map(Result.pure[String, T]).recover { - case NonFatal(e) => Result.fail[String, T](f = e.getMessage) + def toAsyncResult: AsyncResult[T] = { + future.map(Result.pure[T]).recover { + case NonFatal(e) => Result.fail[T](f = e.getMessage) } } } - implicit class AsyncResultOps[F, R](f: AsyncResult[F, R]) { - def eiT: AsyncEitherT[F, R] = EitherT[Future, F, R](f) + implicit class AsyncResultOps[R](f: AsyncResult[R]) { + def eiT: AsyncEitherT[R] = EitherT[Future, String, R](f) - def attemptRun(implicit ec: ExecutionContext): Result[F, R] = + def attemptRun(implicit ec: ExecutionContext): Result[R] = Await.result(f, 1 minute) } - implicit class AsyncResultOpsEitherT[F, R](f: Either[F, R]) { - def eiT(implicit ec: ExecutionContext): AsyncEitherT[F, R] = + implicit class AsyncResultOpsEitherT[R](f: Either[String, R]) { + def eiT(implicit ec: ExecutionContext): AsyncEitherT[R] = EitherT.fromEither[Future](f) } - implicit class AsyncResultOpsAny[F, R](r: R) { - def eiT: AsyncEitherT[F, R] = + implicit class AsyncResultOpsAny[R](r: R) { + def eiT: AsyncEitherT[R] = EitherT.apply(Future.successful(Either right r)) } implicit class ReqToAsyncResult(req: Req) (implicit ec: ExecutionContext) { - def toAsyncResult: AsyncResult[String, Response] = Http(req).toAsyncResult + def toAsyncResult: AsyncResult[Response] = Http(req).toAsyncResult } implicit def toAsyncResult[T](future: scala.concurrent.Future[T]) - (implicit ec: ExecutionContext): AsyncResult[String, T] = + (implicit ec: ExecutionContext): AsyncResult[T] = future.toAsyncResult } @@ -67,7 +67,7 @@ object VaultConfigSyntax { implicit class RequestHelper(config: VaultConfig) { def authenticatedRequest(path: String)(req: Req => Req) - (implicit ec: ExecutionContext): AsyncResult[String, Req] ={ + (implicit ec: ExecutionContext): AsyncResult[Req] ={ val r = for { token <- config.token.eiT } yield req(config.wsClient.path(path).setHeader(VaultTokenHeader, token)) @@ -79,11 +79,11 @@ object VaultConfigSyntax { object JsonSyntax { import AsyncResultSyntax._ - implicit class JsonHandler(json: AsyncResult[String, Json]) { + implicit class JsonHandler(json: AsyncResult[Json]) { def extractFromJson[T](jsonPath: HCursor => ACursor = _.downArray)( implicit decode: Decoder[T], ec: ExecutionContext - ): AsyncResult[String, T] = { + ): AsyncResult[T] = { val r = for { j <- json.eiT e <- decode.tryDecode(jsonPath(j.hcursor)).leftMap(_.message).eiT @@ -98,9 +98,9 @@ object ResponseSyntax { import AsyncResultSyntax._ import JsonSyntax._ - implicit class ResponseHandler(resp: AsyncResult[String, Response]) { + implicit class ResponseHandler(resp: AsyncResult[Response]) { def acceptStatusCodes(codes: Int*) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = { + (implicit ec: ExecutionContext): AsyncResult[Response] = { val r = for { response <- resp.eiT r <- Result.cond( @@ -114,7 +114,7 @@ object ResponseSyntax { } def extractJson(implicit ec: ExecutionContext): - AsyncResult[String, Json] = { + AsyncResult[Json] = { val r = for { response <- resp.eiT r <- parse(response.getResponseBody).leftMap(_.message).eiT @@ -127,7 +127,7 @@ object ResponseSyntax { ( implicit decode: Decoder[T], ec: ExecutionContext - ): AsyncResult[String, T] = + ): AsyncResult[T] = resp.extractJson.extractFromJson[T](jsonPath) } } @@ -135,9 +135,9 @@ object ResponseSyntax { object SyntaxRequest { import AsyncResultSyntax._ - implicit class ExecuteRequest(req: AsyncResult[String, Req]) + implicit class ExecuteRequest(req: AsyncResult[Req]) (implicit ec: ExecutionContext) { - def execute: AsyncResult[String, Response] = { + def execute: AsyncResult[Response] = { val r = for { request <- req.eiT response <- Http(request).toAsyncResult.eiT diff --git a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala index 7c37c07..82f0fdc 100644 --- a/core/src/main/scala/janstenpickle/vault/core/Secrets.scala +++ b/core/src/main/scala/janstenpickle/vault/core/Secrets.scala @@ -13,7 +13,7 @@ import scala.concurrent.ExecutionContext // scalastyle:off magic.number case class Secrets(config: VaultConfig, backend: String) { def get(key: String, subKey: String = "value") - (implicit ec: ExecutionContext): AsyncResult[String, String] = { + (implicit ec: ExecutionContext): AsyncResult[String] = { val r = for { x <- getAll(key).eiT r <- Either.fromOption( @@ -26,27 +26,27 @@ case class Secrets(config: VaultConfig, backend: String) { def getAll(key: String) - (implicit ec: ExecutionContext): AsyncResult[String, Map[String, String]] = + (implicit ec: ExecutionContext): AsyncResult[Map[String, String]] = config.authenticatedRequest(path(key))(_.get). execute. acceptStatusCodes(200). extractFromJson[Map[String, String]](_.downField("data")) def set(key: String, value: String) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = set(key, "value", value) def set(key: String, subKey: String, value: String) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = set(key, Map(subKey -> value)) def set(key: String, values: Map[String, String]) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(path(key))(_.post(values)). execute. acceptStatusCodes(204) - def list(implicit ec: ExecutionContext): AsyncResult[String, List[String]] = + def list(implicit ec: ExecutionContext): AsyncResult[List[String]] = config.authenticatedRequest(backend)( _.addQueryParameter("list", true.toString).get). execute. diff --git a/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala b/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala index 15b77c4..68495bf 100644 --- a/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala +++ b/core/src/main/scala/janstenpickle/vault/core/VaultConfig.scala @@ -12,7 +12,7 @@ import janstenpickle.scala.result._ import scala.concurrent.ExecutionContext -case class VaultConfig(wsClient: WSClient, token: AsyncResult[String, String]) +case class VaultConfig(wsClient: WSClient, token: AsyncResult[String]) @deprecated("Vault 0.6.5 deprecated AppId in favor of AppRole", "0.4.0") case class AppId(app_id: String, user_id: String) case class AppRole(role_id: String, secret_id: String) @@ -55,8 +55,10 @@ object VaultConfig { -case class WSClient(server: URL, - version: String = "v1") { +case class WSClient( + server: URL, + version: String = "v1" +) { def path(p: String): Req = url(s"${server.toString}/$version/$p"). setContentType("application/json", "UTF-8") diff --git a/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala b/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala index 09ab6ff..e95bc0a 100644 --- a/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala +++ b/manage/src/it/scala/janstenpickle/vault/manage/MountIT.scala @@ -79,9 +79,8 @@ object MountIT { forceNoCache <- Gen.option(Gen.oneOf(true, false)) } yield Mount(mountType, description, Some(MountConfig(defaultTtl, maxTtl, forceNoCache))) - def processMountTypes(op: (Result[String, Response], String) => Result[String, - Response]) = - mountTypes.foldLeft[Result[String, Response]](Result.pure(new + def processMountTypes(op: (Result[Response], String) => Result[Response]) = + mountTypes.foldLeft[Result[Response]](Result.pure(new JDKResponse(null, null, null)))(op) } diff --git a/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala b/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala index 03ae7ae..97a3a43 100644 --- a/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala +++ b/manage/src/main/scala/janstenpickle/vault/manage/UserPass.scala @@ -18,7 +18,7 @@ case class UserPass(config: VaultConfig) { ttl: Int, policies: Option[List[String]] = None, client: String = DefaultClient) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"auth/$client/users/$username")( _.post(policies.map(_.mkString(",")).toMap("policies") ++ Map("username" -> username, @@ -27,7 +27,7 @@ case class UserPass(config: VaultConfig) { ).execute.acceptStatusCodes(204) def delete(username: String, client: String = DefaultClient) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"auth/$client/users/$username")(_.delete). execute. acceptStatusCodes(204) @@ -36,7 +36,7 @@ case class UserPass(config: VaultConfig) { username: String, password: String, client: String = DefaultClient - )(implicit ec: ExecutionContext): AsyncResult[String, Response] = + )(implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"auth/$client/users/$username/password")( _.post(Map("username" -> username, "password" -> password)) ).execute.acceptStatusCodes(204) @@ -45,7 +45,7 @@ case class UserPass(config: VaultConfig) { username: String, policies: List[String], client: String = DefaultClient - )(implicit ec: ExecutionContext): AsyncResult[String, Response] = + )(implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"auth/$client/users/$username/policies")( _.post(Map("username" -> username, "policies" -> policies.mkString(","))) ).execute.acceptStatusCodes(204) diff --git a/manage/src/main/scala/janstenpickle/vault/manage/manage.scala b/manage/src/main/scala/janstenpickle/vault/manage/manage.scala index bc59fc7..00fb0e4 100644 --- a/manage/src/main/scala/janstenpickle/vault/manage/manage.scala +++ b/manage/src/main/scala/janstenpickle/vault/manage/manage.scala @@ -18,13 +18,13 @@ case class Auth(config: VaultConfig) { def enable(`type`: String, mountPoint: Option[String] = None, description: Option[String] = None) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"sys/auth/${mountPoint.getOrElse(`type`)}")( _.post(description.toMap("description") + ("type" -> `type`)) ).execute.acceptStatusCodes(204) def disable(mountPoint: String) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"sys/auth/$mountPoint")(_.delete). execute. acceptStatusCodes(204) @@ -32,13 +32,13 @@ case class Auth(config: VaultConfig) { case class Mounts(config: VaultConfig) { def remount(from: String, to: String) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest("sys/remount")( _.post(Map("from" -> from, "to" -> to)) ).execute.acceptStatusCodes(204) def list(implicit ec: ExecutionContext): - AsyncResult[String, Map[String, Mount]] = + AsyncResult[Map[String, Mount]] = config.authenticatedRequest("sys/mounts")(_.get). execute. acceptStatusCodes(200). @@ -48,13 +48,13 @@ case class Mounts(config: VaultConfig) { mountPoint: Option[String] = None, description: Option[String] = None, conf: Option[Mount] = None) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"sys/mounts/${mountPoint.getOrElse(`type`)}")( _.post(MountRequest(`type`, description, conf).asJson) ).execute.acceptStatusCodes(204) def delete(mountPoint: String) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"sys/mounts/$mountPoint")(_.delete). execute. acceptStatusCodes(204) @@ -62,7 +62,7 @@ case class Mounts(config: VaultConfig) { case class Policy(config: VaultConfig) { - def list(implicit ec: ExecutionContext): AsyncResult[String, List[String]] = + def list(implicit ec: ExecutionContext): AsyncResult[List[String]] = config.authenticatedRequest("sys/policy")(_.get). execute. acceptStatusCodes(200). @@ -70,21 +70,21 @@ case class Policy(config: VaultConfig) { // NOTE: `rules` is not valid Json def inspect(policy: String)(implicit ec: ExecutionContext): - AsyncResult[String, String] = + AsyncResult[String] = config.authenticatedRequest(s"sys/policy/$policy")(_.get). execute. acceptStatusCodes(200) .extractFromJson[String](_.downField("rules")) def set(policy: String, rules: List[Rule]) - (implicit ec: ExecutionContext): AsyncResult[String, Response] = + (implicit ec: ExecutionContext): AsyncResult[Response] = config.authenticatedRequest(s"sys/policy/$policy")( _.post(PolicySetting(policy, rules).asJson)). execute. acceptStatusCodes(204) def delete(policy: String)(implicit ec: ExecutionContext): - AsyncResult[String, Response] = + AsyncResult[Response] = config.authenticatedRequest(s"sys/policy/$policy")(_.delete). execute. acceptStatusCodes(204) @@ -104,7 +104,7 @@ object Model { ) case class PolicySetting(name: String, rules: Option[String]) { - lazy val decodeRules: Option[Result[String, List[Rule]]] = rules.filter( + lazy val decodeRules: Option[Result[List[Rule]]] = rules.filter( _.nonEmpty).map(Rule.decode) } object PolicySetting { @@ -136,7 +136,7 @@ object Model { val capabilitiesRegex = """\s+capabilities\s+=\s+\[(.+)\]""".r val policyRegex = """\s+policy\s+=\s+"(\S+)"""".r - def decode(ruleString: String): Result[String, List[Rule]] = { + def decode(ruleString: String): Result[List[Rule]] = { val rules = ruleString.split("""\s*}\s+\n""").toList val decoded = rules.foldLeft(List.empty[Rule])( (acc, v) => acc ++ pathRegex.findFirstMatchIn(v).map(_.group(1)).map(path => From f4e03dc629a70c4a0ed28309f1a56ed4f902276d Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Tue, 2 Jan 2018 17:07:43 +0200 Subject: [PATCH 15/16] Bump to cats 1.0.1 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 67e7db7..c44d21d 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ lazy val scalaVersion211 = "2.11.11" lazy val scalaVersion212 = "2.12.4" lazy val specs2Version = "3.8.8" -lazy val catsVersion = "1.0.0" +lazy val catsVersion = "1.0.1" lazy val circeVersion = "0.9.0-M3" lazy val dispatchVersion = "0.11.3" lazy val startVaultTask = TaskKey[Unit]( From e840eb46a76361aba9f0e97fe63f75e8a3862022 Mon Sep 17 00:00:00 2001 From: Lorand Szakacs Date: Wed, 10 Jan 2018 11:44:54 +0200 Subject: [PATCH 16/16] Bump circe version to 0.9.0 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index c44d21d..f72f7a6 100644 --- a/build.sbt +++ b/build.sbt @@ -7,7 +7,7 @@ lazy val scalaVersion212 = "2.12.4" lazy val specs2Version = "3.8.8" lazy val catsVersion = "1.0.1" -lazy val circeVersion = "0.9.0-M3" +lazy val circeVersion = "0.9.0" lazy val dispatchVersion = "0.11.3" lazy val startVaultTask = TaskKey[Unit]( "startVaultTask",