From 5d836133b8bc5adf936976b9bca29fa176eda26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 11 Jan 2022 12:30:07 +0100 Subject: [PATCH 1/9] Add an endpoint used for querying user rights of a specific user changelog_begin - [HTTP-JSON] Added endpoint /user/rights that if called with POST will return user rights of the user specified via the payload changelog_end --- ...ServiceIntegrationTestUserManagement.scala | 33 +++++++++++- .../com/digitalasset/http/Endpoints.scala | 51 ++++++++++++------- .../scala/com/digitalasset/http/domain.scala | 2 + .../digitalasset/http/json/JsonProtocol.scala | 3 ++ 4 files changed, 70 insertions(+), 19 deletions(-) diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index 73310f7bc6d6..81019a8caa4e 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -180,7 +180,38 @@ class HttpServiceIntegrationTestUserManagementNoAuth } 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.toString)), + CanActAs(Ref.Party.assertFromString(bob.toString)), + ), + ) + (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[UserRights] shouldEqual UserRights( + canActAs = List(alice, bob), + canReadAs = List.empty, + isAdmin = false, + ) + } + } 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") diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala index b9bd6c44958c..3fea82dbdf39 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala @@ -60,6 +60,7 @@ import com.daml.ledger.api.domain.UserRight.{CanActAs, CanReadAs, ParticipantAdm import com.daml.ledger.api.{domain => LedgerApiDomain} import com.daml.ledger.client.services.admin.UserManagementClient import com.daml.ledger.client.services.identity.LedgerIdentityClient +import com.daml.lf.data.Ref.UserId class Endpoints( allowNonHttps: Boolean, @@ -222,6 +223,7 @@ class Endpoints( path("user") apply toRoute(getUser(req)), path("user" / "create") apply toRoute(allocateUser(req)), path("user" / "delete") apply toRoute(deleteUser(req)), + path("user" / "rights") apply toRoute(listUserRights(req)), path("parties") & withFetchTimer apply toRoute(parties(req)), path("parties" / "allocate") & withTimer( allocatePartyTimer @@ -483,14 +485,8 @@ class Endpoints( lc: LoggingContextOf[InstanceUUID with RequestID] ): ET[domain.SyncResponse[Boolean]] = proxyWithCommandET { (jwt, deleteUserRequest: domain.DeleteUserRequest) => - import scalaz.syntax.std.either._ - import com.daml.lf.data.Ref for { - userId <- either( - Ref.UserId.fromString(deleteUserRequest.userId).disjunction.leftMap(InvalidUserInput) - ): ET[ - Ref.UserId - ] + userId <- parseUserId(deleteUserRequest.userId) _ <- EitherT.rightT(userManagementClient.deleteUser(userId, Some(jwt.value))) } yield domain.OkResponse(true): domain.SyncResponse[Boolean] }(req) @@ -505,12 +501,27 @@ class Endpoints( ) } yield domain.OkResponse(users.map(domain.UserDetails.fromUser).toList) + def listUserRights(req: HttpRequest)(implicit + lc: LoggingContextOf[InstanceUUID with RequestID] + ): ET[domain.SyncResponse[domain.UserRights]] = + proxyWithCommandET { (jwt, listUserRightsRequest: domain.ListUserRightsRequest) => + for { + userId <- parseUserId(listUserRightsRequest.userId) + rights <- EitherT.rightT( + userManagementClient.listUserRights(userId, Some(jwt.value)) + ) + } yield domain + .OkResponse(domain.UserRights.fromListUserRights(rights)): domain.SyncResponse[ + domain.UserRights + ] + }(req) + def listAuthenticatedUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] ): ET[domain.SyncResponse[domain.UserRights]] = for { jwt <- eitherT(input(req)).bimap(identity[Error], _._1) - userId <- decodeAndParseUserIdFromToken(jwt, decodeJwt).leftMap(identity[Error]) + userId <- getUserIdFromToken(jwt) rights <- EitherT.rightT( userManagementClient.listUserRights(userId, Some(jwt.value)) ) @@ -520,14 +531,8 @@ class Endpoints( lc: LoggingContextOf[InstanceUUID with RequestID] ): ET[domain.SyncResponse[domain.UserDetails]] = proxyWithCommandET { (jwt, getUserRequest: domain.GetUserRequest) => - import scalaz.syntax.std.either._ - import com.daml.lf.data.Ref for { - userId <- either( - Ref.UserId.fromString(getUserRequest.userId).disjunction.leftMap(InvalidUserInput) - ): ET[ - Ref.UserId - ] + userId <- parseUserId(getUserRequest.userId) user <- EitherT.rightT(userManagementClient.getUser(userId, Some(jwt.value))) } yield domain.OkResponse( domain.UserDetails(user.id, user.primaryParty) @@ -539,7 +544,7 @@ class Endpoints( ): ET[domain.SyncResponse[domain.UserDetails]] = for { jwt <- eitherT(input(req)).bimap(identity[Error], _._1) - userId <- decodeAndParseUserIdFromToken(jwt, decodeJwt).leftMap(identity[Error]) + userId <- getUserIdFromToken(jwt) user <- EitherT.rightT(userManagementClient.getUser(userId, Some(jwt.value))) } yield domain.OkResponse(domain.UserDetails(user.id, user.primaryParty)) @@ -563,7 +568,7 @@ class Endpoints( import com.daml.lf.data.Ref val input = for { - username <- Ref.UserId.fromString(createUserRequest.userId).disjunction + username <- UserId.fromString(createUserRequest.userId).disjunction primaryParty <- createUserRequest.primaryParty.traverse(it => Ref.Party.fromString(it).disjunction ) @@ -586,7 +591,7 @@ class Endpoints( } yield (username, primaryParty, canActAs ++ canReadAs ++ isAdminLs) for { info <- EitherT.either(input.leftMap(InvalidUserInput)): ET[ - (Ref.UserId, Option[Ref.Party], List[UserRight]) + (UserId, Option[Ref.Party], List[UserRight]) ] (username, primaryParty, initialRights) = info _ <- EitherT.rightT( @@ -889,6 +894,16 @@ class Endpoints( )(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] ): ET[R] = proxyWithCommand((jwt, a: A) => fn(jwt, a).run)(req) + + private def getUserIdFromToken(jwt: Jwt): ET[UserId] = + decodeAndParseUserIdFromToken(jwt, decodeJwt).leftMap(identity[Error]) + + private def parseUserId(rawUserId: String): ET[UserId] = { + import scalaz.syntax.std.either._ + either( + UserId.fromString(rawUserId).disjunction.leftMap(InvalidUserInput) + ) + } } object Endpoints { diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index 841a86192069..cad9e60dae92 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -170,6 +170,8 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { isAdmin: Boolean, ) + final case class ListUserRightsRequest(userId: String) + final case class GetUserRequest(userId: String) final case class DeleteUserRequest(userId: String) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala index b9998c611b98..5d2d54846031 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala @@ -82,6 +82,9 @@ object JsonProtocol extends JsonProtocolLow { implicit val CreateUserRequest: JsonFormat[domain.CreateUserRequest] = jsonFormat5(domain.CreateUserRequest) + implicit val ListUserRightsRequest: JsonFormat[domain.ListUserRightsRequest] = + jsonFormat1(domain.ListUserRightsRequest) + implicit val GetUserRequest: JsonFormat[domain.GetUserRequest] = jsonFormat1(domain.GetUserRequest) From ed5c2b19cf88b24d2d39ce609c49b7ee0f37a8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 11 Jan 2022 13:22:59 +0100 Subject: [PATCH 2/9] Add grant&revoke user rights endpoint changelog_begin - [HTTP-JSON] Added endpoints user/rights/grant & user/rights/revoke which allow granting & revoking user rights for a specific user changelog_end --- ...ServiceIntegrationTestUserManagement.scala | 76 ++++++++++++++- .../com/digitalasset/http/Endpoints.scala | 93 +++++++++++++++---- .../scala/com/digitalasset/http/domain.scala | 14 +++ .../digitalasset/http/json/JsonProtocol.scala | 6 ++ 4 files changed, 170 insertions(+), 19 deletions(-) diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index 81019a8caa4e..5ededb6c4404 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -13,7 +13,7 @@ import com.daml.http.domain.{UserDetails, UserRights} 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} @@ -415,6 +415,80 @@ 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.toString)) + ), + ) + (status, _) <- postRequest( + uri.withPath(Uri.Path("/v1/user/rights/grant")), + domain.GrantUserRightsRequest(user.id, List(bob), List.empty, isAdmin = true).toJson, + headers = authorizationHeader(participantAdminJwt), + ) + _ = status shouldBe StatusCodes.OK + (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[UserRights] shouldEqual UserRights( + canActAs = List(alice, bob), + canReadAs = List.empty, + isAdmin = true, + ) + } + } 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") + for { + user <- createUser(ledgerClient)( + Ref.UserId.assertFromString(getUniqueUserName("nice.user")), + initialRights = List( + CanActAs(Ref.Party.assertFromString(alice.toString)), + CanActAs(Ref.Party.assertFromString(bob.toString)), + ParticipantAdmin, + ), + ) + (status, _) <- postRequest( + uri.withPath(Uri.Path("/v1/user/rights/revoke")), + domain.RevokeUserRightsRequest(user.id, List(bob), List.empty, isAdmin = true).toJson, + headers = authorizationHeader(participantAdminJwt), + ) + _ = status shouldBe StatusCodes.OK + (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[UserRights] shouldEqual UserRights( + canActAs = List(alice), + canReadAs = List.empty, + isAdmin = false, + ) + } + } yield assertion + } } class HttpServiceIntegrationTestUserManagement diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala index 3fea82dbdf39..c98f30c2b83e 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala @@ -224,6 +224,8 @@ class Endpoints( path("user" / "create") apply toRoute(allocateUser(req)), path("user" / "delete") apply toRoute(deleteUser(req)), path("user" / "rights") apply toRoute(listUserRights(req)), + path("user" / "rights" / "grant") apply toRoute(grantUserRights(req)), + path("user" / "rights" / "revoke") apply toRoute(revokeUserRights(req)), path("parties") & withFetchTimer apply toRoute(parties(req)), path("parties" / "allocate") & withTimer( allocatePartyTimer @@ -527,6 +529,44 @@ class Endpoints( ) } yield domain.OkResponse(domain.UserRights.fromListUserRights(rights)) + def grantUserRights(req: HttpRequest)(implicit + lc: LoggingContextOf[InstanceUUID with RequestID] + ): ET[domain.SyncResponse[Boolean]] = + proxyWithCommandET { (jwt, grantUserRightsRequest: domain.GrantUserRightsRequest) => + for { + userId <- parseUserId(grantUserRightsRequest.userId) + rights <- either( + toUserRightsList( + grantUserRightsRequest.canActAs, + grantUserRightsRequest.canReadAs, + grantUserRightsRequest.isAdmin, + ) + ).leftMap(InvalidUserInput): ET[List[UserRight]] + _ <- EitherT.rightT( + userManagementClient.grantUserRights(userId, rights, Some(jwt.value)) + ) + } yield domain.OkResponse(true): domain.SyncResponse[Boolean] + }(req) + + def revokeUserRights(req: HttpRequest)(implicit + lc: LoggingContextOf[InstanceUUID with RequestID] + ): ET[domain.SyncResponse[Boolean]] = + proxyWithCommandET { (jwt, revokeUserRightsRequest: domain.RevokeUserRightsRequest) => + for { + userId <- parseUserId(revokeUserRightsRequest.userId) + rights <- either( + toUserRightsList( + revokeUserRightsRequest.canActAs, + revokeUserRightsRequest.canReadAs, + revokeUserRightsRequest.isAdmin, + ) + ).leftMap(InvalidUserInput): ET[List[UserRight]] + _ <- EitherT.rightT( + userManagementClient.revokeUserRights(userId, rights, Some(jwt.value)) + ) + } yield domain.OkResponse(true): domain.SyncResponse[Boolean] + }(req) + def getUser(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] ): ET[domain.SyncResponse[domain.UserDetails]] = @@ -561,7 +601,6 @@ class Endpoints( ): ET[domain.SyncResponse[Boolean]] = proxyWithCommand { (jwt, createUserRequest: domain.CreateUserRequest) => { - import scalaz.std.list._ import scalaz.std.option._ import scalaz.syntax.traverse._ import scalaz.syntax.std.either._ @@ -572,23 +611,12 @@ class Endpoints( primaryParty <- createUserRequest.primaryParty.traverse(it => Ref.Party.fromString(it).disjunction ) - canActAs <- - domain.Party - .unsubst(createUserRequest.canActAs) - .traverse(it => - Ref.Party - .fromString(it) - .map(CanActAs(_): UserRight) - .disjunction - ) - canReadAs <- - domain.Party - .unsubst(createUserRequest.canReadAs) - .traverse(it => Ref.Party.fromString(it).map(CanReadAs(_): UserRight).disjunction) - isAdminLs = - if (createUserRequest.isAdmin) List(ParticipantAdmin) - else List.empty - } yield (username, primaryParty, canActAs ++ canReadAs ++ isAdminLs) + rights <- toUserRightsList( + createUserRequest.canActAs, + createUserRequest.canReadAs, + createUserRequest.isAdmin, + ) + } yield (username, primaryParty, rights) for { info <- EitherT.either(input.leftMap(InvalidUserInput)): ET[ (UserId, Option[Ref.Party], List[UserRight]) @@ -904,6 +932,35 @@ class Endpoints( UserId.fromString(rawUserId).disjunction.leftMap(InvalidUserInput) ) } + + private def toUserRightsList( + canActAs: List[domain.Party], + canReadAs: List[domain.Party], + isAdmin: Boolean, + ): String \/ List[UserRight] = { + import scalaz.std.list._ + import scalaz.syntax.traverse._ + import scalaz.syntax.std.either._ + import com.daml.lf.data.Ref + for { + canActAs <- + domain.Party + .unsubst(canActAs) + .traverse(it => + Ref.Party + .fromString(it) + .map(CanActAs(_): UserRight) + .disjunction + ) + canReadAs <- + domain.Party + .unsubst(canReadAs) + .traverse(it => Ref.Party.fromString(it).map(CanReadAs(_): UserRight).disjunction) + isAdminLs = + if (isAdmin) List(ParticipantAdmin) + else List.empty + } yield canActAs ++ canReadAs ++ isAdminLs + } } object Endpoints { diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index cad9e60dae92..b84247881e4a 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -172,6 +172,20 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { final case class ListUserRightsRequest(userId: String) + final case class GrantUserRightsRequest( + userId: String, + canActAs: List[Party], + canReadAs: List[Party], + isAdmin: Boolean, + ) + + final case class RevokeUserRightsRequest( + userId: String, + canActAs: List[Party], + canReadAs: List[Party], + isAdmin: Boolean, + ) + final case class GetUserRequest(userId: String) final case class DeleteUserRequest(userId: String) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala index 5d2d54846031..ee102523afa4 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala @@ -85,6 +85,12 @@ object JsonProtocol extends JsonProtocolLow { implicit val ListUserRightsRequest: JsonFormat[domain.ListUserRightsRequest] = jsonFormat1(domain.ListUserRightsRequest) + implicit val GrantUserRightsRequest: JsonFormat[domain.GrantUserRightsRequest] = + jsonFormat4(domain.GrantUserRightsRequest) + + implicit val RevokeUserRightsRequest: JsonFormat[domain.RevokeUserRightsRequest] = + jsonFormat4(domain.RevokeUserRightsRequest) + implicit val GetUserRequest: JsonFormat[domain.GetUserRequest] = jsonFormat1(domain.GetUserRequest) From 7767efc59c6cc06a61180c8221294a43cfea7116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 18 Jan 2022 13:39:54 +0100 Subject: [PATCH 3/9] Switch to using the UserRights list instead of a case class with fields --- bazel-java-deps.bzl | 2 + ledger-service/http-json/BUILD.bazel | 1 + ...ServiceIntegrationTestUserManagement.scala | 114 ++++++++++++------ .../com/digitalasset/http/Endpoints.scala | 68 +++-------- .../scala/com/digitalasset/http/domain.scala | 59 +++++---- .../digitalasset/http/json/JsonProtocol.scala | 44 ++++++- maven_install_2.13.json | 40 +++++- 7 files changed, 207 insertions(+), 121 deletions(-) diff --git a/bazel-java-deps.bzl b/bazel-java-deps.bzl index 41ae57ffb76e..bdb79e3c3fe3 100644 --- a/bazel-java-deps.bzl +++ b/bazel-java-deps.bzl @@ -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", @@ -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), ], fetch_sources = True, maven_install_json = "@com_github_digital_asset_daml//:maven_install_{}.json".format(scala_major_version), diff --git a/ledger-service/http-json/BUILD.bazel b/ledger-service/http-json/BUILD.bazel index c01b7084b03e..c3815cbeb814 100644 --- a/ledger-service/http-json/BUILD.bazel +++ b/ledger-service/http-json/BUILD.bazel @@ -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__"], diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index 5ededb6c4404..fb0d9a1bf8ab 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -202,11 +202,13 @@ 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[UserRights] shouldEqual + UserRights( + List[domain.UserRight]( + domain.CanActAs(alice), + domain.CanActAs(bob), + ) + ) } } yield assertion } @@ -231,11 +233,13 @@ 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[UserRights] shouldEqual + UserRights( + List[domain.UserRight]( + domain.CanActAs(alice), + domain.CanActAs(bob), + ) + ) } } yield assertion } @@ -249,9 +253,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( @@ -279,9 +284,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth domain.CreateUserRequest( name, Some(alice.unwrap), - List(alice), - List.empty, - isAdmin = true, + List[domain.UserRight]( + domain.CanActAs(alice), + domain.ParticipantAdmin, + ), ) ) for { @@ -313,9 +319,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( @@ -350,9 +357,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( @@ -386,9 +394,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( @@ -429,12 +438,27 @@ class HttpServiceIntegrationTestUserManagementNoAuth CanActAs(Ref.Party.assertFromString(alice.toString)) ), ) - (status, _) <- postRequest( + (status, output) <- postRequest( uri.withPath(Uri.Path("/v1/user/rights/grant")), - domain.GrantUserRightsRequest(user.id, List(bob), List.empty, isAdmin = true).toJson, + domain + .GrantUserRightsRequest( + user.id, + List[domain.UserRight]( + domain.CanActAs(alice), + domain.CanActAs(bob), + domain.ParticipantAdmin, + ), + ) + .toJson, headers = authorizationHeader(participantAdminJwt), ) - _ = status shouldBe StatusCodes.OK + _ <- { + status shouldBe StatusCodes.OK + assertStatus(output, StatusCodes.OK) + getResult(output).convertTo[UserRights] shouldEqual UserRights( + List[domain.UserRight](domain.CanActAs(bob), domain.ParticipantAdmin) + ) + } (status2, output2) <- postRequest( uri.withPath(Uri.Path("/v1/user/rights")), domain.ListUserRightsRequest(user.id).toJson, @@ -444,9 +468,11 @@ class HttpServiceIntegrationTestUserManagementNoAuth status2 shouldBe StatusCodes.OK assertStatus(output2, StatusCodes.OK) getResult(output2).convertTo[UserRights] shouldEqual UserRights( - canActAs = List(alice, bob), - canReadAs = List.empty, - isAdmin = true, + List[domain.UserRight]( + domain.CanActAs(alice), + domain.CanActAs(bob), + domain.ParticipantAdmin, + ) ) } } yield assertion @@ -458,6 +484,7 @@ class HttpServiceIntegrationTestUserManagementNoAuth 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")), @@ -467,12 +494,27 @@ class HttpServiceIntegrationTestUserManagementNoAuth ParticipantAdmin, ), ) - (status, _) <- postRequest( + (status, output) <- postRequest( uri.withPath(Uri.Path("/v1/user/rights/revoke")), - domain.RevokeUserRightsRequest(user.id, List(bob), List.empty, isAdmin = true).toJson, + domain + .RevokeUserRightsRequest( + user.id, + List[domain.UserRight]( + domain.CanActAs(bob), + domain.CanActAs(charlie), + domain.ParticipantAdmin, + ), + ) + .toJson, headers = authorizationHeader(participantAdminJwt), ) - _ = status shouldBe StatusCodes.OK + _ <- { + status shouldBe StatusCodes.OK + assertStatus(output, StatusCodes.OK) + getResult(output).convertTo[UserRights] shouldEqual UserRights( + List[domain.UserRight](domain.CanActAs(bob), domain.ParticipantAdmin) + ) + } (status2, output2) <- postRequest( uri.withPath(Uri.Path("/v1/user/rights")), domain.ListUserRightsRequest(user.id).toJson, @@ -482,9 +524,7 @@ class HttpServiceIntegrationTestUserManagementNoAuth status2 shouldBe StatusCodes.OK assertStatus(output2, StatusCodes.OK) getResult(output2).convertTo[UserRights] shouldEqual UserRights( - canActAs = List(alice), - canReadAs = List.empty, - isAdmin = false, + List[domain.UserRight](domain.CanActAs(alice)) ) } } yield assertion diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala index c98f30c2b83e..3aa63117350f 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala @@ -56,7 +56,6 @@ import com.daml.logging.{ContextualizedLogger, LoggingContextOf} import com.daml.metrics.{Metrics, Timed} import akka.http.scaladsl.server.Directives._ import com.daml.ledger.api.domain.{User, UserRight} -import com.daml.ledger.api.domain.UserRight.{CanActAs, CanReadAs, ParticipantAdmin} import com.daml.ledger.api.{domain => LedgerApiDomain} import com.daml.ledger.client.services.admin.UserManagementClient import com.daml.ledger.client.services.identity.LedgerIdentityClient @@ -513,7 +512,7 @@ class Endpoints( userManagementClient.listUserRights(userId, Some(jwt.value)) ) } yield domain - .OkResponse(domain.UserRights.fromListUserRights(rights)): domain.SyncResponse[ + .OkResponse(domain.UserRights.fromLedgerUserRights(rights)): domain.SyncResponse[ domain.UserRights ] }(req) @@ -527,44 +526,40 @@ class Endpoints( rights <- EitherT.rightT( userManagementClient.listUserRights(userId, Some(jwt.value)) ) - } yield domain.OkResponse(domain.UserRights.fromListUserRights(rights)) + } yield domain.OkResponse(domain.UserRights.fromLedgerUserRights(rights)) def grantUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] - ): ET[domain.SyncResponse[Boolean]] = + ): ET[domain.SyncResponse[domain.UserRights]] = proxyWithCommandET { (jwt, grantUserRightsRequest: domain.GrantUserRightsRequest) => for { userId <- parseUserId(grantUserRightsRequest.userId) rights <- either( - toUserRightsList( - grantUserRightsRequest.canActAs, - grantUserRightsRequest.canReadAs, - grantUserRightsRequest.isAdmin, - ) + domain.UserRights.toLedgerUserRights(grantUserRightsRequest.rights) ).leftMap(InvalidUserInput): ET[List[UserRight]] - _ <- EitherT.rightT( + grantedUserRights <- EitherT.rightT( userManagementClient.grantUserRights(userId, rights, Some(jwt.value)) ) - } yield domain.OkResponse(true): domain.SyncResponse[Boolean] + } yield domain.OkResponse( + domain.UserRights.fromLedgerUserRights(grantedUserRights) + ): domain.SyncResponse[domain.UserRights] }(req) def revokeUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] - ): ET[domain.SyncResponse[Boolean]] = + ): ET[domain.SyncResponse[domain.UserRights]] = proxyWithCommandET { (jwt, revokeUserRightsRequest: domain.RevokeUserRightsRequest) => for { userId <- parseUserId(revokeUserRightsRequest.userId) rights <- either( - toUserRightsList( - revokeUserRightsRequest.canActAs, - revokeUserRightsRequest.canReadAs, - revokeUserRightsRequest.isAdmin, - ) + domain.UserRights.toLedgerUserRights(revokeUserRightsRequest.rights) ).leftMap(InvalidUserInput): ET[List[UserRight]] - _ <- EitherT.rightT( + revokedUserRights <- EitherT.rightT( userManagementClient.revokeUserRights(userId, rights, Some(jwt.value)) ) - } yield domain.OkResponse(true): domain.SyncResponse[Boolean] + } yield domain.OkResponse( + domain.UserRights.fromLedgerUserRights(revokedUserRights) + ): domain.SyncResponse[domain.UserRights] }(req) def getUser(req: HttpRequest)(implicit @@ -611,11 +606,7 @@ class Endpoints( primaryParty <- createUserRequest.primaryParty.traverse(it => Ref.Party.fromString(it).disjunction ) - rights <- toUserRightsList( - createUserRequest.canActAs, - createUserRequest.canReadAs, - createUserRequest.isAdmin, - ) + rights <- domain.UserRights.toLedgerUserRights(createUserRequest.rights) } yield (username, primaryParty, rights) for { info <- EitherT.either(input.leftMap(InvalidUserInput)): ET[ @@ -932,35 +923,6 @@ class Endpoints( UserId.fromString(rawUserId).disjunction.leftMap(InvalidUserInput) ) } - - private def toUserRightsList( - canActAs: List[domain.Party], - canReadAs: List[domain.Party], - isAdmin: Boolean, - ): String \/ List[UserRight] = { - import scalaz.std.list._ - import scalaz.syntax.traverse._ - import scalaz.syntax.std.either._ - import com.daml.lf.data.Ref - for { - canActAs <- - domain.Party - .unsubst(canActAs) - .traverse(it => - Ref.Party - .fromString(it) - .map(CanActAs(_): UserRight) - .disjunction - ) - canReadAs <- - domain.Party - .unsubst(canReadAs) - .traverse(it => Ref.Party.fromString(it).map(CanReadAs(_): UserRight).disjunction) - isAdminLs = - if (isAdmin) List(ParticipantAdmin) - else List.empty - } yield canActAs ++ canReadAs ++ isAdminLs - } } object Endpoints { diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index b84247881e4a..38b6dc950075 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -4,8 +4,7 @@ package com.daml.http import akka.http.scaladsl.model.{StatusCode, StatusCodes} -import com.daml.ledger.api.domain.{User, UserRight} -import com.daml.ledger.api.domain.UserRight.{CanActAs, CanReadAs, ParticipantAdmin} +import com.daml.ledger.api.domain.User import com.daml.lf.iface import com.daml.ledger.api.refinements.{ApiTypes => lar} import com.daml.ledger.api.{v1 => lav1} @@ -134,24 +133,38 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { final case class PartyDetails(identifier: Party, displayName: Option[String], isLocal: Boolean) - final case class UserRights(canActAs: List[Party], canReadAs: List[Party], isAdmin: Boolean) + sealed trait UserRight + final case object ParticipantAdmin extends UserRight + final case class CanActAs(party: Party) extends UserRight + final case class CanReadAs(party: Party) extends UserRight + + final case class UserRights(rights: List[domain.UserRight]) object UserRights { - def fromListUserRights(input: Vector[UserRight]): UserRights = { - val (canActAs, remaining1) = input.partitionMap { - case CanActAs(party) => Left(party) - case other => Right(other) - } - val (canReadAs, remaining2) = remaining1.partitionMap { - case CanReadAs(party) => Left(party) - case other => Right(other) - } - val isAdmin = remaining2.exists { - case ParticipantAdmin => - true - case _ => false + import com.daml.ledger.api.domain.{UserRight => LedgerUserRight}, com.daml.lf.data.Ref + import scalaz.syntax.traverse._ + import scalaz.syntax.std.either._ + + def toLedgerUserRights(input: List[UserRight]): String \/ List[LedgerUserRight] = + input.traverse { + case ParticipantAdmin => \/.right(LedgerUserRight.ParticipantAdmin) + case CanActAs(party) => + Ref.Party.fromString(Party.unwrap(party)).map(LedgerUserRight.CanActAs).disjunction + case CanReadAs(party) => + Ref.Party.fromString(Party.unwrap(party)).map(LedgerUserRight.CanReadAs).disjunction } - UserRights(Party.subst(canActAs.toList), Party.subst(canReadAs.toList), isAdmin) + + def fromLedgerUserRights(input: Vector[LedgerUserRight]): UserRights = { + val rights: List[domain.UserRight] = input + .map[domain.UserRight] { + case LedgerUserRight.ParticipantAdmin => ParticipantAdmin + case LedgerUserRight.CanActAs(party) => + CanActAs(Party(party.toString: String)) + case LedgerUserRight.CanReadAs(party) => + CanReadAs(Party(party.toString: String)) + } + .toList + UserRights(rights) } } @@ -165,25 +178,19 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { final case class CreateUserRequest( userId: String, primaryParty: Option[String], - canActAs: List[Party], - canReadAs: List[Party], - isAdmin: Boolean, + rights: List[UserRight], ) final case class ListUserRightsRequest(userId: String) final case class GrantUserRightsRequest( userId: String, - canActAs: List[Party], - canReadAs: List[Party], - isAdmin: Boolean, + rights: List[UserRight], ) final case class RevokeUserRightsRequest( userId: String, - canActAs: List[Party], - canReadAs: List[Party], - isAdmin: Boolean, + rights: List[UserRight], ) final case class GetUserRequest(userId: String) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala index ee102523afa4..3b89245f4b01 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala @@ -12,6 +12,7 @@ import com.daml.lf.value.json.ApiCodecCompressed import scalaz.syntax.std.option._ import scalaz.{-\/, NonEmptyList, OneAnd, \/-} import spray.json._ +import spray.json.derived.Discriminator object JsonProtocol extends JsonProtocolLow { @@ -73,23 +74,58 @@ object JsonProtocol extends JsonProtocolLow { implicit val userDetails: JsonFormat[domain.UserDetails] = jsonFormat2(domain.UserDetails.apply) + import spray.json.derived.semiauto._ + + // For whatever reason the annotation detection for the deriveFormat is not working correctly. + // This fixes it. + implicit def annotationFix[T]: shapeless.Annotation[Option[Discriminator], T] = + shapeless.Annotation.mkAnnotation(None) + + implicit val userRight: JF[domain.UserRight] = deriveFormat[domain.UserRight] + // If one uses the uppercase name then the implicit resolution will fail - implicit val userRights: RootJsonFormat[domain.UserRights] = jsonFormat3(domain.UserRights.apply) + implicit val userRights: RootJsonFormat[domain.UserRights] = jsonFormat1(domain.UserRights.apply) implicit val PartyDetails: JsonFormat[domain.PartyDetails] = jsonFormat3(domain.PartyDetails.apply) +// implicit val CreateUserRequest: JsonFormat[domain.UserRight] = +// new RootJsonFormat[domain.UserRight] { +// override def write(a: domain.UserRight): JsValue = { +// val (kind, value) = +// a match { +// case domain.ParticipantAdmin => ("ParticipantAdmin", None) +// case domain.CanActAs(canActAs) => ("CanActAs", Some(Map("party" -> canActAs.toJson))) +// case domain.CanReadAs(canReadAs) => +// ("CanReadAs", Some(Map("party" -> canReadAs.toJson))) +// } +// JsObject(Map("kind" -> JsString(kind)) ++ value.getOrElse(Map.empty)) +// } +// +// override def read(json: JsValue): domain.UserRight = json match { +// case JsObject(fields) => +// for { +// kind <- fields.find { +// case ("kind", kind) => true +// case _ => false +// } +// res <- +// } yield () +// case _ => error(json) +// } +// } + implicit val CreateUserRequest: JsonFormat[domain.CreateUserRequest] = - jsonFormat5(domain.CreateUserRequest) + jsonFormat3(domain.CreateUserRequest) implicit val ListUserRightsRequest: JsonFormat[domain.ListUserRightsRequest] = jsonFormat1(domain.ListUserRightsRequest) implicit val GrantUserRightsRequest: JsonFormat[domain.GrantUserRightsRequest] = - jsonFormat4(domain.GrantUserRightsRequest) + jsonFormat2(domain.GrantUserRightsRequest) implicit val RevokeUserRightsRequest: JsonFormat[domain.RevokeUserRightsRequest] = - jsonFormat4(domain.RevokeUserRightsRequest) + jsonFormat2(domain.RevokeUserRightsRequest) implicit val GetUserRequest: JsonFormat[domain.GetUserRequest] = jsonFormat1(domain.GetUserRequest) diff --git a/maven_install_2.13.json b/maven_install_2.13.json index 4361b03ab985..011bf3b7b440 100644 --- a/maven_install_2.13.json +++ b/maven_install_2.13.json @@ -1,6 +1,6 @@ { "dependency_tree": { - "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 15802995, + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 1058100583, "conflict_resolution": {}, "dependencies": [ { @@ -5138,6 +5138,44 @@ "sha256": "af4a8f01245a5008cde9fa3e77098459c1cfafac72aec82531eab2163582460e", "url": "https://repo1.maven.org/maven2/io/gatling/gatling-redis/3.5.1/gatling-redis-3.5.1-sources.jar" }, + { + "coord": "io.github.paoloboni:spray-json-derived-codecs_2.13:2.3.4", + "dependencies": [ + "com.chuusai:shapeless_2.13:2.3.3", + "org.scala-lang:scala-library:2.13.6", + "io.spray:spray-json_2.13:1.3.5" + ], + "directDependencies": [ + "com.chuusai:shapeless_2.13:2.3.3", + "io.spray:spray-json_2.13:1.3.5", + "org.scala-lang:scala-library:2.13.6" + ], + "file": "v1/https/repo1.maven.org/maven2/io/github/paoloboni/spray-json-derived-codecs_2.13/2.3.4/spray-json-derived-codecs_2.13-2.3.4.jar", + "mirror_urls": [ + "https://repo1.maven.org/maven2/io/github/paoloboni/spray-json-derived-codecs_2.13/2.3.4/spray-json-derived-codecs_2.13-2.3.4.jar" + ], + "sha256": "bba4647901952cb7fb4bb50325b1c42d11b4c8d8981e7c76c4c1275f15fb84b1", + "url": "https://repo1.maven.org/maven2/io/github/paoloboni/spray-json-derived-codecs_2.13/2.3.4/spray-json-derived-codecs_2.13-2.3.4.jar" + }, + { + "coord": "io.github.paoloboni:spray-json-derived-codecs_2.13:jar:sources:2.3.4", + "dependencies": [ + "org.scala-lang:scala-library:jar:sources:2.13.6", + "com.chuusai:shapeless_2.13:jar:sources:2.3.3", + "io.spray:spray-json_2.13:jar:sources:1.3.5" + ], + "directDependencies": [ + "com.chuusai:shapeless_2.13:jar:sources:2.3.3", + "io.spray:spray-json_2.13:jar:sources:1.3.5", + "org.scala-lang:scala-library:jar:sources:2.13.6" + ], + "file": "v1/https/repo1.maven.org/maven2/io/github/paoloboni/spray-json-derived-codecs_2.13/2.3.4/spray-json-derived-codecs_2.13-2.3.4-sources.jar", + "mirror_urls": [ + "https://repo1.maven.org/maven2/io/github/paoloboni/spray-json-derived-codecs_2.13/2.3.4/spray-json-derived-codecs_2.13-2.3.4-sources.jar" + ], + "sha256": "6d6171228e885feebdc7f0fadffc1804ad547d5faaa2615cd14b6c9f7fe127c0", + "url": "https://repo1.maven.org/maven2/io/github/paoloboni/spray-json-derived-codecs_2.13/2.3.4/spray-json-derived-codecs_2.13-2.3.4-sources.jar" + }, { "coord": "io.grpc:grpc-api:1.42.0", "dependencies": [ From dabfeef42722e1f0bbdc4febbea82b7dba6b797f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 18 Jan 2022 15:46:33 +0100 Subject: [PATCH 4/9] fix type annotation --- .../main/scala/com/digitalasset/http/json/JsonProtocol.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala index 3b89245f4b01..8d3c7a759533 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala @@ -81,7 +81,7 @@ object JsonProtocol extends JsonProtocolLow { implicit def annotationFix[T]: shapeless.Annotation[Option[Discriminator], T] = shapeless.Annotation.mkAnnotation(None) - implicit val userRight: JF[domain.UserRight] = deriveFormat[domain.UserRight] + implicit val userRight: JsonFormat[domain.UserRight] = deriveFormat[domain.UserRight] // If one uses the uppercase name then the implicit resolution will fail implicit val userRights: RootJsonFormat[domain.UserRights] = jsonFormat1(domain.UserRights.apply) From db2c5460562b9f9fc44cfa8c97b31db0d102e720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 18 Jan 2022 16:05:04 +0100 Subject: [PATCH 5/9] Rename allocateUser func to createUser to comform with the endpoint path --- .../src/main/scala/com/digitalasset/http/Endpoints.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala index 3aa63117350f..678186bf040f 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala @@ -220,7 +220,7 @@ class Endpoints( path("query") & withTimer(queryMatchingTimer) apply toRoute(query(req)), path("fetch") & withFetchTimer apply toRoute(fetch(req)), path("user") apply toRoute(getUser(req)), - path("user" / "create") apply toRoute(allocateUser(req)), + path("user" / "create") apply toRoute(createUser(req)), path("user" / "delete") apply toRoute(deleteUser(req)), path("user" / "rights") apply toRoute(listUserRights(req)), path("user" / "rights" / "grant") apply toRoute(grantUserRights(req)), @@ -591,7 +591,7 @@ class Endpoints( )(req) .map(ps => partiesResponse(parties = ps._1.toList, unknownParties = ps._2.toList)) - def allocateUser(req: HttpRequest)(implicit + def createUser(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] ): ET[domain.SyncResponse[Boolean]] = proxyWithCommand { (jwt, createUserRequest: domain.CreateUserRequest) => From 24b51b319a31e69ef445c363773bc2409a869599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 18 Jan 2022 17:31:52 +0100 Subject: [PATCH 6/9] Response format simplification & cleanup --- ...ServiceIntegrationTestUserManagement.scala | 46 +++++++++---------- .../com/digitalasset/http/Endpoints.scala | 19 ++++---- .../scala/com/digitalasset/http/domain.scala | 23 ++++------ .../digitalasset/http/json/JsonProtocol.scala | 29 ------------ 4 files changed, 43 insertions(+), 74 deletions(-) diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index fb0d9a1bf8ab..b111f46bec5b 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -9,7 +9,7 @@ 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} @@ -202,12 +202,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth assertion <- { status shouldBe StatusCodes.OK assertStatus(output, StatusCodes.OK) - getResult(output).convertTo[UserRights] shouldEqual - UserRights( - List[domain.UserRight]( - domain.CanActAs(alice), - domain.CanActAs(bob), - ) + getResult(output).convertTo[List[domain.UserRight]] should contain theSameElementsAs + List[domain.UserRight]( + domain.CanActAs(alice), + domain.CanActAs(bob), ) } } yield assertion @@ -233,12 +231,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth assertion <- { status shouldBe StatusCodes.OK assertStatus(output, StatusCodes.OK) - getResult(output).convertTo[UserRights] shouldEqual - UserRights( - List[domain.UserRight]( - domain.CanActAs(alice), - domain.CanActAs(bob), - ) + getResult(output).convertTo[List[domain.UserRight]] should contain theSameElementsAs + List[domain.UserRight]( + domain.CanActAs(alice), + domain.CanActAs(bob), ) } } yield assertion @@ -455,9 +451,9 @@ class HttpServiceIntegrationTestUserManagementNoAuth _ <- { status shouldBe StatusCodes.OK assertStatus(output, StatusCodes.OK) - getResult(output).convertTo[UserRights] shouldEqual UserRights( - List[domain.UserRight](domain.CanActAs(bob), domain.ParticipantAdmin) - ) + 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")), @@ -467,13 +463,12 @@ class HttpServiceIntegrationTestUserManagementNoAuth assertion <- { status2 shouldBe StatusCodes.OK assertStatus(output2, StatusCodes.OK) - getResult(output2).convertTo[UserRights] shouldEqual UserRights( + getResult(output2).convertTo[List[domain.UserRight]] should contain theSameElementsAs List[domain.UserRight]( domain.CanActAs(alice), domain.CanActAs(bob), domain.ParticipantAdmin, ) - ) } } yield assertion } @@ -511,8 +506,12 @@ class HttpServiceIntegrationTestUserManagementNoAuth _ <- { status shouldBe StatusCodes.OK assertStatus(output, StatusCodes.OK) - getResult(output).convertTo[UserRights] shouldEqual UserRights( - List[domain.UserRight](domain.CanActAs(bob), domain.ParticipantAdmin) + getResult(output) + .convertTo[List[domain.UserRight]] should contain theSameElementsAs List[ + domain.UserRight + ]( + domain.CanActAs(bob), + domain.ParticipantAdmin, ) } (status2, output2) <- postRequest( @@ -523,9 +522,10 @@ class HttpServiceIntegrationTestUserManagementNoAuth assertion <- { status2 shouldBe StatusCodes.OK assertStatus(output2, StatusCodes.OK) - getResult(output2).convertTo[UserRights] shouldEqual UserRights( - List[domain.UserRight](domain.CanActAs(alice)) - ) + getResult(output2).convertTo[List[domain.UserRight]] should contain theSameElementsAs + List[domain.UserRight]( + domain.CanActAs(alice) + ) } } yield assertion } diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala index 678186bf040f..7a95e34d6914 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Endpoints.scala @@ -504,7 +504,7 @@ class Endpoints( def listUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] - ): ET[domain.SyncResponse[domain.UserRights]] = + ): ET[domain.SyncResponse[List[domain.UserRight]]] = proxyWithCommandET { (jwt, listUserRightsRequest: domain.ListUserRightsRequest) => for { userId <- parseUserId(listUserRightsRequest.userId) @@ -513,24 +513,27 @@ class Endpoints( ) } yield domain .OkResponse(domain.UserRights.fromLedgerUserRights(rights)): domain.SyncResponse[ - domain.UserRights + List[domain.UserRight] ] }(req) def listAuthenticatedUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] - ): ET[domain.SyncResponse[domain.UserRights]] = + ): ET[domain.SyncResponse[List[domain.UserRight]]] = for { jwt <- eitherT(input(req)).bimap(identity[Error], _._1) userId <- getUserIdFromToken(jwt) rights <- EitherT.rightT( userManagementClient.listUserRights(userId, Some(jwt.value)) ) - } yield domain.OkResponse(domain.UserRights.fromLedgerUserRights(rights)) + } yield domain + .OkResponse(domain.UserRights.fromLedgerUserRights(rights)): domain.SyncResponse[List[ + domain.UserRight + ]] def grantUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] - ): ET[domain.SyncResponse[domain.UserRights]] = + ): ET[domain.SyncResponse[List[domain.UserRight]]] = proxyWithCommandET { (jwt, grantUserRightsRequest: domain.GrantUserRightsRequest) => for { userId <- parseUserId(grantUserRightsRequest.userId) @@ -542,12 +545,12 @@ class Endpoints( ) } yield domain.OkResponse( domain.UserRights.fromLedgerUserRights(grantedUserRights) - ): domain.SyncResponse[domain.UserRights] + ): domain.SyncResponse[List[domain.UserRight]] }(req) def revokeUserRights(req: HttpRequest)(implicit lc: LoggingContextOf[InstanceUUID with RequestID] - ): ET[domain.SyncResponse[domain.UserRights]] = + ): ET[domain.SyncResponse[List[domain.UserRight]]] = proxyWithCommandET { (jwt, revokeUserRightsRequest: domain.RevokeUserRightsRequest) => for { userId <- parseUserId(revokeUserRightsRequest.userId) @@ -559,7 +562,7 @@ class Endpoints( ) } yield domain.OkResponse( domain.UserRights.fromLedgerUserRights(revokedUserRights) - ): domain.SyncResponse[domain.UserRights] + ): domain.SyncResponse[List[domain.UserRight]] }(req) def getUser(req: HttpRequest)(implicit diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index 38b6dc950075..5fb206316b47 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -138,8 +138,6 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { final case class CanActAs(party: Party) extends UserRight final case class CanReadAs(party: Party) extends UserRight - final case class UserRights(rights: List[domain.UserRight]) - object UserRights { import com.daml.ledger.api.domain.{UserRight => LedgerUserRight}, com.daml.lf.data.Ref import scalaz.syntax.traverse._ @@ -154,18 +152,15 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { Ref.Party.fromString(Party.unwrap(party)).map(LedgerUserRight.CanReadAs).disjunction } - def fromLedgerUserRights(input: Vector[LedgerUserRight]): UserRights = { - val rights: List[domain.UserRight] = input - .map[domain.UserRight] { - case LedgerUserRight.ParticipantAdmin => ParticipantAdmin - case LedgerUserRight.CanActAs(party) => - CanActAs(Party(party.toString: String)) - case LedgerUserRight.CanReadAs(party) => - CanReadAs(Party(party.toString: String)) - } - .toList - UserRights(rights) - } + def fromLedgerUserRights(input: Vector[LedgerUserRight]): List[UserRight] = input + .map[domain.UserRight] { + case LedgerUserRight.ParticipantAdmin => ParticipantAdmin + case LedgerUserRight.CanActAs(party) => + CanActAs(Party(party.toString: String)) + case LedgerUserRight.CanReadAs(party) => + CanReadAs(Party(party.toString: String)) + } + .toList } final case class UserDetails(userId: String, primaryParty: Option[String]) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala index 8d3c7a759533..a38b1508c7b6 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala @@ -83,38 +83,9 @@ object JsonProtocol extends JsonProtocolLow { implicit val userRight: JsonFormat[domain.UserRight] = deriveFormat[domain.UserRight] - // If one uses the uppercase name then the implicit resolution will fail - implicit val userRights: RootJsonFormat[domain.UserRights] = jsonFormat1(domain.UserRights.apply) - implicit val PartyDetails: JsonFormat[domain.PartyDetails] = jsonFormat3(domain.PartyDetails.apply) -// implicit val CreateUserRequest: JsonFormat[domain.UserRight] = -// new RootJsonFormat[domain.UserRight] { -// override def write(a: domain.UserRight): JsValue = { -// val (kind, value) = -// a match { -// case domain.ParticipantAdmin => ("ParticipantAdmin", None) -// case domain.CanActAs(canActAs) => ("CanActAs", Some(Map("party" -> canActAs.toJson))) -// case domain.CanReadAs(canReadAs) => -// ("CanReadAs", Some(Map("party" -> canReadAs.toJson))) -// } -// JsObject(Map("kind" -> JsString(kind)) ++ value.getOrElse(Map.empty)) -// } -// -// override def read(json: JsValue): domain.UserRight = json match { -// case JsObject(fields) => -// for { -// kind <- fields.find { -// case ("kind", kind) => true -// case _ => false -// } -// res <- -// } yield () -// case _ => error(json) -// } -// } - implicit val CreateUserRequest: JsonFormat[domain.CreateUserRequest] = jsonFormat3(domain.CreateUserRequest) From ecb65c3b20564a26e8b3b2a38baca84bb3a38d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Tue, 18 Jan 2022 17:52:16 +0100 Subject: [PATCH 7/9] Add one last test that ensure that the JSON format of UserRight doesn't change without us noticing --- ...ttpServiceIntegrationTestUserManagement.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index b111f46bec5b..5f1c4ef1e8a0 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -74,6 +74,22 @@ class HttpServiceIntegrationTestUserManagementNoAuth 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 + JsArray( + JsObject("type" -> JsString("CanActAs"), "party" -> JsString(ham.unwrap)), + JsObject("type" -> JsString("CanReadAs"), "party" -> JsString(spam.unwrap)), + JsObject("type" -> JsString("ParticipantAdmin")), + ) + } + "create IOU should work with correct user rights" in withHttpServiceAndClient( participantAdminJwt ) { (uri, encoder, _, ledgerClient, _) => From 7457b04169832c8312ce8746ec28be4638e14d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Wed, 19 Jan 2022 12:54:35 +0100 Subject: [PATCH 8/9] Apply suggestions from code review Co-authored-by: Stephen Compall --- .../HttpServiceIntegrationTestUserManagement.scala | 12 ++++++------ .../main/scala/com/digitalasset/http/domain.scala | 2 +- .../com/digitalasset/http/json/JsonProtocol.scala | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index 5f1c4ef1e8a0..fae614375d9e 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -83,11 +83,11 @@ class HttpServiceIntegrationTestUserManagementNoAuth domain.CanReadAs(spam), domain.ParticipantAdmin, ).toJson shouldBe - JsArray( - JsObject("type" -> JsString("CanActAs"), "party" -> JsString(ham.unwrap)), - JsObject("type" -> JsString("CanReadAs"), "party" -> JsString(spam.unwrap)), - JsObject("type" -> JsString("ParticipantAdmin")), - ) + 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( @@ -447,7 +447,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)) ), ) (status, output) <- postRequest( diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index 5fb206316b47..ac051dba3fe8 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -133,7 +133,7 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { final case class PartyDetails(identifier: Party, displayName: Option[String], isLocal: Boolean) - sealed trait UserRight + sealed abstract class UserRight extends Product with Serializable final case object ParticipantAdmin extends UserRight final case class CanActAs(party: Party) extends UserRight final case class CanReadAs(party: Party) extends UserRight diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala index a38b1508c7b6..6e1931849c33 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/json/JsonProtocol.scala @@ -78,7 +78,7 @@ object JsonProtocol extends JsonProtocolLow { // For whatever reason the annotation detection for the deriveFormat is not working correctly. // This fixes it. - implicit def annotationFix[T]: shapeless.Annotation[Option[Discriminator], T] = + private implicit def annotationFix[T]: shapeless.Annotation[Option[Discriminator], T] = shapeless.Annotation.mkAnnotation(None) implicit val userRight: JsonFormat[domain.UserRight] = deriveFormat[domain.UserRight] From b394a5131044157129246ff4e637a622cffd47be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Peter=20Rouven=20M=C3=BCller?= Date: Wed, 19 Jan 2022 13:10:04 +0100 Subject: [PATCH 9/9] Remove unnecessary conversations and also just use unwrap for tagged strings --- ...ServiceIntegrationTestUserManagement.scala | 29 +++++++++---------- .../scala/com/digitalasset/http/domain.scala | 9 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala index fae614375d9e..7dcdcae06dc8 100644 --- a/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala +++ b/ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala @@ -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 @@ -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 } @@ -72,8 +73,6 @@ 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") @@ -100,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( @@ -128,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( @@ -156,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( @@ -190,7 +189,7 @@ class HttpServiceIntegrationTestUserManagementNoAuth assertStatus(output, StatusCodes.OK) getResult(output).convertTo[UserDetails] shouldEqual UserDetails( user.id, - user.primaryParty.map(_.toString), + user.primaryParty: Option[String], ) } } yield assertion @@ -206,8 +205,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) <- postRequest( @@ -236,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( @@ -447,7 +446,7 @@ class HttpServiceIntegrationTestUserManagementNoAuth user <- createUser(ledgerClient)( Ref.UserId.assertFromString(getUniqueUserName("nice.user")), initialRights = List( - CanActAs(Ref.Party.assertFromString(alice)) + CanActAs(Ref.Party.assertFromString(alice.unwrap)) ), ) (status, output) <- postRequest( @@ -500,8 +499,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)), ParticipantAdmin, ), ) diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala index ac051dba3fe8..f107bd017bec 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/domain.scala @@ -142,23 +142,24 @@ object domain extends com.daml.fetchcontracts.domain.Aliases { import com.daml.ledger.api.domain.{UserRight => LedgerUserRight}, com.daml.lf.data.Ref import scalaz.syntax.traverse._ import scalaz.syntax.std.either._ + import scalaz.syntax.tag._ def toLedgerUserRights(input: List[UserRight]): String \/ List[LedgerUserRight] = input.traverse { case ParticipantAdmin => \/.right(LedgerUserRight.ParticipantAdmin) case CanActAs(party) => - Ref.Party.fromString(Party.unwrap(party)).map(LedgerUserRight.CanActAs).disjunction + Ref.Party.fromString(party.unwrap).map(LedgerUserRight.CanActAs).disjunction case CanReadAs(party) => - Ref.Party.fromString(Party.unwrap(party)).map(LedgerUserRight.CanReadAs).disjunction + Ref.Party.fromString(party.unwrap).map(LedgerUserRight.CanReadAs).disjunction } def fromLedgerUserRights(input: Vector[LedgerUserRight]): List[UserRight] = input .map[domain.UserRight] { case LedgerUserRight.ParticipantAdmin => ParticipantAdmin case LedgerUserRight.CanActAs(party) => - CanActAs(Party(party.toString: String)) + CanActAs(Party(party: String)) case LedgerUserRight.CanReadAs(party) => - CanReadAs(Party(party.toString: String)) + CanReadAs(Party(party: String)) } .toList }