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

[JSON-API] Add list-specific-users-rights, grant & revoke user rights endpoints #12352

Merged
merged 9 commits into from
Jan 25, 2022
2 changes: 2 additions & 0 deletions bazel-java-deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def install_java_deps():
"io.gatling:gatling-http-client:{}".format(gatling_version),
"io.reactivex.rxjava2:rxjava:2.2.1",
"io.spray:spray-json_{}:1.3.5".format(scala_major_version),
"io.github.paoloboni:spray-json-derived-codecs_{}:2.3.4".format(scala_major_version),
"javax.annotation:javax.annotation-api:1.2",
"javax.ws.rs:javax.ws.rs-api:2.1",
"junit:junit:4.12",
Expand Down Expand Up @@ -194,6 +195,7 @@ def install_java_deps():
"org.xerial:sqlite-jdbc:3.36.0.1",
"com.fasterxml.jackson.core:jackson-core:2.12.0",
"com.fasterxml.jackson.core:jackson-databind:2.12.0",
"org.scala-lang:scala-library:{}".format(scala_version),
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the intent here to pin the Scala version? How does this work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It pins the scala std library version because the in-place mechanisms we have are not enough to pin the version of the scala std library. Specifically this causes that all libraries which depend on the std lib will use the same version of the std lib because a speciifc version of the std lib is already in our must-have dependency list (at least it seems to work like this).

],
fetch_sources = True,
maven_install_json = "@com_github_digital_asset_daml//:maven_install_{}.json".format(scala_major_version),
Expand Down
1 change: 1 addition & 0 deletions ledger-service/http-json/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ hj_scalacopts = lf_scalacopts + [
"@maven//:org_typelevel_cats_effect",
"@maven//:org_typelevel_cats_free",
"@maven//:org_typelevel_cats_kernel",
"@maven//:io_github_paoloboni_spray_json_derived_codecs",
],
scalacopts = hj_scalacopts,
tags = ["maven_coordinates=com.daml:http-json:__VERSION__"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import com.daml.fetchcontracts.domain.TemplateId.OptionalPkg
import com.daml.http.HttpServiceTestFixture.{UseTls, authorizationHeader, getResult, postRequest}
import com.daml.ledger.client.withoutledgerid.{LedgerClient => DamlLedgerClient}
import com.daml.http.dbbackend.JdbcConfig
import com.daml.http.domain.{UserDetails, UserRights}
import com.daml.http.domain.UserDetails
import com.daml.http.json.JsonProtocol._
import com.daml.jwt.domain.Jwt
import com.daml.ledger.api.domain.{User, UserRight}
import com.daml.ledger.api.domain.UserRight.CanActAs
import com.daml.ledger.api.domain.UserRight.{CanActAs, ParticipantAdmin}
import com.daml.ledger.api.v1.{value => v}
import com.daml.lf.data.Ref
import com.daml.platform.sandbox.{SandboxRequiringAuthorization, SandboxRequiringAuthorizationFuns}
Expand All @@ -26,6 +26,7 @@ import scalaz.syntax.show._
import spray.json.JsValue

import scala.concurrent.Future
import scalaz.syntax.tag._

@SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements"))
trait HttpServiceIntegrationTestUserManagementFuns
Expand All @@ -52,7 +53,7 @@ trait HttpServiceIntegrationTestUserManagementFuns
def headersWithUserAuth(userId: String): List[Authorization] =
authorizationHeader(jwtForUser(userId))

def getUniqueUserName(name: String): String = getUniqueParty(name).toString
def getUniqueUserName(name: String): String = getUniqueParty(name).unwrap

}

Expand All @@ -72,7 +73,21 @@ class HttpServiceIntegrationTestUserManagementNoAuth

override def wsConfig: Option[WebsocketConfig] = None

import scalaz.syntax.tag._
"Json format of UserRight should be stable" in Future {
import spray.json._
val ham = getUniqueParty("ham")
val spam = getUniqueParty("spam")
List[domain.UserRight](
domain.CanActAs(ham),
domain.CanReadAs(spam),
domain.ParticipantAdmin,
).toJson shouldBe
List(
Map("type" -> "CanActAs", "party" -> ham.unwrap),
Map("type" -> "CanReadAs", "party" -> spam.unwrap),
Map("type" -> "ParticipantAdmin"),
).toJson
}

