mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
enable oauth2-test's user tokens in trigger service tests (#12994)
Duplicates the trigger service's auth tests for the two modes of the oauth test server from #12929, then selects out the claim-token-dependent tests as explained in https://github.com/digital-asset/daml/issues/12831#issuecomment-1048176312 Part of #12831. * also suppress claims-only tests in oracle-with-oauth2-test * use inClaims for several tests CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
0e03e2f100
commit
786906f23b
@ -6,4 +6,12 @@ package com.daml.lf.engine.trigger
|
||||
class TriggerServiceTestAuth
|
||||
extends AbstractTriggerServiceTest
|
||||
with AbstractTriggerServiceTestInMem
|
||||
with AbstractTriggerServiceTestAuthMiddleware {}
|
||||
with AbstractTriggerServiceTestAuthMiddleware
|
||||
with DisableOauthClaimsTests
|
||||
|
||||
class TriggerServiceTestAuthClaims
|
||||
extends AbstractTriggerServiceTest
|
||||
with AbstractTriggerServiceTestInMem
|
||||
with AbstractTriggerServiceTestAuthMiddleware {
|
||||
override protected[this] def oauth2YieldsUserTokens = false
|
||||
}
|
||||
|
@ -7,4 +7,13 @@ class TriggerServiceTestAuthWithOracle
|
||||
extends AbstractTriggerServiceTest
|
||||
with AbstractTriggerServiceTestWithDatabase
|
||||
with TriggerDaoOracleFixture
|
||||
with AbstractTriggerServiceTestAuthMiddleware {}
|
||||
with AbstractTriggerServiceTestAuthMiddleware
|
||||
with DisableOauthClaimsTests
|
||||
|
||||
class TriggerServiceTestAuthWithOracleClaims
|
||||
extends AbstractTriggerServiceTest
|
||||
with AbstractTriggerServiceTestWithDatabase
|
||||
with TriggerDaoOracleFixture
|
||||
with AbstractTriggerServiceTestAuthMiddleware {
|
||||
protected[this] override def oauth2YieldsUserTokens = false
|
||||
}
|
||||
|
@ -179,6 +179,7 @@ trait AuthMiddlewareFixture
|
||||
Uri()
|
||||
.withScheme("http")
|
||||
.withAuthority(authMiddleware.localAddress.getHostString, authMiddleware.localAddress.getPort)
|
||||
protected[this] def oauth2YieldsUserTokens: Boolean = true
|
||||
|
||||
private val authSecret: String = "secret"
|
||||
private var resource
|
||||
@ -208,7 +209,7 @@ trait AuthMiddlewareFixture
|
||||
ledgerId = ledgerId,
|
||||
jwtSecret = authSecret,
|
||||
clock = Some(clock),
|
||||
yieldUserTokens = false, // TODO parameterize (#12831)
|
||||
yieldUserTokens = oauth2YieldsUserTokens,
|
||||
)
|
||||
oauthServer = OAuthServer(oauthConfig)
|
||||
oauth <- Resource(oauthServer.start())(closeServerBinding)
|
||||
@ -290,8 +291,8 @@ trait SandboxFixture extends BeforeAndAfterAll with AbstractAuthFixture with Akk
|
||||
participantId = None,
|
||||
exp = None,
|
||||
admin = admin,
|
||||
actAs = actAs.map(ApiTypes.Party.unwrap),
|
||||
readAs = readAs.map(ApiTypes.Party.unwrap),
|
||||
actAs = ApiTypes.Party unsubst actAs,
|
||||
readAs = ApiTypes.Party unsubst readAs,
|
||||
)
|
||||
),
|
||||
),
|
||||
|
@ -14,6 +14,7 @@ import java.time.{Duration => JDuration}
|
||||
import java.util.UUID
|
||||
|
||||
import akka.http.scaladsl.model.Uri.Query
|
||||
import org.scalactic.source
|
||||
import org.scalatest._
|
||||
import org.scalatest.flatspec.AsyncFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
@ -89,6 +90,16 @@ trait AbstractTriggerServiceTest
|
||||
protected val aliceAcs: Party = Tag("Alice_acs")
|
||||
protected val aliceExp: Party = Tag("Alice_exp")
|
||||
|
||||
protected[this] def inClaims(self: ItVerbString, testFn: => Future[Assertion])(implicit
|
||||
pos: source.Position
|
||||
) =
|
||||
self in testFn
|
||||
|
||||
protected[this] implicit final class `InClaims syntax`(private val self: ItVerbString) {
|
||||
def inClaims(testFn: => Future[Assertion])(implicit pos: source.Position) =
|
||||
AbstractTriggerServiceTest.this.inClaims(self, testFn)
|
||||
}
|
||||
|
||||
def startTrigger(
|
||||
uri: Uri,
|
||||
triggerName: String,
|
||||
@ -262,7 +273,7 @@ trait AbstractTriggerServiceTest
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "successfully start a trigger that uses multi-read-as" in withTriggerService(
|
||||
it should "successfully start a trigger that uses multi-read-as" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
val visibleToPublicId = Identifier(testPkgId, "ReadAs", "VisibleToPublic")
|
||||
@ -305,7 +316,7 @@ trait AbstractTriggerServiceTest
|
||||
|
||||
triggerId <- parseTriggerId(resp)
|
||||
_ <- assertTriggerIds(uri, alice, Vector(triggerId))
|
||||
_ <- assertTriggerStatus(triggerId, _.last == "running")
|
||||
_ <- assertTriggerStatus(triggerId, _.last shouldBe "running")
|
||||
|
||||
} yield succeed
|
||||
}
|
||||
@ -342,7 +353,7 @@ trait AbstractTriggerServiceTest
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "should enable a trigger on http request" in withTriggerService(List(dar)) { uri: Uri =>
|
||||
it should "enable a trigger on http request" inClaims withTriggerService(List(dar)) { uri: Uri =>
|
||||
for {
|
||||
client <- sandboxClient(
|
||||
ApiTypes.ApplicationId("my-app-id"),
|
||||
@ -405,7 +416,7 @@ trait AbstractTriggerServiceTest
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "restart trigger on initialization failure due to failed connection" in withTriggerService(
|
||||
it should "restart trigger on initialization failure due to failed connection" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
for {
|
||||
@ -424,7 +435,7 @@ trait AbstractTriggerServiceTest
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "restart trigger on run-time failure due to dropped connection" in withTriggerService(
|
||||
it should "restart trigger on run-time failure due to dropped connection" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
// Simulate the ledger being briefly unavailable due to network connectivity loss.
|
||||
@ -460,18 +471,22 @@ trait AbstractTriggerServiceTest
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "restart triggers with update errors" in withTriggerService(List(dar)) { uri: Uri =>
|
||||
for {
|
||||
resp <- startTrigger(uri, s"$testPkgId:LowLevelErrorTrigger:trigger", alice)
|
||||
aliceTrigger <- parseTriggerId(resp)
|
||||
_ <- assertTriggerIds(uri, alice, Vector(aliceTrigger))
|
||||
// We will attempt to restart the trigger indefinitely.
|
||||
// Just check that we see a few failures and restart attempts.
|
||||
// This relies on a small minimum restart interval as the interval doubles after each
|
||||
// failure.
|
||||
_ <- assertTriggerStatus(aliceTrigger, _.count(_ == "starting") should be > 2)
|
||||
_ <- assertTriggerStatus(aliceTrigger, _.count(_ == "stopped: runtime failure") should be > 2)
|
||||
} yield succeed
|
||||
it should "restart triggers with update errors" inClaims withTriggerService(List(dar)) {
|
||||
uri: Uri =>
|
||||
for {
|
||||
resp <- startTrigger(uri, s"$testPkgId:LowLevelErrorTrigger:trigger", alice)
|
||||
aliceTrigger <- parseTriggerId(resp)
|
||||
_ <- assertTriggerIds(uri, alice, Vector(aliceTrigger))
|
||||
// We will attempt to restart the trigger indefinitely.
|
||||
// Just check that we see a few failures and restart attempts.
|
||||
// This relies on a small minimum restart interval as the interval doubles after each
|
||||
// failure.
|
||||
_ <- assertTriggerStatus(aliceTrigger, _.count(_ == "starting") should be > 2)
|
||||
_ <- assertTriggerStatus(
|
||||
aliceTrigger,
|
||||
_.count(_ == "stopped: runtime failure") should be > 2,
|
||||
)
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "give a 'not found' response for a stop request with an unparseable UUID" in withTriggerService(
|
||||
@ -549,7 +564,7 @@ trait AbstractTriggerServiceTestWithDatabase extends AbstractTriggerServiceTest
|
||||
}
|
||||
} yield succeed)
|
||||
|
||||
it should "restart triggers after shutdown" in (for {
|
||||
it should "restart triggers after shutdown" inClaims (for {
|
||||
_ <- withTriggerService(List(dar)) { uri: Uri =>
|
||||
for {
|
||||
// Start a trigger in the first run of the service.
|
||||
@ -609,16 +624,17 @@ trait AbstractTriggerServiceTestAuthMiddleware
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "forbid a non-authorized party to start a trigger" in withTriggerService(List(dar)) {
|
||||
uri: Uri =>
|
||||
authServer.revokeParty(eve)
|
||||
for {
|
||||
resp <- startTrigger(uri, s"$testPkgId:TestTrigger:trigger", eve)
|
||||
_ <- resp.status shouldBe StatusCodes.Forbidden
|
||||
} yield succeed
|
||||
it should "forbid a non-authorized party to start a trigger" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
authServer.revokeParty(eve)
|
||||
for {
|
||||
resp <- startTrigger(uri, s"$testPkgId:TestTrigger:trigger", eve)
|
||||
_ <- resp.status shouldBe StatusCodes.Forbidden
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "forbid a non-authorized party to list triggers" in withTriggerService(Nil) {
|
||||
it should "forbid a non-authorized party to list triggers" inClaims withTriggerService(Nil) {
|
||||
uri: Uri =>
|
||||
authServer.revokeParty(eve)
|
||||
for {
|
||||
@ -627,7 +643,7 @@ trait AbstractTriggerServiceTestAuthMiddleware
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "forbid a non-authorized party to check the status of a trigger" in withTriggerService(
|
||||
it should "forbid a non-authorized party to check the status of a trigger" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
for {
|
||||
@ -642,28 +658,30 @@ trait AbstractTriggerServiceTestAuthMiddleware
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "forbid a non-authorized party to stop a trigger" in withTriggerService(List(dar)) {
|
||||
uri: Uri =>
|
||||
for {
|
||||
resp <- startTrigger(uri, s"$testPkgId:TestTrigger:trigger", alice)
|
||||
_ <- resp.status shouldBe StatusCodes.OK
|
||||
triggerId <- parseTriggerId(resp)
|
||||
// emulate access by a different user by revoking access to alice and deleting the current token cookie
|
||||
_ = authServer.revokeParty(alice)
|
||||
_ = deleteCookies()
|
||||
resp <- stopTrigger(uri, triggerId, alice)
|
||||
_ <- resp.status shouldBe StatusCodes.Forbidden
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "forbid a non-authorized user to upload a DAR" in withTriggerService(Nil) { uri: Uri =>
|
||||
authServer.revokeAdmin()
|
||||
it should "forbid a non-authorized party to stop a trigger" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
for {
|
||||
resp <- uploadDar(uri, darPath) // same dar as in initialization
|
||||
resp <- startTrigger(uri, s"$testPkgId:TestTrigger:trigger", alice)
|
||||
_ <- resp.status shouldBe StatusCodes.OK
|
||||
triggerId <- parseTriggerId(resp)
|
||||
// emulate access by a different user by revoking access to alice and deleting the current token cookie
|
||||
_ = authServer.revokeParty(alice)
|
||||
_ = deleteCookies()
|
||||
resp <- stopTrigger(uri, triggerId, alice)
|
||||
_ <- resp.status shouldBe StatusCodes.Forbidden
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "forbid a non-authorized user to upload a DAR" inClaims withTriggerService(Nil) {
|
||||
uri: Uri =>
|
||||
authServer.revokeAdmin()
|
||||
for {
|
||||
resp <- uploadDar(uri, darPath) // same dar as in initialization
|
||||
_ <- resp.status shouldBe StatusCodes.Forbidden
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "request a fresh token after expiry on user request" in withTriggerService(Nil) {
|
||||
uri: Uri =>
|
||||
for {
|
||||
@ -678,88 +696,96 @@ trait AbstractTriggerServiceTestAuthMiddleware
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it should "refresh a token after expiry on the server side" in withTriggerService(List(dar)) {
|
||||
uri: Uri =>
|
||||
for {
|
||||
client <- sandboxClient(
|
||||
ApiTypes.ApplicationId("exp-app-id"),
|
||||
actAs = List(ApiTypes.Party(aliceExp.unwrap)),
|
||||
)
|
||||
// Make sure that no contracts exist initially to guard against accidental
|
||||
// party reuse.
|
||||
_ <- getActiveContracts(client, aliceExp, Identifier(testPkgId, "TestTrigger", "B"))
|
||||
.map(_ shouldBe Vector())
|
||||
// Start the trigger
|
||||
resp <- startTrigger(
|
||||
uri,
|
||||
s"$testPkgId:TestTrigger:trigger",
|
||||
aliceExp,
|
||||
Some(ApplicationId("exp-app-id")),
|
||||
)
|
||||
triggerId <- parseTriggerId(resp)
|
||||
it should "refresh a token after expiry on the server side" inClaims withTriggerService(
|
||||
List(dar)
|
||||
) { uri: Uri =>
|
||||
for {
|
||||
client <- sandboxClient(
|
||||
ApiTypes.ApplicationId("exp-app-id"),
|
||||
actAs = List(ApiTypes.Party(aliceExp.unwrap)),
|
||||
)
|
||||
// Make sure that no contracts exist initially to guard against accidental
|
||||
// party reuse.
|
||||
_ <- getActiveContracts(client, aliceExp, Identifier(testPkgId, "TestTrigger", "B"))
|
||||
.map(_ shouldBe Vector())
|
||||
// Start the trigger
|
||||
resp <- startTrigger(
|
||||
uri,
|
||||
s"$testPkgId:TestTrigger:trigger",
|
||||
aliceExp,
|
||||
Some(ApplicationId("exp-app-id")),
|
||||
)
|
||||
triggerId <- parseTriggerId(resp)
|
||||
|
||||
// Expire old token and test that the trigger service requests a new token during trigger start-up.
|
||||
// TODO[AH] Here we want to test token expiry during QueryingACS.
|
||||
// For now the test relies on timing. Find a way to enforce expiry during QueryingACS.
|
||||
_ = authClock.fastForward(
|
||||
JDuration.ofSeconds(authServer.tokenLifetimeSeconds.asInstanceOf[Long] + 1)
|
||||
)
|
||||
// Expire old token and test that the trigger service requests a new token during trigger start-up.
|
||||
// TODO[AH] Here we want to test token expiry during QueryingACS.
|
||||
// For now the test relies on timing. Find a way to enforce expiry during QueryingACS.
|
||||
_ = authClock.fastForward(
|
||||
JDuration.ofSeconds(authServer.tokenLifetimeSeconds.asInstanceOf[Long] + 1)
|
||||
)
|
||||
|
||||
// Trigger is running, create an A contract
|
||||
createACommand = { v: Long =>
|
||||
Command().withCreate(
|
||||
CreateCommand(
|
||||
templateId = Some(Identifier(testPkgId, "TestTrigger", "A")),
|
||||
createArguments = Some(
|
||||
Record(
|
||||
None,
|
||||
Seq(
|
||||
RecordField(value = Some(Value().withParty(aliceExp.unwrap))),
|
||||
RecordField(value = Some(Value().withInt64(v))),
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
// Trigger is running, create an A contract
|
||||
createACommand = { v: Long =>
|
||||
Command().withCreate(
|
||||
CreateCommand(
|
||||
templateId = Some(Identifier(testPkgId, "TestTrigger", "A")),
|
||||
createArguments = Some(
|
||||
Record(
|
||||
None,
|
||||
Seq(
|
||||
RecordField(value = Some(Value().withParty(aliceExp.unwrap))),
|
||||
RecordField(value = Some(Value().withInt64(v))),
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
_ <- submitCmd(client, aliceExp.unwrap, createACommand(7))
|
||||
// Query ACS until we see a B contract
|
||||
_ <- RetryStrategy.constant(5, 1.seconds) { (_, _) =>
|
||||
getActiveContracts(client, aliceExp, Identifier(testPkgId, "TestTrigger", "B"))
|
||||
.map(_.length shouldBe 1)
|
||||
}
|
||||
|
||||
// Expire old token and test that the trigger service requests a new token during running trigger.
|
||||
_ = authClock.fastForward(
|
||||
JDuration.ofSeconds(authServer.tokenLifetimeSeconds.asInstanceOf[Long] + 1)
|
||||
)
|
||||
}
|
||||
_ <- submitCmd(client, aliceExp.unwrap, createACommand(7))
|
||||
// Query ACS until we see a B contract
|
||||
_ <- RetryStrategy.constant(5, 1.seconds) { (_, _) =>
|
||||
getActiveContracts(client, aliceExp, Identifier(testPkgId, "TestTrigger", "B"))
|
||||
.map(_.length shouldBe 1)
|
||||
}
|
||||
|
||||
// Create another A contract
|
||||
_ <- submitCmd(client, aliceExp.unwrap, createACommand(42))
|
||||
// Query ACS until we see a second B contract
|
||||
_ <- RetryStrategy.constant(5, 1.seconds) { (_, _) =>
|
||||
getActiveContracts(client, aliceExp, Identifier(testPkgId, "TestTrigger", "B"))
|
||||
.map(_.length shouldBe 2)
|
||||
}
|
||||
// Expire old token and test that the trigger service requests a new token during running trigger.
|
||||
_ = authClock.fastForward(
|
||||
JDuration.ofSeconds(authServer.tokenLifetimeSeconds.asInstanceOf[Long] + 1)
|
||||
)
|
||||
|
||||
// Read completions to make sure we set the right app id.
|
||||
r <- client.commandClient
|
||||
.completionSource(List(aliceExp.unwrap), LedgerOffset(Boundary(LEDGER_BEGIN)))
|
||||
.collect({
|
||||
case CompletionStreamElement.CompletionElement(completion, _)
|
||||
if completion.transactionId.nonEmpty =>
|
||||
completion
|
||||
})
|
||||
.take(1)
|
||||
.runWith(Sink.seq)
|
||||
_ = r.length shouldBe 1
|
||||
status <- triggerStatus(uri, triggerId)
|
||||
_ = status.status shouldBe StatusCodes.OK
|
||||
body <- responseBodyToString(status)
|
||||
_ =
|
||||
body shouldBe s"""{"result":{"party":"Alice_exp","status":"running","triggerId":"$testPkgId:TestTrigger:trigger"},"status":200}"""
|
||||
resp <- stopTrigger(uri, triggerId, aliceExp)
|
||||
_ <- assert(resp.status.isSuccess)
|
||||
} yield succeed
|
||||
// Create another A contract
|
||||
_ <- submitCmd(client, aliceExp.unwrap, createACommand(42))
|
||||
// Query ACS until we see a second B contract
|
||||
_ <- RetryStrategy.constant(5, 1.seconds) { (_, _) =>
|
||||
getActiveContracts(client, aliceExp, Identifier(testPkgId, "TestTrigger", "B"))
|
||||
.map(_.length shouldBe 2)
|
||||
}
|
||||
|
||||
// Read completions to make sure we set the right app id.
|
||||
r <- client.commandClient
|
||||
.completionSource(List(aliceExp.unwrap), LedgerOffset(Boundary(LEDGER_BEGIN)))
|
||||
.collect({
|
||||
case CompletionStreamElement.CompletionElement(completion, _)
|
||||
if completion.transactionId.nonEmpty =>
|
||||
completion
|
||||
})
|
||||
.take(1)
|
||||
.runWith(Sink.seq)
|
||||
_ = r.length shouldBe 1
|
||||
status <- triggerStatus(uri, triggerId)
|
||||
_ = status.status shouldBe StatusCodes.OK
|
||||
body <- responseBodyToString(status)
|
||||
_ =
|
||||
body shouldBe s"""{"result":{"party":"Alice_exp","status":"running","triggerId":"$testPkgId:TestTrigger:trigger"},"status":200}"""
|
||||
resp <- stopTrigger(uri, triggerId, aliceExp)
|
||||
_ <- assert(resp.status.isSuccess)
|
||||
} yield succeed
|
||||
}
|
||||
}
|
||||
|
||||
trait DisableOauthClaimsTests extends AbstractTriggerServiceTest {
|
||||
protected[this] override final def inClaims(self: ItVerbString, testFn: => Future[Assertion])(
|
||||
implicit pos: source.Position
|
||||
) =
|
||||
self ignore testFn
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user