Access control in Cadence contract

Hi,

Iโ€™m following flow tutorials and need help with contract level access controls.

Example: I want to extend ExampleNFT to have a flag that can be set only by the NFTโ€™s owner but read by everyone.

  1. I added a pub var flag [isLostOrStolen]
  2. Created a setter with access(account) access [setLostOrStolen]

See the code here: https://testnet.flowscan.org/contract/A.b9af6584a32f2225.BlockchainBackedItem1

When I try to execute:

import BlockchainBackedItem1 from 0xb9af6584a32f2225

transaction(id: UInt64) {
    prepare(signer: AuthAccount) {
        let collectionRef = signer.getCapability(BlockchainBackedItem1.CollectionPublicPath)
            .borrow<&{BlockchainBackedItem1.BlockchainBackedItemCollectionPublic}>()
            ?? panic("Could not get receiver reference to the NFT Collection")

        let bbi = collectionRef.borrowBlockchainBackedItem(id: id)

        bbi.setLostOrStolen()
    }
}

I get

|
12 |         bbi.setLostOrStolen()
   |             ^^^^^^^^^^^^^^^ unknown member

It looks like I canโ€™t access functions with account(access) using a transaction signed by the account owner.
Am I doing something wrong?

I could create an Admin resource that has a public function that flips the flag and is stored in the account without creating a public capability, to restrict the access, but it sounds overcomplicated. What is an advised approach to implement a simple flag that is modified by the account owner?

Problem is access(account) can only be accessed by other contracts in the same account. (The account itself canโ€™t access via a transaction)

In your case you can just make it pub which will make it readable by all and only settable by the current owner of the resource.

1 Like

Oh geez, :woman_facepalming: so obvious. Thank you so much!

1 Like

I tried setting access to variables as pub, but if I try to change it directly using a signed transaction I get:

error: cannot assign to `isLostOrStolen`: field has public access

^^^^^^^^^ consider making it publicly settable with `pub(set)`

Iโ€™m guessing thatโ€™s because transaction is outside of the scope in which this variable lives.

Making it pub(set) will allow other accounts modify it, which I donโ€™t want.
I also tried using setters, but

pub fun setLost() {
    self.isLostOrStolen
}

also can be called by anyone.
Any other suggestions?

If you want to protect certain functionality from access by everyone and want to only grant access to certain users, have a read through https://docs.onflow.org/cadence/language/capability-based-access-control/ and https://docs.onflow.org/cadence/msg-sender/. In particular, https://docs.onflow.org/cadence/msg-sender/#admin-rights shows how you can allow controlled access to e.g. setLostOrStolen in your case.

1 Like

Got it. Thanks for pointing me to the right direction @turbolent !