1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
#![feature(vec_remove_item)]

use hdk::prelude::*;
use holochain_anchors;
use serde_derive::{Deserialize, Serialize};

pub mod progenitor;
pub mod validation;
pub mod handlers;

pub const ROLE_TYPE: &'static str = "role";
pub const AGENT_TO_ROLE_LINK_TYPE: &'static str = "agent->role";
pub const ANCHOR_TO_ROLE_LINK_TYPE: &'static str = "anchor->role";
pub const ADMIN_ROLE_NAME: &'static str = "Admin";

#[derive(Serialize, Deserialize, Debug, DefaultJson, Clone)]
pub struct Role {
    pub role_name: String,
    pub members: Vec<Address>,
}

impl Role {
    pub fn from(role_name: String, members: Vec<Address>) -> Role {
        Role { role_name, members }
    }

    pub fn entry(&self) -> Entry {
        Entry::App(ROLE_TYPE.into(), self.into())
    }

    pub fn address(&self) -> ZomeApiResult<Address> {
        let initial_role_entry = Role::from(self.role_name.clone(), vec![]);

        hdk::entry_address(&initial_role_entry.entry())
    }
}

/**
 * Role entry definition
 * This function must be called from the zome entry point for this mixin to be setup properly
 */
pub fn role_entry_def() -> ValidatingEntryType {
    entry!(
        name: ROLE_TYPE,
        description: "role entry that contains a role name and the members of that role",
        sharing: Sharing::Public,
        validation_package: || {
            hdk::ValidationPackageDefinition::Entry
        },
        validation: | _validation_data: hdk::EntryValidationData<Role>| {
            match _validation_data {
                hdk::EntryValidationData::Create { entry, validation_data } => {
                    if entry.members.len() != 0 {
                        return Err(String::from("The first role entry cannot have any members"));
                    }

                    if entry.role_name == String::from(ADMIN_ROLE_NAME) {
                        return Ok(());
                    }

                    let agent_address = &validation_data.sources()[0];

                    match validation::is_agent_admin(&agent_address)? {
                        true => Ok(()),
                        false => Err(String::from("Only admins can create roles"))
                    }
                },
                hdk::EntryValidationData::Modify { validation_data, .. } => {
                    let agent_address = &validation_data.sources()[0];

                    match validation::is_agent_admin(&agent_address)? {
                        true => Ok(()),
                        false => Err(String::from("Only admins can assign roles"))
                    }
                },
                _ => Err(String::from("Cannot delete roles"))
            }
        },
        links: [
            from!(
                "%agent_id",
                link_type: AGENT_TO_ROLE_LINK_TYPE,

                validation_package: || {
                    hdk::ValidationPackageDefinition::Entry
                },

                validation: |_validation_data: hdk::LinkValidationData| {
                    Ok(())
                }
            ),
            from!(
                holochain_anchors::ANCHOR_TYPE,
                link_type: ANCHOR_TO_ROLE_LINK_TYPE,

                validation_package: || {
                    hdk::ValidationPackageDefinition::Entry
                },

                validation: |_validation_data: hdk::LinkValidationData| {
                    Ok(())
                }
            )
        ]
    )
}