Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(javasdk): support multiple values in a static claim #1925

Merged
merged 3 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/src/modules/java/pages/using-jwts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ When the values of specific claims are known in advance, Kalix can be configured
@JWT(validate = JWT.JwtMethodMode.BEARER_TOKEN,
bearerTokenIssuer = "my-issuer",
staticClaims = {
@JWT.StaticClaim(claim = "role", value = "admin"),
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")})
@JWT.StaticClaim(claim = "role", value = {"admin", "editor"}), // <1>
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")}) // <2>
----
<1> When declaring multiple values for the same claim, **all** of them will be required when validating the request.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

<2> The value of the claim can be dependent on an environment variable, which will be resolved at runtime.

NOTE: For specifying an issuer claim (i.e. "iss"), you should still use the provided issuer-specific fields and not static claims.

Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object Dependencies {
val RuntimeVersion = System.getProperty(
"kalix-runtime.version",
// temporarily accept the old system property name
System.getProperty("kalix-proxy.version", "1.1.27"))
System.getProperty("kalix-proxy.version", "1.1.29"))
}

// changing the Scala version of the Java SDK affects end users
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ enum JwtMethodMode {

/**
* The value can be set as: a hardcoded literal (e.g. "admin"), an ENV variable (e.g "${ENV_VAR}")
* or a combination of both (e.g. "${ENV_VAR}-admin")
* or a combination of both (e.g. "${ENV_VAR}-admin").
* When declaring multiple values, ALL of those will be required when validating the claim.
*/
String value();
String[] value();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
package kalix.javasdk.impl

import java.lang.reflect.Method

import kalix.JwtMethodOptions
import kalix.JwtServiceOptions
import kalix.MethodOptions
import kalix.javasdk.annotations.JWT
import Reflect.Syntax._
import kalix.JwtStaticClaim

import scala.jdk.CollectionConverters.IterableHasAsJava

object JwtDescriptorFactory {

private def jwtMethodOptions(javaMethod: Method): JwtMethodOptions = {
Expand All @@ -34,9 +35,11 @@ object JwtDescriptorFactory {
.validate()
.map(springValidate => jwt.addValidate(JwtMethodOptions.JwtMethodMode.forNumber(springValidate.ordinal())))
ann.bearerTokenIssuer().map(jwt.addBearerTokenIssuer)

ann
.staticClaims()
.foreach(sc => jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).setValue(sc.value())))
.foreach(sc =>
jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).addAllValue(sc.value().toList.asJava)))
jwt.build()
}

Expand Down Expand Up @@ -66,7 +69,8 @@ object JwtDescriptorFactory {
ann.bearerTokenIssuer().map(jwt.addBearerTokenIssuer)
ann
.staticClaims()
.foreach(sc => jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).setValue(sc.value())))
.foreach(sc =>
jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).addAllValue(sc.value().toList.asJava)))

val kalixServiceOptions = kalix.ServiceOptions.newBuilder()
kalixServiceOptions.setJwt(jwt.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public static class ActionWithMethodLevelJWT extends Action {
bearerTokenIssuer = {"a", "b"},
staticClaims = {
@JWT.StaticClaim(claim = "role", value = "admin"),
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io"),
@JWT.StaticClaim(claim = "more-roles", value = {"viewer", "editor"})
})
public Action.Effect<Message> message(@RequestBody Message msg) {
return effects().reply(msg);
Expand All @@ -120,7 +121,8 @@ public Action.Effect<Message> message(@RequestBody Message msg) {
bearerTokenIssuer = {"a", "b"},
staticClaims = {
@JWT.StaticClaim(claim = "role", value = "admin"),
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io"),
@JWT.StaticClaim(claim = "more-roles", value = {"editor", "viewer"})
})
public static class ActionWithServiceLevelJWT extends Action {
@PostMapping("/message")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,13 @@ class ActionDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSu
jwtOption.getValidate(0) shouldBe JwtMethodMode.BEARER_TOKEN
assertRequestFieldJavaType(method, "json_body", JavaType.MESSAGE)

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
val Seq(claim1, claim2, claim3) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
claim3.getClaim shouldBe "more-roles"
claim3.getValueList.asScala.toSeq shouldBe Seq("viewer", "editor")
}
}

Expand All @@ -234,11 +236,13 @@ class ActionDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSu
jwtOption.getBearerTokenIssuer(1) shouldBe "b"
jwtOption.getValidate shouldBe JwtServiceMode.BEARER_TOKEN

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
val Seq(claim1, claim2, claim3) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
claim3.getClaim shouldBe "more-roles"
claim3.getValueList.asScala.toSeq shouldBe Seq("editor", "viewer")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ class EventSourcedEntityDescriptorFactorySpec extends AnyWordSpec with Component

val Seq(claim1, claim2) = jwtOption2.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "method-admin"
claim1.getValue(0) shouldBe "method-admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}"
claim2.getValue(0) shouldBe "${ENV}"
}
}

Expand All @@ -133,9 +133,9 @@ class EventSourcedEntityDescriptorFactorySpec extends AnyWordSpec with Component

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ class ValueEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDescrip
jwtOption.getValidate shouldBe JwtServiceMode.BEARER_TOKEN
val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand All @@ -119,9 +119,9 @@ class ValueEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDescrip
jwtOption.getValidate(0) shouldBe JwtMethodMode.BEARER_TOKEN
val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "method-admin"
claim1.getValue(0) shouldBe "method-admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}"
claim2.getValue(0) shouldBe "${ENV}"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,9 @@ class ViewDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSuit

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand All @@ -295,9 +295,9 @@ class ViewDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSuit

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ class WorkflowEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDesc

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "method-admin"
claim1.getValue(0) shouldBe "method-admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand All @@ -121,9 +121,9 @@ class WorkflowEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDesc

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}"
claim2.getValue(0) shouldBe "${ENV}"
}
}

Expand Down