Support multiple participant in daml script’s user management (#12091)

part of #11997

No tests for now since we don’t have a multi-participant ledger that
supports this in `main`. The logic for selecting the client for the
participant is the same as for party management and other stuff anyway
so tests don’t add that much.

changelog_begin
changelog_end
This commit is contained in:
Moritz Kiefer 2021-12-10 15:08:28 +01:00 committed by GitHub
parent 1989a2def9
commit 8011d602c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 129 additions and 48 deletions

View File

@ -55,13 +55,21 @@ module Daml.Script
, User(..)
, UserRight(..)
, createUser
, createUserOn
, getUser
, getUserOn
, deleteUser
, deleteUserOn
, listUsers
, listUsersOn
, grantUserRights
, grantUserRightsOn
, revokeUserRights
, revokeUserRightsOn
, listUserRights
, listUserRightsOn
, submitUser
, submitUserOn
) where
#ifdef DAML_EXCEPTIONS
@ -766,7 +774,15 @@ data UserRight
-- | HIDE Create a user with the given rights
createUser : HasCallStack => User -> [UserRight] -> Script User
createUser user rights = lift $ Free $ CreateUser CreateUserPayload with
createUser user rights = createUser' user rights None
-- | HIDE Create a user with the given rights on the given participant.
createUserOn : HasCallStack => User -> [UserRight] -> ParticipantName -> Script User
createUserOn user rights participant = createUser' user rights (Some participant)
createUser' : HasCallStack => User -> [UserRight] -> Optional ParticipantName -> Script User
createUser' user rights participant = lift $ Free $ CreateUser CreateUserPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
user
@ -774,20 +790,44 @@ createUser user rights = lift $ Free $ CreateUser CreateUserPayload with
-- | HIDE Fetch a user by user id
getUser : HasCallStack => Text -> Script (Optional User)
getUser userId = lift $ Free $ GetUser GetUserPayload with
getUser userId = getUser' userId None
-- | HIDE Fetch a user by user id from the given participant.
getUserOn : HasCallStack => Text -> ParticipantName -> Script (Optional User)
getUserOn userId participant = getUser' userId (Some participant)
getUser' : HasCallStack => Text -> Optional ParticipantName -> Script (Optional User)
getUser' userId participant = lift $ Free $ GetUser GetUserPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
userId
-- | HIDE List all users on the participant
-- | Hide List all users
listUsers : Script [User]
listUsers = lift $ Free $ ListUsers ListUsersPayload with
listUsers = listUsers' None
-- | Hide List all users on the given participant
listUsersOn : ParticipantName -> Script [User]
listUsersOn participant = listUsers' (Some participant)
listUsers' : Optional ParticipantName -> Script [User]
listUsers' participant = lift $ Free $ ListUsers ListUsersPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
-- | HIDE Grant the user the given rights. Returns the rights that have been newly granted.
grantUserRights : HasCallStack => Text -> [UserRight] -> Script [UserRight]
grantUserRights userId rights = lift $ Free $ GrantUserRights GrantUserRightsPayload with
grantUserRights userId rights = grantUserRights' userId rights None
-- | HIDE Grant the user on the given participant the given rights. Returns the rights that have been newly granted.
grantUserRightsOn : HasCallStack => Text -> [UserRight] -> ParticipantName -> Script [UserRight]
grantUserRightsOn userId rights participant = grantUserRights' userId rights (Some participant)
grantUserRights' : HasCallStack => Text -> [UserRight] -> Optional ParticipantName -> Script [UserRight]
grantUserRights' userId rights participant = lift $ Free $ GrantUserRights GrantUserRightsPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
userId
@ -795,7 +835,14 @@ grantUserRights userId rights = lift $ Free $ GrantUserRights GrantUserRightsPay
-- | HIDE Revoke the rights of the given user. Returns the revoked rights.
revokeUserRights : HasCallStack => Text -> [UserRight] -> Script [UserRight]
revokeUserRights userId rights = lift $ Free $ RevokeUserRights RevokeUserRightsPayload with
revokeUserRights userId rights = revokeUserRights' userId rights None
revokeUserRightsOn : HasCallStack => Text -> [UserRight] -> ParticipantName -> Script [UserRight]
revokeUserRightsOn userId rights participant = revokeUserRights' userId rights (Some participant)
revokeUserRights' : HasCallStack => Text -> [UserRight] -> Optional ParticipantName -> Script [UserRight]
revokeUserRights' userId rights participant = lift $ Free $ RevokeUserRights RevokeUserRightsPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
userId
@ -803,22 +850,46 @@ revokeUserRights userId rights = lift $ Free $ RevokeUserRights RevokeUserRights
-- | HIDE Delete the given user. Fails if the user does not exist.
deleteUser : HasCallStack => Text -> Script ()
deleteUser userId = lift $ Free $ DeleteUser DeleteUserPayload with
deleteUser userId = deleteUser' userId None
-- | HIDE Delete the given user on the given participant. Fails if the user does not exist.
deleteUserOn : HasCallStack => Text -> ParticipantName -> Script ()
deleteUserOn userId participant = deleteUser' userId (Some participant)
deleteUser' : HasCallStack => Text -> Optional ParticipantName -> Script ()
deleteUser' userId participant = lift $ Free $ DeleteUser DeleteUserPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
userId
-- | HIDE List the rights of the given user
-- | HIDE List the rights of the given user .
listUserRights : HasCallStack => Text -> Script [UserRight]
listUserRights userId = lift $ Free $ ListUserRights ListUserRightsPayload with
listUserRights userId = listUserRights' userId None
-- | HIDE List the rights of the user on the given participant.
listUserRightsOn : HasCallStack => Text -> ParticipantName -> Script [UserRight]
listUserRightsOn userId participant = listUserRights' userId (Some participant)
listUserRights' : HasCallStack => Text -> Optional ParticipantName -> Script [UserRight]
listUserRights' userId participant = lift $ Free $ ListUserRights ListUserRightsPayload with
participant = fmap participantName participant
continue = pure
locations = getCallStack callStack
userId
-- | HIDE Submit the commands with the actAs and readAs claims granted to the user.
submitUser : HasCallStack => User -> Commands a -> Script a
submitUser u cmds = do
rights <- listUserRights u.id
submitUser : HasCallStack => Text -> Commands a -> Script a
submitUser userId cmds = submitUser' userId None cmds
-- | HIDE Submit the commands with the actAs and readAs claims granted
-- to the user on the given participant
submitUserOn : HasCallStack => Text -> ParticipantName -> Commands a -> Script a
submitUserOn userId participant cmds = submitUser' userId (Some participant) cmds
submitUser' : HasCallStack => Text -> Optional ParticipantName -> Commands a -> Script a
submitUser' userId participant cmds = do
rights <- listUserRights' userId participant
let actAs = [ p | CanActAs p <- rights ]
let readAs = [ p | CanReadAs p <- rights ]
submitMulti actAs readAs cmds
@ -827,6 +898,7 @@ data CreateUserPayload a = CreateUserPayload
with
user: User
rights: [UserRight]
participant : Optional Text
continue : User -> a
locations : [(Text, SrcLoc)]
deriving Functor
@ -834,6 +906,7 @@ data CreateUserPayload a = CreateUserPayload
data GetUserPayload a = GetUserPayload
with
userId : Text
participant : Optional Text
continue : Optional User -> a
locations : [(Text, SrcLoc)]
deriving Functor
@ -841,12 +914,14 @@ data GetUserPayload a = GetUserPayload
data DeleteUserPayload a = DeleteUserPayload
with
userId : Text
participant : Optional Text
continue : () -> a
locations : [(Text, SrcLoc)]
deriving Functor
data ListUsersPayload a = ListUsersPayload
with
participant : Optional Text
continue : [User] -> a
locations : [(Text, SrcLoc)]
deriving Functor
@ -855,6 +930,7 @@ data GrantUserRightsPayload a = GrantUserRightsPayload
with
userId : Text
rights : [UserRight]
participant : Optional Text
continue : [UserRight] -> a
locations : [(Text, SrcLoc)]
deriving Functor
@ -863,6 +939,7 @@ data RevokeUserRightsPayload a = RevokeUserRightsPayload
with
userId : Text
rights : [UserRight]
participant : Optional Text
continue : [UserRight] -> a
locations : [(Text, SrcLoc)]
deriving Functor
@ -870,6 +947,7 @@ data RevokeUserRightsPayload a = RevokeUserRightsPayload
data ListUserRightsPayload a = ListUserRightsPayload
with
userId : Text
participant : Optional Text
continue : [UserRight] -> a
locations : [(Text, SrcLoc)]
deriving Functor

