Modeling the delegation from Employer to Employee
Would the following serve as a good example of how to model the way that employers assign roles to employees?
import Daml.Script
import DA.List
template DocumentManager with
employer : Party
employee : Party
where
signatory employer
observer employee
nonconsuming choice CreateDocument : ContractId Document with
controller employee
do create Document with
observers = [employee]
value = 1
..
nonconsuming choice EditDocument : ContractId Document with
doc : ContractId Document
controller employee
do exercise doc Edit
template Document with
employer : Party
value : Int
observers : [Party]
where
signatory employer
observer observers
choice Edit : ContractId Document
controller employer
do create this with value = value + 1
choice ChangeAcceess : ContractId Document with
removes : [Party]
adds : [Party]
controller employer
do
create this with
observers = dedup $ foldr delete (adds ++ observers) removes
demo : Script ()
demo = script do
bank <- allocateParty "Bank"
-- bob joins the company and does some work
bob <- allocateParty "Bob"
bobRole <- submit bank $ createCmd DocumentManager with employer = bank, employee = bob
doc1 <- submit bob $ exerciseCmd bobRole CreateDocument
doc1 <- submit bob $ exerciseCmd bobRole EditDocument with doc = doc1
doc1 <- submit bob $ exerciseCmd bobRole EditDocument with doc = doc1
doc1 <- submit bob $ exerciseCmd bobRole EditDocument with doc = doc1
doc1 <- submit bob $ exerciseCmd bobRole EditDocument with doc = doc1
-- alice joins the company and does some work
alice <- allocateParty "Alice"
aliceRole <- submit bank $ createCmd DocumentManager with employer = bank, employee = alice
doc2 <- submit alice $ exerciseCmd aliceRole CreateDocument
doc2 <- submit alice $ exerciseCmd aliceRole EditDocument with doc = doc2
doc2 <- submit alice $ exerciseCmd aliceRole EditDocument with doc = doc2
-- alice initially cannot edit bob's docs
submitMustFail alice $ exerciseCmd aliceRole EditDocument with doc = doc1
-- alice get's access to bob's docs and does some work
doc1 <- submit bank $ exerciseCmd doc1 ChangeAcceess with adds = [alice], removes = []
doc1 <- submit alice $ exerciseCmd aliceRole EditDocument with doc = doc1
-- bob leaves the company
submit bank $ archiveCmd bobRole
doc1 <- submit bank $ exerciseCmd doc1 ChangeAcceess with removes = [bob], adds = []
submitMustFail bob $ exerciseCmd bobRole EditDocument with doc = doc1
-- alice continues work
doc1 <- submit alice $ exerciseCmd aliceRole EditDocument with doc = doc1
doc2 <- submit alice $ exerciseCmd aliceRole EditDocument with doc = doc2
pure()
For write access this is fine, but for read access you can do better with Users. How would you modify the example to avoid maintaining an observers list on Document?
Thanks for looking at this with me, @Leonid_Rozenberg
Here is a User-based implementation. Instead of having a Daml template (like DocumentManager above) to model the delegation, we use a party (like docManager below) and the built-in actAs field. This implementation does not require an observers : [Party].
import Daml.Script
import DA.Stack (HasCallStack)
template Document with
party : Party
value : Int
where
signatory party
choice Edit : ContractId Document
controller party
do create this with value = value + 1
demo : Script ()
demo = script do
docManager <- allocateParty "Document Manager"
-- bob joins the company
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
bobId <- validateUserId "bob"
createUser (User bobId (Some bob)) [CanActAs bob, CanActAs docManager]
-- bob does some work
doc1 <- submitUser bobId $ createCmd Document with value = 1, party = docManager
doc1 <- submitUser bobId $ exerciseCmd doc1 Edit
doc1 <- submitUser bobId $ exerciseCmd doc1 Edit
doc1 <- submitUser bobId $ exerciseCmd doc1 Edit
doc1 <- submitUser bobId $ exerciseCmd doc1 Edit
-- alice joins the company and does some work
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
aliceId <- validateUserId "alice"
createUser (User aliceId (Some alice) ) [CanActAs alice, CanActAs docManager]
doc2 <- submitUser bobId $ createCmd Document with value = 1, party = docManager
doc2 <- submitUser bobId $ exerciseCmd doc2 Edit
doc2 <- submitUser bobId $ exerciseCmd doc2 Edit
-- sadly alice can modify bob's docs
doc1 <- submitUser aliceId $ exerciseCmd doc1 Edit
-- bob leaves the company
revokeUserRights bobId [CanActAs docManager]
submitUserMustFail bobId $ exerciseCmd doc1 Edit
deleteUser bobId
submitUserMustFail implementation
-- https://discuss.daml.com/t/why-is-there-no-submitusermustfail/4978/3?u=wallacekelly
submitUserMustFail : HasCallStack => UserId -> Commands a -> Script ()
submitUserMustFail userId cmds = do
rights <- listUserRights userId
let actAs = [ p | CanActAs p <- rights ]
let readAs = [ p | CanReadAs p <- rights ]
submitMultiMustFail actAs readAs cmds
This code is much simpler and shorter. However, I do not see a way to limit Alice’s access to Bob’s documents. (See the comment, “sadly alice can modify bob’s docs”.)
As best I can tell, if we need to support user-level visibility to contracts, then we will end up with user-level parties in an observer’s list.
Am I wrong?
This code is much simpler and shorter. However, I do not see a way to limit Alice’s access to Bob’s documents. (See the comment, “sadly alice can modify bob’s docs”.)
You need to combine both approaches. The first approach to control the right to modify and the second to control the right to read.