Skip to content

Commit

Permalink
Merge pull request #237 from Banno/parameterize-k8s-mountpoint
Browse files Browse the repository at this point in the history
Parameterize the Kubernetes mount point
  • Loading branch information
rossabaker committed Jul 26, 2021
2 parents b9f83d7 + 7e3ef37 commit 4525e41
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 14 deletions.
19 changes: 15 additions & 4 deletions core/src/main/scala/com/banno/vault/Vault.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,17 @@ object Vault {
} yield token
}


/**
* https://www.vaultproject.io/api/auth/kubernetes/index.html#login
*/
def kubernetesLogin[F[_]](client: Client[F], vaultUri: Uri)(role: String, jwt: String)(implicit F: Sync[F]): F[VaultToken] = {
* https://www.vaultproject.io/api/auth/kubernetes/index.html#login
*
* @param mountPoint The mount point of the Kubernetes auth method.
* Should start with a slash.
*/
def loginKubernetes[F[_]](client: Client[F], vaultUri: Uri)(role: String, jwt: String, mountPoint: String = "/auth/kubernetes")(implicit F: Sync[F]): F[VaultToken] = {
val request = Request[F](
method = Method.POST,
uri = vaultUri / "v1" / "auth" / "kubernetes" / "login"
uri = vaultUri.withPath(s"/v1${mountPoint}/login")
).withEntity(
Json.obj(
("role", Json.fromString(role)),
Expand All @@ -68,6 +72,13 @@ object Vault {
} yield token
}

/**
* https://www.vaultproject.io/api/auth/kubernetes/index.html#login
*/
@deprecated("Use loginKubernetes, which parameterizes the mount point", "7.1.2")
def kubernetesLogin[F[_]](client: Client[F], vaultUri: Uri)(role: String, jwt: String)(implicit F: Sync[F]): F[VaultToken] =
loginKubernetes(client, vaultUri)(role, jwt)

/**
* https://www.vaultproject.io/api/secret/kv/index.html#read-secret
*/
Expand Down
42 changes: 32 additions & 10 deletions core/src/test/scala/com/banno/vault/VaultSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class VaultSpec extends CatsEffectSuite with ScalaCheckEffectSuite with MissingP
val validKubernetesJwt: String = Random.alphanumeric.take(20).mkString //simulate a signed jwt https://www.vaultproject.io/api/auth/kubernetes/index.html#login

val clientToken: String = UUID.randomUUID().toString
val altClientToken: String = UUID.randomUUID().toString
val leaseDuration: Long = Random.nextLong()
val leaseId: String = UUID.randomUUID().toString
val renewable: Boolean = Random.nextBoolean()
Expand All @@ -116,6 +117,7 @@ class VaultSpec extends CatsEffectSuite with ScalaCheckEffectSuite with MissingP
val generateCertsPath: String = "pki/issue/ip"

val validToken = VaultToken(clientToken, leaseDuration, renewable)
val altValidToken = VaultToken(altClientToken, leaseDuration, renewable)

def mockVaultService[F[_]: Sync]: HttpRoutes[F] = {
object dsl extends Http4sDsl[F]
Expand Down Expand Up @@ -216,6 +218,20 @@ class VaultSpec extends CatsEffectSuite with ScalaCheckEffectSuite with MissingP
case _ =>
BadRequest("")
}
case req @ POST -> Root / "v1" / "auth" / "kubernetes2" / "login" =>
req.decodeJson[RoleAndJwt].flatMap {
case RoleAndJwt(`validKubernetesRole`, `validKubernetesJwt`) =>
Ok(s"""
|{
| "auth": {
| "client_token": "$altClientToken",
| "lease_duration": $leaseDuration,
| "renewable": $renewable
| }
|}""".stripMargin)
case _ =>
BadRequest("")
}
case req @ GET -> Root / "v1" / "secret" / "postgres1" / "password" =>
checkVaultToken(req){
Ok(s"""
Expand Down Expand Up @@ -330,44 +346,50 @@ test("login should fail when the response doesn't contains a lease duration") {
}
}

test("kubernetesLogin works as expected when sending valid role and jwt") {
test("loginKubernetes works as expected when sending valid role and jwt") {
PropF.forAllF(VaultArbitraries.validVaultUri) { uri =>
Vault.kubernetesLogin(mockClient, uri)(validKubernetesRole, validKubernetesJwt).assertEquals(validToken)
Vault.loginKubernetes(mockClient, uri)(validKubernetesRole, validKubernetesJwt).assertEquals(validToken)
}
}

test("kubernetesLogin should fail when sending an invalid roleId") {
test("loginKubernetes respects alternate mount points") {
PropF.forAllF(VaultArbitraries.validVaultUri) { uri =>
Vault.loginKubernetes(mockClient, uri)(validKubernetesRole, validKubernetesJwt, "/auth/kubernetes2").assertEquals(altValidToken)
}
}

test("loginKubernetes should fail when sending an invalid roleId") {
PropF.forAllF(VaultArbitraries.validVaultUri){uri =>
Vault.kubernetesLogin(mockClient, uri)(UUID.randomUUID().toString, validKubernetesJwt)
Vault.loginKubernetes(mockClient, uri)(UUID.randomUUID().toString, validKubernetesJwt)
.attempt
.map(_.isLeft)
.assert
}
}

test("kubernetesLogin should fail when the response is not a valid JSON") {
test("loginKubernetes should fail when the response is not a valid JSON") {
PropF.forAllF(VaultArbitraries.validVaultUri){uri =>
Vault.kubernetesLogin(mockClient, uri)(invalidJSONRoleId, validKubernetesJwt)
Vault.loginKubernetes(mockClient, uri)(invalidJSONRoleId, validKubernetesJwt)
.attempt
.map(_.isLeft)
.assert
}
}

test("kubernetesLogin should fail when the response doesn't contains a token") {
test("loginKubernetes should fail when the response doesn't contains a token") {
PropF.forAllF(VaultArbitraries.validVaultUri){uri =>
import org.http4s.DecodeFailure
Vault.kubernetesLogin(mockClient, uri)(roleIdWithoutToken, validKubernetesJwt)
Vault.loginKubernetes(mockClient, uri)(roleIdWithoutToken, validKubernetesJwt)
.attempt
.map(_.leftMap(_.isInstanceOf[DecodeFailure]))
.assertEquals(Left(true))
}
}

test("kubernetesLogin should fail when the response doesn't contains a lease duration") {
test("loginKubernetes should fail when the response doesn't contains a lease duration") {
PropF.forAllF(VaultArbitraries.validVaultUri){uri =>
import org.http4s.DecodeFailure
Vault.kubernetesLogin(mockClient, uri)(roleIdWithoutLease, validKubernetesJwt)
Vault.loginKubernetes(mockClient, uri)(roleIdWithoutLease, validKubernetesJwt)
.attempt
.map(_.leftMap(_.isInstanceOf[DecodeFailure]))
.assertEquals(Left(true))
Expand Down

0 comments on commit 4525e41

Please sign in to comment.