# Lesson 5 - Role-Based Access Control

## Introduction

This lesson targets the importance of verifying who can access or call the different functions of your Aleph Zero smart contract.

## Prerequisites

To be able to understand and complete this lesson, we believe that the minimum requirement should be the completion of [`Lesson 1 - Getting Started`](/aleph-zero/build/security-course-by-kudelski-security/lesson-1-getting-started-with-ink.md). If you desire to be even more prepared please have a look at the official website of [ink!](https://use.ink/).

## Objectives and Outcomes

In this lesson you will learn:

* What access control means in web3;
* The consequences of a bad access control implementation;
* How to exploit poorly implemented access control in practice;
* How to mitigate an attack on access control.

## Exercise

### Vulnerable Smart contract

Access control in smart contracts protect the access to functions or state. Having no access control could result to having external users modifying the global state of your contract in an undesired way and break the logic of your smart contract.

For example, Bob developed an application that he would like to deploy on Aleph Zero. To achieve his goal, he decided to implement his smart contract in the following way.

Consider a contract that sets a value or a state at construction that is intended to only be modified by the owner of the contract. This contract has two messages that are publicly accessible: `set_price` and `set_owner`.

```rust
#![cfg_attr(not(feature = "std"), no_std)]


#[ink::contract]
mod price {
    use ink::storage::Mapping;
   
    #[ink(storage)]
    #[derive(Default)]
    pub struct Price {
        total_supply: u32,
        price: u32,
        owner: AccountId,
        balances: Mapping<AccountId, u32>,
        
    }
    impl Price {
        #[ink(constructor)]
        pub fn new(supply: u32, price: u32) -> Self {
            let mut balances = Mapping::default();
            let caller = Self::env().caller();
            balances.insert(caller, &supply);
              Self{ 
                total_supply:supply,
                price : price,
                balances,
                owner: caller,
              }
            
        }

        #[ink(message)]
        pub fn total_supply(&self) -> u32 {
            self.total_supply
        }
        #[ink(message)]
        pub fn set_price(&mut self, price: u32) {
            self.price = price
        }
        #[ink(message)]
        pub fn get_price(&self) -> u32 {
            self.price
        }

        #[ink(message)]
        pub fn get_owner(&self) -> AccountId {
            self.owner
        }
        #[ink(message)]
        pub fn set_owner(&mut self, new_owner: AccountId) {
            self.owner = new_owner
        }
        /// Simply returns the current value of our `bool`.
        #[ink(message)]
        pub fn balance_of(&self, account: AccountId) -> u32 {
            match self.balances.get(&account) {
                Some(value) => value,
                None => 0,
            }
        }

        pub fn inflation(&mut self) {
            self.total_supply += 999_999
        }

        #[ink(message)]
        pub fn transfer(&mut self, recipient: AccountId, amount: u32) {
            let sender = self.env().caller();
            let sender_balance = self.balance_of(sender);
            if sender_balance < amount {
                return;
            }
            self.balances.insert(sender, &(sender_balance - amount));
            let recipient_balance = self.balance_of(recipient);
            self.balances.insert(recipient, &(recipient_balance + amount));
        }
    }

    #[cfg(test)]
mod tests {
    use crate::price::Price;
    use ink::env::{test, DefaultEnvironment};

    #[ink::test]
    fn contract_construction() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let price = Price::new(1000, 10);
        assert_eq!(price.total_supply(), 1000);
        assert_eq!(price.get_price(), 10);
        assert_eq!(price.get_owner(), accounts.alice);
    }

    #[ink::test]
    fn access_to_non_message_functions() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(100_000, 100);
        let starting_supply = price.total_supply;
        // run non-message function `inflation` to add 999_999 to total_supply
        price.inflation();
        assert_eq!(price.total_supply, starting_supply + 999_999 )
    }

    #[ink::test]
    fn non_owner_set_price() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        // price set to 100 by alice at construction
        // change caller to bob
        test::set_caller::<DefaultEnvironment>(accounts.bob);
        //bob's attempt to set price and panic will result
        price.set_price(10);
        assert_eq!(price.get_price(), 10);
    }
    #[ink::test]
    fn non_owner_change_owner() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        // owner = alice
        // change caller to bob
        test::set_caller::<DefaultEnvironment>(accounts.bob);
        // attempt to set owner to bob and panic will result
        price.set_owner(accounts.bob);
        assert_eq!(price.get_owner(), accounts.bob);
    }
    #[ink::test]
    fn owner_change_owner() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        price.set_owner(accounts.django);
        assert_eq!(price.get_owner(), accounts.django);
        
        
    }
    #[ink::test]
    fn owner_change_price() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        price.set_price(50);
        assert_eq!(price.get_price(), 50);
        
    }
}
}
```

Unfortunately, Bob did not follow the [security guidelines](https://nagragrp.sharepoint.com/:w:/r/teams/INTERNALAlephZeroSecurityPartnershipP12831/Shared%20Documents/General/Technical%20Material/Security_Guideline/A0_dev_checklist.docx?d=wf2499ef60428424fa3255b30e238e6a4\&csf=1\&web=1\&e=PjMOAb).

What are the security vulnerabilities in the code presented in above?

\==- Hint Please have a look at the function `set_price` and `set_owner`&#x20;

\==- Answer The price and the owner of the smart contract can be modified by any user. ==- Let’s discuss the consequences of the vulnerabilities. Can you think of a way to exploit those vulnerabilities?

### Simulated Attack

Please download the executable of the contract here, deploy it on Aleph Zero testnet (<https://test.azero.dev/> ) and try to attack it. Any

Did you succeed? Yes! Well done, if not please do not worry and have a look at the proposed attack below.

\==- Attack

#### Setup

To simulate you will need two founded accounts, please watch the video of \[Course\_04]\(add link) if you do not know how to do it.

```sh
cargo install cargo-contract --version 2.0.0-beta.1
```

```sh
cargo contract new price
cd price 
```

then copy paste the code above into the lib.rs file

```sh
cargo +nightly test -- --nocapture
```

Tests Output

```sh
running 6 tests
test price::tests::non_owner_change_owner ... ok
test price::tests::access_to_non_message_functions ... ok
test price::tests::owner_change_owner ... ok
test price::tests::contract_construction ... ok
test price::tests::non_owner_set_price ... ok
test price::tests::owner_change_price ... ok
```

#### Build and Deploy to Testnet

**Using Cargo Contract Command line**

```sh
cargo +nightly contract build --release
```

```sh
export SEED="[put your 12 words seed phrase here]"
export URL="wss://ws.test.azero.dev"
```

```sh
cargo contract instantiate --suri "$SEED" --url "$URL" \
        --constructor new \
        --args 1000 450
```

Output

```sh
 Dry-running new (skip with --skip-dry-run)
    Success! Gas required estimated at Weight(ref_time: 513761328, proof_size: 0)
Confirm transaction details: (skip with --skip-confirm)
 Constructor new
        Args 1000 450
   Gas limit Weight(ref_time: 513761328, proof_size: 0)
Submit? (Y/n): y
    Contract 5D3U2wgaBKaYDA7459TPrCJZ8LBVfTA44JLf5W1WRHuE1bey
      Events
      ....
```

```sh
export CONTRACT="5D3U2wgaBKaYDA7459TPrCJZ8LBVfTA44JLf5W1WRHuE1bey"
```

```sh
cargo contract call --suri "$SEED" --url "$URL"  --contract "$CONTRACT"  --message get_price --dry-run
```

Output

```sh
    Result Success!
    Reverted false
    Data Tuple(Tuple { ident: Some("Ok"), values: [UInt(450)] })
```

Set price with second account(`$SEED2`)

```sh
cargo contract call --suri "$SEED2" --url "$URL"  --contract "$CONTRACT"  --message set_price --args 10    
```

```sh
cargo contract call --suri "$SEED" --url "$URL"  --contract "$CONTRACT"  --message get_price --dry-run 
```

```sh
    Result Success!
    Reverted false
    Data Tuple(Tuple { ident: Some("Ok"), values: [UInt(10)] })
```

User with `$SEED2` sets themselves to owner

```sh
cargo contract call --suri "$SEED2" --url "$URL"  --contract "$CONTRACT"  --message set_owner --args "5HiyV1tUmLy4ARX4tUk9eqMRt1D2dAc7WSPLLK22oAk6aoPK" 
```

```sh
cargo contract call --suri "$SEED" --url "$URL"  --contract "$CONTRACT"  --message get_owner --dry-run
```

```sh
    Result Success!
    Reverted false
    Data Tuple(Tuple { ident: Some("Ok"), values: [Literal("5HiyV1tUmLy4ARX4tUk9eqMRt1D2dAc7WSPLLK22oAk6aoPK")] })
```

**Using Contracts-UI from substrate.io**

Go to <https://contracts-ui.substrate.io/>

*Add New Contract*

Upload `money.contract` from the *target/ink* directory and click next

Set the supply to `10000` and the price to `450` and click *next*

Click *Upload and Instantiate*

The price is `450`

The owner is `justinTest` or `5EZTBFJJxgSFwSALjVitwzmuiTDzT5JjxoYmmaejo9ueBUwt`

The price being changed to `10` by `just2`

The price is set to `10`

`just2` setting themself to owner

The Owner is now `5HiyV1tUmLy4ARX4tUk9eqMRt1D2dAc7WSPLLK22oAk6aoPK` ==-

### Secure Solution

Now we have discovered the problem and its consequences. Let's talk about the secure way of developing the smart contract.

Any idea? Let's look at the solution!

\==- Solution Verify for the desired function that the owner of the smart contract is also the caller of the function by checking `self.owner == self.env().caller()`. It is important to know that you could also give access to other users. ==-

You can see below a secure way to implement the smart contract that Bob desired.

\==- Reveal Secure Implementation

```rs
# Secure Smart Contract
#![cfg_attr(not(feature = "std"), no_std)]


#[ink::contract]
mod price {
    use ink::storage::Mapping;
   
    #[ink(storage)]
    #[derive(Default)]
    pub struct Price {
        total_supply: u32,
        price: u32,
        owner: AccountId,
        balances: Mapping<AccountId, u32>,
        
    }
    impl Price {
        #[ink(constructor)]
        pub fn new(supply: u32, price: u32) -> Self {
            let mut balances = Mapping::default();
            let caller = Self::env().caller();
            balances.insert(caller, &supply);
              Self{ 
                total_supply:supply,
                price : price,
                balances,
                owner: caller,
              }
            
        }

        #[ink(message)]
        pub fn total_supply(&self) -> u32 {
            self.total_supply
        }
        #[ink(message)]
        pub fn set_price(&mut self, price: u32) {
            if self.owner == self.env().caller() {
                self.price = price
            }
        }
        #[ink(message)]
        pub fn get_price(&self) -> u32 {
            self.price
        }

        #[ink(message)]
        pub fn get_owner(&self) -> AccountId {
            self.owner
        }
        #[ink(message)]
        pub fn set_owner(&mut self, new_owner: AccountId) {
            if self.owner == self.env().caller() {
                self.owner = new_owner
            }
        }
        /// Simply returns the current value of our `bool`.
        #[ink(message)]
        pub fn balance_of(&self, account: AccountId) -> u32 {
            match self.balances.get(&account) {
                Some(value) => value,
                None => 0,
            }
        }

        pub fn inflation(&mut self) {
            self.total_supply += 999_999
        }

        #[ink(message)]
        pub fn transfer(&mut self, recipient: AccountId, amount: u32) {
            let sender = self.env().caller();
            let sender_balance = self.balance_of(sender);
            if sender_balance < amount {
                return;
            }
            self.balances.insert(sender, &(sender_balance - amount));
            let recipient_balance = self.balance_of(recipient);
            self.balances.insert(recipient, &(recipient_balance + amount));
        }
    }

    #[cfg(test)]
mod tests {
    use crate::price::Price;
    use ink::env::{test, DefaultEnvironment};

    #[ink::test]
    fn contract_construction() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let price = Price::new(1000, 10);
        assert_eq!(price.total_supply(), 1000);
        assert_eq!(price.get_price(), 10);
        assert_eq!(price.get_owner(), accounts.alice);
    }

    #[ink::test]
    fn access_to_non_message_functions() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(100_000, 100);
        let starting_supply = price.total_supply;
        // run non-message function `inflation` to add 999_999 to total_supply
        price.inflation();
        assert_eq!(price.total_supply, starting_supply + 999_999 )
    }

    #[ink::test]
    fn non_owner_set_price() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        // price set to 100 by alice at construction
        // change caller to bob
        test::set_caller::<DefaultEnvironment>(accounts.bob);
        //bob's attempt to set price and no change since bob is not an owner
        price.set_price(10);
        assert_eq!(price.get_price(), 100); 
    }
    #[ink::test]
    fn non_owner_change_owner() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        // owner = alice
        // change caller to bob
        test::set_caller::<DefaultEnvironment>(accounts.bob);
        // attempt to set owner to bob and no change since owner is alice.
        price.set_owner(accounts.bob);
        assert_eq!(price.get_owner(), accounts.alice);
    }
    #[ink::test]
    fn owner_change_owner() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        price.set_owner(accounts.django);
        assert_eq!(price.get_owner(), accounts.django);
        
        
    }
    #[ink::test]
    fn owner_change_price() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut price = Price::new(10000, 100);
        price.set_price(50);
        assert_eq!(price.get_price(), 50);
        
    }
}
}

```

\==- If you want can verify that this new source code secures the smart contract by replaying the attack over that has been done above.

### Question

If you are up to the challenge, see if you know the answer to this question:&#x20;

**True or false**: Which other identity than the contract owner could be allowed to modified the price of the token?&#x20;

\==- Answers **False**. For example, an oracle if the price depends of some date external to the blockchain. ==-


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.alephzero.org/aleph-zero/build/security-course-by-kudelski-security/lesson-5-role-based-access-control.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