"create IOU should work with correct user rights" in withHttpServiceAndClient(
participantAdminJwt
Expand All @@ -84,7 +99,7 @@ class HttpServiceIntegrationTestUserManagementNoAuth
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(alice.toString))
CanActAs(Ref.Party.assertFromString(alice.unwrap))
),
)
(status, output) <- postJsonRequest(
Expand Down Expand Up @@ -112,7 +127,7 @@ class HttpServiceIntegrationTestUserManagementNoAuth
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(bob.toString))
CanActAs(Ref.Party.assertFromString(bob.unwrap))
),
)
(status, output) <- postJsonRequest(
Expand Down Expand Up @@ -140,8 +155,8 @@ class HttpServiceIntegrationTestUserManagementNoAuth
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(alice.toString)),
CanActAs(Ref.Party.assertFromString(bob.toString)),
CanActAs(Ref.Party.assertFromString(alice.unwrap)),
CanActAs(Ref.Party.assertFromString(bob.unwrap)),
),
)
(status, output) <- postJsonRequest(
Expand Down Expand Up @@ -174,13 +189,44 @@ class HttpServiceIntegrationTestUserManagementNoAuth
assertStatus(output, StatusCodes.OK)
getResult(output).convertTo[UserDetails] shouldEqual UserDetails(
user.id,
user.primaryParty.map(_.toString),
user.primaryParty: Option[String],
)
}
} yield assertion
}

"requesting the user rights should be possible via the user/rights endpoint" in withHttpServiceAndClient(
"requesting the user rights for a specific user should be possible via a POST to the user/rights endpoint" in withHttpServiceAndClient(
participantAdminJwt
) { (uri, _, _, ledgerClient, _) =>
import spray.json._
val alice = getUniqueParty("Alice")
val bob = getUniqueParty("Bob")
for {
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(alice.unwrap)),
CanActAs(Ref.Party.assertFromString(bob.unwrap)),
),
)
(status, output) <- postRequest(
uri.withPath(Uri.Path("/v1/user/rights")),
domain.ListUserRightsRequest(user.id).toJson,
headers = authorizationHeader(participantAdminJwt),
)
assertion <- {
status shouldBe StatusCodes.OK
assertStatus(output, StatusCodes.OK)
getResult(output).convertTo[List[domain.UserRight]] should contain theSameElementsAs
List[domain.UserRight](
domain.CanActAs(alice),
domain.CanActAs(bob),
)
}
} yield assertion
}

