diff --git a/extractor/src/test/suite/scala/com/digitalasset/extractor/AuthSpec.scala b/extractor/src/test/suite/scala/com/digitalasset/extractor/AuthSpec.scala index ec5ec4033e67..496deb9a73f8 100644 --- a/extractor/src/test/suite/scala/com/digitalasset/extractor/AuthSpec.scala +++ b/extractor/src/test/suite/scala/com/digitalasset/extractor/AuthSpec.scala @@ -7,7 +7,6 @@ import java.nio.file.Files import java.time.Duration import java.util.concurrent.atomic.AtomicReference -import com.daml.lf.data.Ref.Party import com.daml.extractor.config.{ExtractorConfig, SnapshotEndSetting} import com.daml.extractor.ledger.types.TransactionTree import com.daml.extractor.targets.TextPrintTarget @@ -20,7 +19,9 @@ import com.daml.ledger.api.v1.command_service.{CommandServiceGrpc, SubmitAndWait import com.daml.ledger.api.v1.ledger_offset.LedgerOffset import com.daml.ledger.client.services.commands.SynchronousCommandClient import com.daml.ledger.service.LedgerReader.PackageStore -import com.daml.platform.sandbox.services.{SandboxFixtureWithAuth, TestCommands} +import com.daml.lf.data.Ref.Party +import com.daml.platform.sandbox.SandboxRequiringAuthorization +import com.daml.platform.sandbox.services.{SandboxFixture, TestCommands} import com.daml.timer.Delayed import org.scalatest.{AsyncFlatSpec, Matchers} import org.slf4j.LoggerFactory @@ -34,7 +35,8 @@ import scala.util.{Failure, Success} final class AuthSpec extends AsyncFlatSpec - with SandboxFixtureWithAuth + with SandboxFixture + with SandboxRequiringAuthorization with SuiteResourceManagementAroundAll with Matchers with TestCommands { diff --git a/language-support/scala/codegen-sample-app/BUILD.bazel b/language-support/scala/codegen-sample-app/BUILD.bazel index ce8ef72739d0..43395a07db53 100644 --- a/language-support/scala/codegen-sample-app/BUILD.bazel +++ b/language-support/scala/codegen-sample-app/BUILD.bazel @@ -139,6 +139,7 @@ da_scala_test( "//ledger-api/rs-grpc-bridge", "//ledger-api/testing-utils", "//ledger/caching", + "//ledger/ledger-api-auth", "//ledger/ledger-api-client", "//ledger/ledger-api-common", "//ledger/participant-integration-api", diff --git a/ledger/ledger-api-client/BUILD.bazel b/ledger/ledger-api-client/BUILD.bazel index 1d54cc2afbad..b9ea13a7e813 100644 --- a/ledger/ledger-api-client/BUILD.bazel +++ b/ledger/ledger-api-client/BUILD.bazel @@ -71,6 +71,7 @@ da_scala_test_suite( "//ledger-api/rs-grpc-bridge", "//ledger-api/testing-utils", "//ledger/caching", + "//ledger/ledger-api-auth", "//ledger/ledger-api-common", "//ledger/ledger-api-domain", "//ledger/participant-integration-api", @@ -83,6 +84,7 @@ da_scala_test_suite( "//ledger/sandbox-common:sandbox-common-scala-tests-lib", "//ledger/test-common", "//libs-scala/direct-execution-context", + "//libs-scala/grpc-utils", "//libs-scala/ports", "//libs-scala/resources", "@maven//:ch_qos_logback_logback_classic", diff --git a/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientAuthIT.scala b/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientAuthIT.scala new file mode 100644 index 000000000000..946162b7c9a2 --- /dev/null +++ b/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientAuthIT.scala @@ -0,0 +1,99 @@ +// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.ledger.client + +import com.daml.grpc.GrpcException +import com.daml.ledger.api.domain +import com.daml.ledger.api.testing.utils.{AkkaBeforeAndAfterAll, SuiteResourceManagementAroundEach} +import com.daml.ledger.client.configuration.{ + CommandClientConfiguration, + LedgerClientConfiguration, + LedgerIdRequirement +} +import com.daml.platform.common.LedgerIdMode +import com.daml.platform.sandbox.SandboxRequiringAuthorization +import com.daml.platform.sandbox.config.SandboxConfig +import com.daml.platform.sandboxnext.SandboxNextFixture +import org.scalatest.{AsyncWordSpec, Inside, Matchers} + +final class LedgerClientAuthIT + extends AsyncWordSpec + with Matchers + with Inside + with AkkaBeforeAndAfterAll + with SuiteResourceManagementAroundEach + with SandboxNextFixture + with SandboxRequiringAuthorization { + + private val LedgerId = + domain.LedgerId(s"${classOf[LedgerClientAuthIT].getSimpleName.toLowerCase}-ledger-id") + + private val ClientConfigurationWithoutToken = LedgerClientConfiguration( + applicationId = classOf[LedgerClientAuthIT].getSimpleName, + ledgerIdRequirement = LedgerIdRequirement.none, + commandClient = CommandClientConfiguration.default, + sslContext = None, + token = None, + ) + + private val ClientConfiguration = ClientConfigurationWithoutToken.copy( + token = Some(toHeader(readOnlyToken("Read-only party"))), + ) + + override protected def config: SandboxConfig = super.config.copy( + ledgerIdMode = LedgerIdMode.Static(LedgerId), + ) + + "the ledger client" when { + "it has a read-only token" should { + "retrieve the ledger ID" in { + for { + client <- LedgerClient(channel, ClientConfiguration) + } yield { + client.ledgerId should be(LedgerId) + } + } + + "fail to conduct an admin operation with the same token" in { + for { + client <- LedgerClient(channel, ClientConfiguration) + exception <- client.partyManagementClient + .allocateParty(hint = Some("Bob"), displayName = None) + .failed + } yield { + inside(exception) { + case GrpcException.PERMISSION_DENIED() => succeed + } + } + } + + "succeed in conducting an admin operation with an admin token" in { + val partyName = "Carol" + for { + client <- LedgerClient(channel, ClientConfiguration) + allocatedParty <- client.partyManagementClient + .allocateParty( + hint = Some(partyName), + displayName = Some(partyName), + token = Some(toHeader(adminToken)), + ) + } yield { + allocatedParty.displayName should be(Some(partyName)) + } + } + } + + "it does not have a token" should { + "fail to construct" in { + for { + exception <- LedgerClient(channel, ClientConfigurationWithoutToken).failed + } yield { + inside(exception) { + case GrpcException.UNAUTHENTICATED() => succeed + } + } + } + } + } +} diff --git a/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientIT.scala b/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientIT.scala index 41cd91112983..e4b873f803f0 100644 --- a/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientIT.scala +++ b/ledger/ledger-api-client/src/it/scala/com/digitalasset/ledger/client/LedgerClientIT.scala @@ -3,15 +3,20 @@ package com.daml.ledger.client +import com.daml.ledger.api.domain import com.daml.ledger.api.testing.utils.{AkkaBeforeAndAfterAll, SuiteResourceManagementAroundEach} import com.daml.ledger.client.configuration.{ CommandClientConfiguration, LedgerClientConfiguration, LedgerIdRequirement } +import com.daml.lf.data.Ref +import com.daml.platform.common.LedgerIdMode +import com.daml.platform.sandbox.config.SandboxConfig import com.daml.platform.sandboxnext.SandboxNextFixture import io.grpc.ManagedChannel import org.scalatest.{AsyncWordSpec, Inside, Matchers} +import scalaz.OneAnd final class LedgerClientIT extends AsyncWordSpec @@ -20,18 +25,48 @@ final class LedgerClientIT with AkkaBeforeAndAfterAll with SuiteResourceManagementAroundEach with SandboxNextFixture { + + private val LedgerId = + domain.LedgerId(s"${classOf[LedgerClientIT].getSimpleName.toLowerCase}-ledger-id") + + private val ClientConfiguration = LedgerClientConfiguration( + applicationId = classOf[LedgerClientIT].getSimpleName, + ledgerIdRequirement = LedgerIdRequirement.none, + commandClient = CommandClientConfiguration.default, + sslContext = None, + token = None, + ) + + override protected def config: SandboxConfig = super.config.copy( + ledgerIdMode = LedgerIdMode.Static(LedgerId), + ) + "the ledger client" should { - "shut down the channel when closed" in { - val clientConfig = LedgerClientConfiguration( - applicationId = classOf[LedgerClientIT].getSimpleName, - ledgerIdRequirement = LedgerIdRequirement.none, - commandClient = CommandClientConfiguration.default, - sslContext = None, - token = None, - ) + "retrieve the ledger ID" in { + for { + client <- LedgerClient(channel, ClientConfiguration) + } yield { + client.ledgerId should be(LedgerId) + } + } + "make some requests" in { + val partyName = "Alice" + for { + client <- LedgerClient(channel, ClientConfiguration) + // The request type is irrelevant here; the point is that we can make some. + allocatedParty <- client.partyManagementClient + .allocateParty(hint = Some(partyName), displayName = None) + retrievedParties <- client.partyManagementClient + .getParties(OneAnd(Ref.Party.assertFromString(partyName), Set.empty)) + } yield { + retrievedParties should be(List(allocatedParty)) + } + } + + "shut down the channel when closed" in { for { - client <- LedgerClient(channel, clientConfig) + client <- LedgerClient(channel, ClientConfiguration) } yield { inside(channel) { case channel: ManagedChannel => diff --git a/ledger/sandbox-classic/BUILD.bazel b/ledger/sandbox-classic/BUILD.bazel index 7f6acb39bfcd..bb7fda312bd9 100644 --- a/ledger/sandbox-classic/BUILD.bazel +++ b/ledger/sandbox-classic/BUILD.bazel @@ -140,7 +140,6 @@ da_scala_library( "//language-support/scala/bindings", "//ledger-api/rs-grpc-bridge", "//ledger-api/testing-utils", - "//ledger-service/jwt", "//ledger/caching", "//ledger/ledger-api-auth", "//ledger/ledger-api-auth-client", @@ -163,12 +162,10 @@ da_scala_library( "//libs-scala/resources", "//libs-scala/timer-utils", "@maven//:ch_qos_logback_logback_classic", - "@maven//:com_auth0_java_jwt", "@maven//:com_typesafe_akka_akka_actor_2_12", "@maven//:com_typesafe_akka_akka_stream_2_12", "@maven//:com_typesafe_play_anorm_2_12", "@maven//:com_typesafe_play_anorm_tokenizer_2_12", - "@maven//:com_typesafe_scala_logging_scala_logging_2_12", "@maven//:io_dropwizard_metrics_metrics_core", "@maven//:org_scalactic_scalactic_2_12", "@maven//:org_scalatest_scalatest_2_12", diff --git a/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/auth/ServiceCallAuthTests.scala b/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/auth/ServiceCallAuthTests.scala index 070fc1c47409..e04c2b4acf09 100644 --- a/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/auth/ServiceCallAuthTests.scala +++ b/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/auth/ServiceCallAuthTests.scala @@ -11,7 +11,8 @@ import com.daml.ledger.api.auth.client.LedgerCallCredentials import com.daml.ledger.api.testing.utils.SuiteResourceManagementAroundAll import com.daml.ledger.api.v1.ledger_offset.LedgerOffset import com.daml.ledger.api.v1.transaction_filter.{Filters, TransactionFilter} -import com.daml.platform.sandbox.services.SandboxFixtureWithAuth +import com.daml.platform.sandbox.SandboxRequiringAuthorization +import com.daml.platform.sandbox.services.SandboxFixture import io.grpc.Status import io.grpc.stub.AbstractStub import org.scalatest.{Assertion, AsyncFlatSpec, Matchers} @@ -21,7 +22,8 @@ import scala.util.control.NonFatal trait ServiceCallAuthTests extends AsyncFlatSpec - with SandboxFixtureWithAuth + with SandboxFixture + with SandboxRequiringAuthorization with SuiteResourceManagementAroundAll with Matchers { @@ -46,7 +48,8 @@ trait ServiceCallAuthTests case NonFatal(e) => fail(e) } - protected def txFilterFor(party: String) = Some(TransactionFilter(Map(party -> Filters()))) + protected def txFilterFor(party: String): Option[TransactionFilter] = + Some(TransactionFilter(Map(party -> Filters()))) protected def ledgerBegin: LedgerOffset = LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN)) @@ -57,46 +60,47 @@ trait ServiceCallAuthTests expectUnauthenticated(serviceCallWithToken(None)) } - protected val canActAsRandomParty = + protected val canActAsRandomParty: Option[String] = Option(toHeader(readWriteToken(UUID.randomUUID.toString))) - protected val canActAsRandomPartyExpired = + protected val canActAsRandomPartyExpired: Option[String] = Option(toHeader(expiringIn(Duration.ofDays(-1), readWriteToken(UUID.randomUUID.toString)))) - protected val canActAsRandomPartyExpiresTomorrow = + protected val canActAsRandomPartyExpiresTomorrow: Option[String] = Option(toHeader(expiringIn(Duration.ofDays(1), readWriteToken(UUID.randomUUID.toString)))) - protected val canReadAsRandomParty = + protected val canReadAsRandomParty: Option[String] = Option(toHeader(readOnlyToken(UUID.randomUUID.toString))) - protected val canReadAsRandomPartyExpired = + protected val canReadAsRandomPartyExpired: Option[String] = Option(toHeader(expiringIn(Duration.ofDays(-1), readOnlyToken(UUID.randomUUID.toString)))) - protected val canReadAsRandomPartyExpiresTomorrow = + protected val canReadAsRandomPartyExpiresTomorrow: Option[String] = Option(toHeader(expiringIn(Duration.ofDays(1), readOnlyToken(UUID.randomUUID.toString)))) - protected val canReadAsAdmin = + protected val canReadAsAdmin: Option[String] = Option(toHeader(adminToken)) - protected val canReadAsAdminExpired = + protected val canReadAsAdminExpired: Option[String] = Option(toHeader(expiringIn(Duration.ofDays(-1), adminToken))) - protected val canReadAsAdminExpiresTomorrow = Option( - toHeader(expiringIn(Duration.ofDays(1), adminToken))) + protected val canReadAsAdminExpiresTomorrow: Option[String] = + Option(toHeader(expiringIn(Duration.ofDays(1), adminToken))) // Note: lazy val, because the ledger ID is only known after the sandbox start - protected lazy val canReadAsRandomPartyActualLedgerId = + protected lazy val canReadAsRandomPartyActualLedgerId: Option[String] = Option(toHeader(forLedgerId(unwrappedLedgerId, readOnlyToken(UUID.randomUUID.toString)))) - protected val canReadAsRandomPartyRandomLedgerId = + protected val canReadAsRandomPartyRandomLedgerId: Option[String] = Option(toHeader(forLedgerId(UUID.randomUUID.toString, readOnlyToken(UUID.randomUUID.toString)))) - protected val canReadAsRandomPartyActualParticipantId = + protected val canReadAsRandomPartyActualParticipantId: Option[String] = Option( toHeader(forParticipantId("sandbox-participant", readOnlyToken(UUID.randomUUID.toString)))) - protected val canReadAsRandomPartyRandomParticipantId = + protected val canReadAsRandomPartyRandomParticipantId: Option[String] = Option( toHeader(forParticipantId(UUID.randomUUID.toString, readOnlyToken(UUID.randomUUID.toString)))) // Note: lazy val, because the ledger ID is only known after the sandbox start - protected lazy val canReadAsAdminActualLedgerId = + protected lazy val canReadAsAdminActualLedgerId: Option[String] = Option(toHeader(forLedgerId(unwrappedLedgerId, adminToken))) - protected val canReadAsAdminRandomLedgerId = + protected val canReadAsAdminRandomLedgerId: Option[String] = Option(toHeader(forLedgerId(UUID.randomUUID.toString, adminToken))) - protected val canReadAsAdminActualParticipantId = + protected val canReadAsAdminActualParticipantId: Option[String] = Option(toHeader(forParticipantId("sandbox-participant", adminToken))) - protected val canReadAsAdminRandomParticipantId = + protected val canReadAsAdminRandomParticipantId: Option[String] = Option(toHeader(forParticipantId(UUID.randomUUID.toString, adminToken))) + } diff --git a/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/services/SandboxFixtureWithAuth.scala b/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/services/SandboxFixtureWithAuth.scala deleted file mode 100644 index 8643085454bf..000000000000 --- a/ledger/sandbox-classic/src/test/lib/scala/platform/sandbox/services/SandboxFixtureWithAuth.scala +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package com.daml.platform.sandbox.services - -import java.time.{Duration, Instant} -import java.util.UUID - -import com.daml.jwt.domain.DecodedJwt -import com.daml.jwt.{HMAC256Verifier, JwtSigner} -import com.daml.ledger.api.auth.{AuthServiceJWT, AuthServiceJWTCodec, AuthServiceJWTPayload} -import com.daml.platform.sandbox.config.SandboxConfig -import org.scalatest.Suite -import scalaz.syntax.tag.ToTagOps - -trait SandboxFixtureWithAuth extends SandboxFixture { self: Suite => - - val emptyToken = AuthServiceJWTPayload( - ledgerId = None, - participantId = None, - applicationId = None, - exp = None, - admin = false, - actAs = Nil, - readAs = Nil - ) - - val adminToken: AuthServiceJWTPayload = emptyToken.copy(admin = true) - - def readOnlyToken(party: String): AuthServiceJWTPayload = - emptyToken.copy(readAs = List(party)) - - def readWriteToken(party: String): AuthServiceJWTPayload = - emptyToken.copy(actAs = List(party)) - - def expiringIn(t: Duration, p: AuthServiceJWTPayload): AuthServiceJWTPayload = - p.copy(exp = Option(Instant.now().plusNanos(t.toNanos))) - - def forLedgerId(id: String, p: AuthServiceJWTPayload): AuthServiceJWTPayload = - p.copy(ledgerId = Some(id)) - - def forParticipantId(id: String, p: AuthServiceJWTPayload): AuthServiceJWTPayload = - p.copy(participantId = Some(id)) - - def forApplicationId(id: String, p: AuthServiceJWTPayload): AuthServiceJWTPayload = - p.copy(applicationId = Some(id)) - - override protected def config: SandboxConfig = - super.config.copy( - authService = Some( - AuthServiceJWT(HMAC256Verifier(jwtSecret) - .getOrElse(sys.error("Failed to create HMAC256 verifier"))))) - - protected lazy val wrappedLedgerId = ledgerId(Some(toHeader(adminToken))) - protected lazy val unwrappedLedgerId = wrappedLedgerId.unwrap - - private val jwtHeader = """{"alg": "HS256", "typ": "JWT"}""" - private val jwtSecret = UUID.randomUUID.toString - - private def signed(payload: AuthServiceJWTPayload, secret: String): String = - JwtSigner.HMAC256 - .sign(DecodedJwt(jwtHeader, AuthServiceJWTCodec.compactPrint(payload)), secret) - .getOrElse(sys.error("Failed to generate token")) - .value - - def toHeader(payload: AuthServiceJWTPayload, secret: String = jwtSecret): String = - signed(payload, secret) -} diff --git a/ledger/sandbox-common/BUILD.bazel b/ledger/sandbox-common/BUILD.bazel index 4801641adb53..7afbe8be09a7 100644 --- a/ledger/sandbox-common/BUILD.bazel +++ b/ledger/sandbox-common/BUILD.bazel @@ -77,6 +77,7 @@ da_scala_library( "//language-support/scala/bindings", "//ledger-api/rs-grpc-bridge", "//ledger-api/testing-utils", + "//ledger-service/jwt", "//ledger/caching", "//ledger/ledger-api-auth", "//ledger/ledger-api-auth-client", @@ -91,8 +92,10 @@ da_scala_library( "//libs-scala/resources", "//libs-scala/timer-utils", "@maven//:ch_qos_logback_logback_classic", + "@maven//:com_auth0_java_jwt", "@maven//:com_typesafe_akka_akka_actor_2_12", "@maven//:com_typesafe_akka_akka_stream_2_12", + "@maven//:com_typesafe_scala_logging_scala_logging_2_12", "@maven//:io_netty_netty_handler", "@maven//:org_scalactic_scalactic_2_12", "@maven//:org_scalatest_scalatest_2_12", diff --git a/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/AbstractSandboxFixture.scala b/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/AbstractSandboxFixture.scala index 0b729f12d129..714855bf80ea 100644 --- a/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/AbstractSandboxFixture.scala +++ b/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/AbstractSandboxFixture.scala @@ -10,6 +10,7 @@ import akka.stream.Materializer import com.daml.api.util.TimeProvider import com.daml.bazeltools.BazelRunfiles._ import com.daml.grpc.adapter.ExecutionSequencerFactory +import com.daml.ledger.api.auth.AuthService import com.daml.ledger.api.auth.client.LedgerCallCredentials import com.daml.ledger.api.domain import com.daml.ledger.api.domain.LedgerId @@ -65,10 +66,13 @@ trait AbstractSandboxFixture extends AkkaBeforeAndAfterAll { scenario = scenario, ledgerIdMode = LedgerIdMode.Static(LedgerId("sandbox-server")), seeding = Some(Seeding.Weak), + authService = authService, ) protected def packageFiles: List[File] = List(darFile) + protected def authService: Option[AuthService] = None + protected def scenario: Option[String] = None protected def database: Option[ResourceOwner[DbInfo]] = None diff --git a/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/SandboxRequiringAuthorization.scala b/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/SandboxRequiringAuthorization.scala new file mode 100644 index 000000000000..359f17676678 --- /dev/null +++ b/ledger/sandbox-common/src/test/lib/scala/platform/sandbox/SandboxRequiringAuthorization.scala @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.platform.sandbox + +import java.time.{Duration, Instant} +import java.util.UUID + +import com.daml.jwt.domain.DecodedJwt +import com.daml.jwt.{HMAC256Verifier, JwtSigner} +import com.daml.ledger.api.auth.{ + AuthService, + AuthServiceJWT, + AuthServiceJWTCodec, + AuthServiceJWTPayload +} +import com.daml.ledger.api.domain.LedgerId +import org.scalatest.Suite +import scalaz.syntax.tag.ToTagOps + +trait SandboxRequiringAuthorization { + self: Suite with AbstractSandboxFixture => + + private val jwtHeader = """{"alg": "HS256", "typ": "JWT"}""" + private val jwtSecret = UUID.randomUUID.toString + + protected val emptyToken: AuthServiceJWTPayload = AuthServiceJWTPayload( + ledgerId = None, + participantId = None, + applicationId = None, + exp = None, + admin = false, + actAs = Nil, + readAs = Nil, + ) + + protected val adminToken: AuthServiceJWTPayload = emptyToken.copy(admin = true) + + protected lazy val wrappedLedgerId: LedgerId = ledgerId(Some(toHeader(adminToken))) + protected lazy val unwrappedLedgerId: String = wrappedLedgerId.unwrap + + override protected def authService: Option[AuthService] = { + val jwtVerifier = + HMAC256Verifier(jwtSecret).getOrElse(sys.error("Failed to create HMAC256 verifier")) + Some(AuthServiceJWT(jwtVerifier)) + } + + protected def readOnlyToken(party: String): AuthServiceJWTPayload = + emptyToken.copy(readAs = List(party)) + + protected def readWriteToken(party: String): AuthServiceJWTPayload = + emptyToken.copy(actAs = List(party)) + + protected def expiringIn(t: Duration, p: AuthServiceJWTPayload): AuthServiceJWTPayload = + p.copy(exp = Option(Instant.now().plusNanos(t.toNanos))) + + protected def forLedgerId(id: String, p: AuthServiceJWTPayload): AuthServiceJWTPayload = + p.copy(ledgerId = Some(id)) + + protected def forParticipantId(id: String, p: AuthServiceJWTPayload): AuthServiceJWTPayload = + p.copy(participantId = Some(id)) + + protected def forApplicationId(id: String, p: AuthServiceJWTPayload): AuthServiceJWTPayload = + p.copy(applicationId = Some(id)) + + protected def toHeader(payload: AuthServiceJWTPayload, secret: String = jwtSecret): String = + signed(payload, secret) + + private def signed(payload: AuthServiceJWTPayload, secret: String): String = + JwtSigner.HMAC256 + .sign(DecodedJwt(jwtHeader, AuthServiceJWTCodec.compactPrint(payload)), secret) + .getOrElse(sys.error("Failed to generate token")) + .value +} diff --git a/triggers/tests/BUILD.bazel b/triggers/tests/BUILD.bazel index d4a335a32458..3a4c13756e51 100644 --- a/triggers/tests/BUILD.bazel +++ b/triggers/tests/BUILD.bazel @@ -73,6 +73,7 @@ da_scala_library( "//language-support/scala/bindings-akka", "//ledger-api/rs-grpc-bridge", "//ledger-api/testing-utils", + "//ledger/ledger-api-auth", "//ledger/ledger-api-common", "//ledger/ledger-api-domain", "//ledger/sandbox-classic", diff --git a/triggers/tests/src/test/scala/com/digitalasset/daml/lf/engine/trigger/test/Jwt.scala b/triggers/tests/src/test/scala/com/digitalasset/daml/lf/engine/trigger/test/Jwt.scala index e01d83c53197..70504f5f1562 100644 --- a/triggers/tests/src/test/scala/com/digitalasset/daml/lf/engine/trigger/test/Jwt.scala +++ b/triggers/tests/src/test/scala/com/digitalasset/daml/lf/engine/trigger/test/Jwt.scala @@ -3,22 +3,23 @@ package com.daml.lf.engine.trigger.test -import akka.stream.scaladsl.{Flow} -import com.daml.lf.data.Ref._ -import com.daml.platform.sandbox.services.SandboxFixtureWithAuth +import akka.stream.scaladsl.Flow import com.daml.ledger.api.refinements.ApiTypes.ApplicationId -import com.daml.ledger.api.testing.utils.{SuiteResourceManagementAroundAll} -import com.daml.ledger.api.v1.commands._ +import com.daml.ledger.api.testing.utils.SuiteResourceManagementAroundAll import com.daml.ledger.api.v1.commands.CreateCommand import com.daml.ledger.api.v1.{value => LedgerApi} -import org.scalatest._ - +import com.daml.ledger.client.configuration.LedgerClientConfiguration +import com.daml.lf.data.Ref._ import com.daml.lf.engine.trigger.TriggerMsg +import com.daml.platform.sandbox.SandboxRequiringAuthorization +import com.daml.platform.sandbox.services.SandboxFixture +import org.scalatest._ class Jwt extends AsyncWordSpec with AbstractTriggerTest - with SandboxFixtureWithAuth + with SandboxFixture + with SandboxRequiringAuthorization with Matchers with SuiteResourceManagementAroundAll with TryValues { @@ -27,9 +28,10 @@ class Jwt // Override to make sure we set it correctly. override protected val applicationId: ApplicationId = ApplicationId("custom app id") - override protected def ledgerClientConfiguration = super.ledgerClientConfiguration.copy( - token = Some(toHeader(forApplicationId("custom app id", readWriteToken(party)))) - ) + override protected def ledgerClientConfiguration: LedgerClientConfiguration = + super.ledgerClientConfiguration.copy( + token = Some(toHeader(forApplicationId("custom app id", readWriteToken(party)))), + ) private val party = "AliceAuth" @@ -37,6 +39,7 @@ class Jwt // We just need something simple to test the connection. val assetId = LedgerApi.Identifier(packageId, "ACS", "Asset") val assetMirrorId = LedgerApi.Identifier(packageId, "ACS", "AssetMirror") + def asset(party: String): CreateCommand = CreateCommand( templateId = Some(assetId),