Skip to content

Commit

Permalink
identity provider id awareness for party and user management apis thr…
Browse files Browse the repository at this point in the history
…ough ledger client (#16632) (#16634)
  • Loading branch information
garyverhaegen-da authored Mar 30, 2023
1 parent 099eee7 commit eb273b1
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ object EndpointsCompanion {
case standardToken: StandardJWTPayload =>
createFromUserToken(
standardToken,
userId => userManagementClient.listUserRights(userId, Some(jwt.value)),
userId => userManagementClient.listUserRights(userId = userId, token = Some(jwt.value)),
() => ledgerIdentityClient.getLedgerId(Some(jwt.value)),
).leftMap(identity[Error])
case customToken: CustomDamlJWTPayload =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private[http] final class UserManagement(
def getUser(jwt: Jwt, req: domain.GetUserRequest): ET[domain.SyncResponse[domain.UserDetails]] =
for {
userId <- parseUserId(req.userId)
user <- EitherT.rightT(userManagementClient.getUser(userId, Some(jwt.value)))
user <- EitherT.rightT(userManagementClient.getUser(userId = userId, token = Some(jwt.value)))
} yield domain.OkResponse(
domain.UserDetails(user.id, user.primaryParty)
): domain.SyncResponse[domain.UserDetails]
Expand Down Expand Up @@ -76,7 +76,7 @@ private[http] final class UserManagement(
): ET[domain.SyncResponse[spray.json.JsObject]] = {
for {
userId <- parseUserId(deleteUserRequest.userId)
_ <- EitherT.rightT(userManagementClient.deleteUser(userId, Some(jwt.value)))
_ <- EitherT.rightT(userManagementClient.deleteUser(userId = userId, token = Some(jwt.value)))
} yield emptyObjectResponse
}

Expand All @@ -87,7 +87,7 @@ private[http] final class UserManagement(
for {
userId <- parseUserId(listUserRightsRequest.userId)
rights <- EitherT.rightT(
userManagementClient.listUserRights(userId, Some(jwt.value))
userManagementClient.listUserRights(userId = userId, token = Some(jwt.value))
)
} yield domain
.OkResponse(domain.UserRights.fromLedgerUserRights(rights)): domain.SyncResponse[List[
Expand All @@ -105,7 +105,11 @@ private[http] final class UserManagement(
domain.UserRights.toLedgerUserRights(grantUserRightsRequest.rights)
).leftMap(InvalidUserInput): ET[List[UserRight]]
grantedUserRights <- EitherT.rightT(
userManagementClient.grantUserRights(userId, rights, Some(jwt.value))
userManagementClient.grantUserRights(
userId = userId,
rights = rights,
token = Some(jwt.value),
)
)
} yield domain.OkResponse(
domain.UserRights.fromLedgerUserRights(grantedUserRights)
Expand All @@ -122,7 +126,11 @@ private[http] final class UserManagement(
domain.UserRights.toLedgerUserRights(revokeUserRightsRequest.rights)
).leftMap(InvalidUserInput): ET[List[UserRight]]
revokedUserRights <- EitherT.rightT(
userManagementClient.revokeUserRights(userId, rights, Some(jwt.value))
userManagementClient.revokeUserRights(
userId = userId,
rights = rights,
token = Some(jwt.value),
)
)
} yield domain.OkResponse(
domain.UserRights.fromLedgerUserRights(revokedUserRights)
Expand All @@ -132,14 +140,14 @@ private[http] final class UserManagement(
def getAuthenticatedUser(jwt: Jwt): ET[domain.SyncResponse[domain.UserDetails]] =
for {
userId <- getUserIdFromToken(jwt)
user <- EitherT.rightT(userManagementClient.getUser(userId, Some(jwt.value)))
user <- EitherT.rightT(userManagementClient.getUser(userId = userId, token = Some(jwt.value)))
} yield domain.OkResponse(domain.UserDetails(user.id, user.primaryParty))

def listAuthenticatedUserRights(jwt: Jwt): ET[domain.SyncResponse[List[domain.UserRight]]] = {
for {
userId <- getUserIdFromToken(jwt)
rights <- EitherT.rightT(
userManagementClient.listUserRights(userId, Some(jwt.value))
userManagementClient.listUserRights(userId = userId, token = Some(jwt.value))
)
} yield domain
.OkResponse(domain.UserRights.fromLedgerUserRights(rights)): domain.SyncResponse[List[
Expand All @@ -163,7 +171,7 @@ private[http] final class UserManagement(
Source.unfoldAsync(some("")) {
_ traverse { pageToken =>
userManagementClient
.listUsers(token, pageToken, pageSize)
.listUsers(token = token, pageToken = pageToken, pageSize = pageSize)
.map {
case (users, "") => (None, \/-(users))
case (users, pageToken) => (Some(pageToken), \/-(users))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,62 @@ import com.daml.ledger.api.v1.admin.party_management_service.{
GetParticipantIdRequest,
GetPartiesRequest,
ListKnownPartiesRequest,
UpdatePartyDetailsRequest,
PartyDetails => ApiPartyDetails,
}
import com.daml.ledger.api.v1.admin.object_meta.{ObjectMeta => ApiObjectMeta}
import com.daml.ledger.client.LedgerClient
import com.google.protobuf.field_mask.FieldMask
import scalaz.OneAnd

import scala.concurrent.{ExecutionContext, Future}

object PartyManagementClient {

private def details(d: ApiPartyDetails): PartyDetails =
private def details(proto: ApiPartyDetails): PartyDetails =
PartyDetails(
Party.assertFromString(d.party),
if (d.displayName.isEmpty) None else Some(d.displayName),
d.isLocal,
ObjectMeta.empty,
IdentityProviderId(d.identityProviderId),
Party.assertFromString(proto.party),
if (proto.displayName.isEmpty) None else Some(proto.displayName),
proto.isLocal,
fromProtoObjectMeta(proto.localMetadata),
IdentityProviderId(proto.identityProviderId),
)

private def toProtoPartyDetails(domain: PartyDetails): ApiPartyDetails =
ApiPartyDetails(
domain.party,
domain.displayName.getOrElse(""),
domain.isLocal,
toProtoObjectMeta(domain.metadata),
domain.identityProviderId.toRequestString,
)

private val getParticipantIdRequest = GetParticipantIdRequest()

private val listKnownPartiesRequest = ListKnownPartiesRequest()
private def fromProtoObjectMeta(protoObjectMeta: Option[ApiObjectMeta]): ObjectMeta = {
protoObjectMeta match {
case None => ObjectMeta.empty
case Some(ApiObjectMeta("", _)) => ObjectMeta.empty
case Some(ApiObjectMeta(resourceVersion, annotations)) =>
ObjectMeta(Some(resourceVersion.toLong), annotations)
}
}

private def toProtoObjectMeta(objectMeta: ObjectMeta): Option[ApiObjectMeta] = {
objectMeta match {
case ObjectMeta(None, _) => None
case ObjectMeta(Some(resourceVersion), annotations) =>
Some(ApiObjectMeta(resourceVersion.toString, annotations))
}
}

private def getPartiesRequest(parties: OneAnd[Set, Ref.Party]) = {
private def getPartiesRequest(
parties: OneAnd[Set, Ref.Party],
identityProviderId: IdentityProviderId,
) = {
import scalaz.std.iterable._
import scalaz.syntax.foldable._
GetPartiesRequest(parties.toList)
GetPartiesRequest(parties.toList, identityProviderId.toRequestString)
}
}

Expand All @@ -51,29 +81,56 @@ final class PartyManagementClient(service: PartyManagementServiceStub)(implicit
.getParticipantId(PartyManagementClient.getParticipantIdRequest)
.map(r => ParticipantId(Ref.ParticipantId.assertFromString(r.participantId)))

def listKnownParties(token: Option[String] = None): Future[List[PartyDetails]] =
def listKnownParties(
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[List[PartyDetails]] =
LedgerClient
.stub(service, token)
.listKnownParties(PartyManagementClient.listKnownPartiesRequest)
.listKnownParties(ListKnownPartiesRequest(identityProviderId.toRequestString))
.map(_.partyDetails.view.map(PartyManagementClient.details).toList)

def getParties(
parties: OneAnd[Set, Ref.Party],
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[List[PartyDetails]] =
LedgerClient
.stub(service, token)
.getParties(PartyManagementClient.getPartiesRequest(parties))
.getParties(PartyManagementClient.getPartiesRequest(parties, identityProviderId))
.map(_.partyDetails.view.map(PartyManagementClient.details).toList)

def allocateParty(
hint: Option[String],
displayName: Option[String],
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[PartyDetails] =
LedgerClient
.stub(service, token)
.allocateParty(
new AllocatePartyRequest(
partyIdHint = hint.getOrElse(""),
displayName = displayName.getOrElse(""),
identityProviderId = identityProviderId.toRequestString,
)
)
.map(_.partyDetails.getOrElse(sys.error("No PartyDetails in response.")))
.map(PartyManagementClient.details)

def updatePartyDetails(
partyDetails: Option[PartyDetails],
updateMask: Option[FieldMask],
token: Option[String] = None,
): Future[PartyDetails] =
LedgerClient
.stub(service, token)
.allocateParty(new AllocatePartyRequest(hint.getOrElse(""), displayName.getOrElse("")))
.updatePartyDetails(
UpdatePartyDetailsRequest(
partyDetails = partyDetails.map(PartyManagementClient.toProtoPartyDetails),
updateMask,
)
)
.map(_.partyDetails.getOrElse(sys.error("No PartyDetails in response.")))
.map(PartyManagementClient.details)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
package com.daml.ledger.client.services.admin

import com.daml.ledger.api.domain
import com.daml.ledger.api.domain.{ObjectMeta, User, UserRight}
import com.daml.ledger.api.domain.{IdentityProviderId, ObjectMeta, User, UserRight}
import com.daml.ledger.api.v1.admin.user_management_service.UserManagementServiceGrpc.UserManagementServiceStub
import com.daml.ledger.api.v1.admin.{user_management_service => proto}
import com.daml.ledger.api.v1.{admin => admin_proto}
Expand Down Expand Up @@ -50,10 +50,14 @@ final class UserManagementClient(service: UserManagementServiceStub)(implicit
.map(res => fromProtoUser(res.user.get))
}

def getUser(userId: UserId, token: Option[String] = None): Future[User] =
def getUser(
userId: UserId,
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[User] =
LedgerClient
.stub(service, token)
.getUser(proto.GetUserRequest(userId.toString))
.getUser(proto.GetUserRequest(userId.toString, identityProviderId.toRequestString))
.map(res => fromProtoUser(res.user.get))

/** Retrieve the User information for the user authenticated by the token(s) on the call . */
Expand All @@ -63,49 +67,80 @@ final class UserManagementClient(service: UserManagementServiceStub)(implicit
.getUser(proto.GetUserRequest())
.map(res => fromProtoUser(res.user.get))

def deleteUser(userId: UserId, token: Option[String] = None): Future[Unit] =
def deleteUser(
userId: UserId,
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[Unit] =
LedgerClient
.stub(service, token)
.deleteUser(proto.DeleteUserRequest(userId.toString))
.deleteUser(proto.DeleteUserRequest(userId.toString, identityProviderId.toRequestString))
.map(_ => ())

def listUsers(
token: Option[String] = None,
pageToken: String,
pageSize: Int,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[(Seq[User], String)] =
LedgerClient
.stub(service, token)
.listUsers(proto.ListUsersRequest(pageToken = pageToken, pageSize = pageSize))
.listUsers(
proto.ListUsersRequest(
pageToken = pageToken,
pageSize = pageSize,
identityProviderId.toRequestString,
)
)
.map(res => res.users.view.map(fromProtoUser).toSeq -> res.nextPageToken)

def grantUserRights(
userId: UserId,
rights: Seq[UserRight],
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[Seq[UserRight]] =
LedgerClient
.stub(service, token)
.grantUserRights(proto.GrantUserRightsRequest(userId.toString, rights.map(toProtoRight)))
.grantUserRights(
proto.GrantUserRightsRequest(
userId.toString,
rights.map(toProtoRight),
identityProviderId.toRequestString,
)
)
.map(_.newlyGrantedRights.view.collect(fromProtoRight.unlift).toSeq)

def revokeUserRights(
userId: UserId,
rights: Seq[UserRight],
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[Seq[UserRight]] =
LedgerClient
.stub(service, token)
.revokeUserRights(proto.RevokeUserRightsRequest(userId.toString, rights.map(toProtoRight)))
.revokeUserRights(
proto.RevokeUserRightsRequest(
userId.toString,
rights.map(toProtoRight),
identityProviderId.toRequestString,
)
)
.map(_.newlyRevokedRights.view.collect(fromProtoRight.unlift).toSeq)

/** List the rights of the given user.
* Unknown rights are ignored.
*/
def listUserRights(userId: UserId, token: Option[String] = None): Future[Seq[UserRight]] =
def listUserRights(
userId: UserId,
token: Option[String] = None,
identityProviderId: IdentityProviderId = IdentityProviderId.Default,
): Future[Seq[UserRight]] =
LedgerClient
.stub(service, token)
.listUserRights(proto.ListUserRightsRequest(userId.toString))
.listUserRights(
proto.ListUserRightsRequest(userId.toString, identityProviderId.toRequestString)
)
.map(_.rights.view.collect(fromProtoRight.unlift).toSeq)

/** Retrieve the rights of the user authenticated by the token(s) on the call .
Expand All @@ -126,6 +161,7 @@ object UserManagementClient {
Option.unless(user.primaryParty.isEmpty)(Party.assertFromString(user.primaryParty)),
isDeactivated = user.isDeactivated,
metadata = user.metadata.fold(domain.ObjectMeta.empty)(fromProtoMetadata),
identityProviderId = IdentityProviderId(user.identityProviderId),
)

private def fromProtoMetadata(
Expand All @@ -145,6 +181,7 @@ object UserManagementClient {
primaryParty = user.primaryParty.fold("")(_.toString),
isDeactivated = user.isDeactivated,
metadata = Some(toProtoObjectMeta(user.metadata)),
identityProviderId = user.identityProviderId.toRequestString,
)

private def toProtoObjectMeta(meta: ObjectMeta): admin_proto.object_meta.ObjectMeta =
Expand Down

0 comments on commit eb273b1

Please sign in to comment.