"requesting the user rights for the current user should be possible via a GET to the user/rights endpoint" in withHttpServiceAndClient(
participantAdminJwt
) { (uri, _, _, ledgerClient, _) =>
val alice = getUniqueParty("Alice")
Expand All @@ -189,8 +235,8 @@ class HttpServiceIntegrationTestUserManagementNoAuth
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(alice.toString)),
CanActAs(Ref.Party.assertFromString(bob.toString)),
CanActAs(Ref.Party.assertFromString(alice.unwrap)),
CanActAs(Ref.Party.assertFromString(bob.unwrap)),
),
)
(status, output) <- getRequest(
Expand All @@ -200,11 +246,11 @@ class HttpServiceIntegrationTestUserManagementNoAuth
assertion <- {
status shouldBe StatusCodes.OK
assertStatus(output, StatusCodes.OK)
getResult(output).convertTo[UserRights] shouldEqual UserRights(
canActAs = List(alice, bob),
canReadAs = List.empty,
isAdmin = false,
)
getResult(output).convertTo[List[domain.UserRight]] should contain theSameElementsAs
List[domain.UserRight](
domain.CanActAs(alice),
domain.CanActAs(bob),
)
}
} yield assertion
}
Expand All @@ -218,9 +264,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth
val createUserRequest = domain.CreateUserRequest(
"nice.user2",
Some(alice.unwrap),
List(alice),
List.empty,
isAdmin = true,
List[domain.UserRight](
domain.CanActAs(alice),
domain.ParticipantAdmin,
),
)
for {
(status, output) <- postRequest(
Expand Down Expand Up @@ -248,9 +295,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth
domain.CreateUserRequest(
name,
Some(alice.unwrap),
List(alice),
List.empty,
isAdmin = true,
List[domain.UserRight](
domain.CanActAs(alice),
domain.ParticipantAdmin,
),
)
)
for {
Expand Down Expand Up @@ -282,9 +330,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth
val createUserRequest = domain.CreateUserRequest(
getUniqueUserName("nice.user"),
Some(alice.unwrap),
List(alice),
List.empty,
isAdmin = true,
List[domain.UserRight](
domain.CanActAs(alice),
domain.ParticipantAdmin,
),
)
for {
(status1, output1) <- postRequest(
Expand Down Expand Up @@ -319,9 +368,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth
val createUserRequest = domain.CreateUserRequest(
getUniqueUserName("nice.user"),
Some(alice.unwrap),
List(alice),
List.empty,
isAdmin = true,
List[domain.UserRight](
domain.CanActAs(alice),
domain.ParticipantAdmin,
),
)
for {
(status1, output1) <- postRequest(
Expand Down Expand Up @@ -355,9 +405,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth
val createUserRequest = domain.CreateUserRequest(
getUniqueUserName("nice.user"),
Some(alice.unwrap),
List(alice),
List.empty,
isAdmin = true,
List[domain.UserRight](
domain.CanActAs(alice),
domain.ParticipantAdmin,
),
)
for {
(status1, output1) <- postRequest(
Expand All @@ -384,6 +435,115 @@ class HttpServiceIntegrationTestUserManagementNoAuth
getResult(output3).convertTo[List[UserDetails]] should not contain createUserRequest.userId
}
}

"granting the user rights for a specific user should be possible via a POST to the user/rights/grant endpoint" in withHttpServiceAndClient(
participantAdminJwt
) { (uri, _, _, ledgerClient, _) =>
import spray.json._
val alice = getUniqueParty("Alice")
val bob = getUniqueParty("Bob")
for {
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(alice.unwrap))
),
)
(status, output) <- postRequest(
uri.withPath(Uri.Path("/v1/user/rights/grant")),
domain
.GrantUserRightsRequest(
user.id,
List[domain.UserRight](
domain.CanActAs(alice),
domain.CanActAs(bob),
domain.ParticipantAdmin,
),
)
.toJson,
headers = authorizationHeader(participantAdminJwt),
)
_ <- {
status shouldBe StatusCodes.OK
assertStatus(output, StatusCodes.OK)
getResult(output).convertTo[List[domain.UserRight]] should contain theSameElementsAs List[
domain.UserRight
](domain.CanActAs(bob), domain.ParticipantAdmin)
}
(status2, output2) <- postRequest(
uri.withPath(Uri.Path("/v1/user/rights")),
domain.ListUserRightsRequest(user.id).toJson,
headers = authorizationHeader(participantAdminJwt),
)
assertion <- {
status2 shouldBe StatusCodes.OK
assertStatus(output2, StatusCodes.OK)
getResult(output2).convertTo[List[domain.UserRight]] should contain theSameElementsAs
List[domain.UserRight](
domain.CanActAs(alice),
domain.CanActAs(bob),
domain.ParticipantAdmin,
)
}
} yield assertion
}

"revoking the user rights for a specific user should be possible via a POST to the user/rights/revoke endpoint" in withHttpServiceAndClient(
participantAdminJwt
) { (uri, _, _, ledgerClient, _) =>
import spray.json._
val alice = getUniqueParty("Alice")
val bob = getUniqueParty("Bob")
val charlie = getUniqueParty("Charlie")
for {
user <- createUser(ledgerClient)(
Ref.UserId.assertFromString(getUniqueUserName("nice.user")),
initialRights = List(
CanActAs(Ref.Party.assertFromString(alice.unwrap)),
CanActAs(Ref.Party.assertFromString(bob.unwrap)),
ParticipantAdmin,
),
)
(status, output) <- postRequest(
uri.withPath(Uri.Path("/v1/user/rights/revoke")),
domain
.RevokeUserRightsRequest(
user.id,
List[domain.UserRight](
domain.CanActAs(bob),
domain.CanActAs(charlie),
domain.ParticipantAdmin,
),
)
.toJson,
headers = authorizationHeader(participantAdminJwt),
)
_ <- {
status shouldBe StatusCodes.OK
assertStatus(output, StatusCodes.OK)
getResult(output)
.convertTo[List[domain.UserRight]] should contain theSameElementsAs List[
domain.UserRight
](
domain.CanActAs(bob),
domain.ParticipantAdmin,
)
}
(status2, output2) <- postRequest(
uri.withPath(Uri.Path("/v1/user/rights")),
domain.ListUserRightsRequest(user.id).toJson,
headers = authorizationHeader(participantAdminJwt),
)
assertion <- {
status2 shouldBe StatusCodes.OK
assertStatus(output2, StatusCodes.OK)
getResult(output2).convertTo[List[domain.UserRight]] should contain theSameElementsAs
List[domain.UserRight](
domain.CanActAs(alice)
)
}
} yield assertion
}
}

class HttpServiceIntegrationTestUserManagement
Expand Down
Loading