View File

@ -415,6 +415,7 @@ object ScriptF {
final case class CreateUser(
user: User,
rights: List[UserRight],
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
@ -425,9 +426,7 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
user <- client.createUser(user, rights)
user <- Converter.toFuture(Converter.fromUser(env.scriptIds, user))
} yield SEApp(SEValue(continue), Array(SEValue(user)))
@ -435,6 +434,7 @@ object ScriptF {
final case class GetUser(
userId: UserId,
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
@ -445,9 +445,7 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
user <- client.getUser(userId)
user <- Converter.toFuture(
Converter.fromOptional(user, Converter.fromUser(env.scriptIds, _))
@ -457,6 +455,7 @@ object ScriptF {
final case class DeleteUser(
userId: UserId,
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
@ -467,14 +466,16 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
_ <- client.deleteUser(userId)
} yield SEApp(SEValue(continue), Array(SEValue(SUnit)))
}
final case class ListUsers(stackTrace: StackTrace, continue: SValue) extends Cmd {
final case class ListUsers(
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
override def description = "listUsers"
override def execute(env: Env)(implicit
ec: ExecutionContext,
@ -482,9 +483,7 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
users <- client.listUsers()
users <- Converter.toFuture(
users.to(FrontStack).traverse(Converter.fromUser(env.scriptIds, _))
@ -495,6 +494,7 @@ object ScriptF {
final case class GrantUserRights(
userId: UserId,
rights: List[UserRight],
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
@ -505,9 +505,7 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
rights <- client.grantUserRights(userId, rights)
rights <- Converter.toFuture(
rights.to(FrontStack).traverse(Converter.fromUserRight(env.scriptIds, _))
@ -518,6 +516,7 @@ object ScriptF {
final case class RevokeUserRights(
userId: UserId,
rights: List[UserRight],
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
@ -528,9 +527,7 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
rights <- client.revokeUserRights(userId, rights)
rights <- Converter.toFuture(
rights.to(FrontStack).traverse(Converter.fromUserRight(env.scriptIds, _))
@ -540,6 +537,7 @@ object ScriptF {
final case class ListUserRights(
userId: UserId,
participant: Option[Participant],
stackTrace: StackTrace,
continue: SValue,
) extends Cmd {
@ -550,9 +548,7 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] =
for {
// TODO https://github.com/digital-asset/daml/issues/11997
// support multiple participants
client <- Converter.toFuture(env.clients.getParticipant(None))
client <- Converter.toFuture(env.clients.getParticipant(participant))
rights <- client.listUserRights(userId)
rights <- Converter.toFuture(
rights.to(FrontStack).traverse(Converter.fromUserRight(env.scriptIds, _))
@ -796,73 +792,80 @@ object ScriptF {
private def parseCreateUser(ctx: Ctx, v: SValue): Either[String, CreateUser] =
v match {
case SRecord(_, _, JavaList(user, rights, continue, stackTrace)) =>
case SRecord(_, _, JavaList(user, rights, participant, continue, stackTrace)) =>
for {
user <- Converter.toUser(user)
participant <- Converter.toParticipantName(participant)
rights <- Converter.toList(rights, Converter.toUserRight)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield CreateUser(user, rights, stackTrace, continue)
} yield CreateUser(user, rights, participant, stackTrace, continue)
case _ => Left(s"Exected CreateUser payload but got $v")
}
private def parseGetUser(ctx: Ctx, v: SValue): Either[String, GetUser] =
v match {
case SRecord(_, _, JavaList(userId, continue, stackTrace)) =>
case SRecord(_, _, JavaList(userId, participant, continue, stackTrace)) =>
for {
userId <- Converter.toUserId(userId)
participant <- Converter.toParticipantName(participant)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield GetUser(userId, stackTrace, continue)
} yield GetUser(userId, participant, stackTrace, continue)
case _ => Left(s"Expected GetUser payload but got $v")
}
private def parseDeleteUser(ctx: Ctx, v: SValue): Either[String, DeleteUser] =
v match {
case SRecord(_, _, JavaList(userId, continue, stackTrace)) =>
case SRecord(_, _, JavaList(userId, participant, continue, stackTrace)) =>
for {
userId <- Converter.toUserId(userId)
participant <- Converter.toParticipantName(participant)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield DeleteUser(userId, stackTrace, continue)
} yield DeleteUser(userId, participant, stackTrace, continue)
case _ => Left(s"Expected DeleteUser payload but got $v")
}
private def parseListUsers(ctx: Ctx, v: SValue): Either[String, ListUsers] =
v match {
case SRecord(_, _, JavaList(continue, stackTrace)) =>
case SRecord(_, _, JavaList(participant, continue, stackTrace)) =>
for {
participant <- Converter.toParticipantName(participant)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield ListUsers(stackTrace, continue)
} yield ListUsers(participant, stackTrace, continue)
case _ => Left(s"Expected ListUsers payload but got $v")
}
private def parseGrantUserRights(ctx: Ctx, v: SValue): Either[String, GrantUserRights] =
v match {
case SRecord(_, _, JavaList(userId, rights, continue, stackTrace)) =>
case SRecord(_, _, JavaList(userId, rights, participant, continue, stackTrace)) =>
for {
userId <- Converter.toUserId(userId)
rights <- Converter.toList(rights, Converter.toUserRight)
participant <- Converter.toParticipantName(participant)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield GrantUserRights(userId, rights, stackTrace, continue)
} yield GrantUserRights(userId, rights, participant, stackTrace, continue)
case _ => Left(s"Expected GrantUserRights payload but got $v")
}
private def parseRevokeUserRights(ctx: Ctx, v: SValue): Either[String, RevokeUserRights] =
v match {
case SRecord(_, _, JavaList(userId, rights, continue, stackTrace)) =>
case SRecord(_, _, JavaList(userId, rights, participant, continue, stackTrace)) =>
for {
userId <- Converter.toUserId(userId)
rights <- Converter.toList(rights, Converter.toUserRight)
participant <- Converter.toParticipantName(participant)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield RevokeUserRights(userId, rights, stackTrace, continue)
} yield RevokeUserRights(userId, rights, participant, stackTrace, continue)
case _ => Left(s"Expected RevokeUserRights payload but got $v")
}
private def parseListUserRights(ctx: Ctx, v: SValue): Either[String, ListUserRights] =
v match {
case SRecord(_, _, JavaList(userId, continue, stackTrace)) =>
case SRecord(_, _, JavaList(userId, participant, continue, stackTrace)) =>
for {
userId <- Converter.toUserId(userId)
participant <- Converter.toParticipantName(participant)
stackTrace <- toStackTrace(ctx, Some(stackTrace))
} yield ListUserRights(userId, stackTrace, continue)
} yield ListUserRights(userId, participant, stackTrace, continue)
case _ => Left(s"Expected ListUserRights payload but got $v")
}