#[allow(unused_imports)]
use crate as casper_contract_sdk;
use casper_contract_macros::casper;
use crate::{
casper::{self, Entity},
collections::{sorted_vector::SortedVector, Map},
};
pub type Role = [u8; 32];
const ROLES_PREFIX: &str = "roles";
#[casper(path = "crate")]
pub struct AccessControlState {
roles: Map<Entity, SortedVector<Role>>,
}
impl AccessControlState {
pub fn new() -> Self {
Self {
roles: Map::new(ROLES_PREFIX),
}
}
}
impl Default for AccessControlState {
fn default() -> Self {
Self::new()
}
}
#[casper(path = "crate")]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum AccessControlError {
NotAuthorized,
}
#[casper(path = "crate", export = true)]
pub trait AccessControl {
#[casper(private)]
fn state(&self) -> &AccessControlState;
#[casper(private)]
fn state_mut(&mut self) -> &mut AccessControlState;
#[casper(private)]
fn has_role(&self, entity: Entity, role: Role) -> bool {
match self.state().roles.get(&entity) {
Some(roles) => roles.contains(&role),
None => false,
}
}
#[casper(private)]
fn has_any_role(&self, entity: Entity, roles: &[Role]) -> bool {
match self.state().roles.get(&entity) {
Some(roles_vec) => roles_vec.iter().any(|r| roles.contains(&r)),
None => false,
}
}
#[casper(private)]
fn grant_role(&mut self, entity: Entity, role: Role) {
match self.state_mut().roles.get(&entity) {
Some(mut roles) => {
if roles.contains(&role) {
return;
}
roles.push(role);
}
None => {
let mut roles = SortedVector::new(format!(
"{ROLES_PREFIX}-{:02x}{}",
entity.tag(),
base16::encode_lower(&entity.address())
));
roles.push(role);
self.state_mut().roles.insert(&entity, &roles);
}
}
}
#[casper(private)]
fn revoke_role(&mut self, entity: Entity, role: Role) {
if let Some(mut roles) = self.state_mut().roles.get(&entity) {
roles.retain(|r| r != &role);
}
}
#[casper(private)]
fn require_role(&self, role: Role) -> Result<(), AccessControlError> {
let caller = casper::get_caller();
if !self.has_role(caller, role) {
return Err(AccessControlError::NotAuthorized);
}
Ok(())
}
#[casper(private)]
fn require_any_role(&self, roles: &[Role]) -> Result<(), AccessControlError> {
let caller = casper::get_caller();
if !self.has_any_role(caller, roles) {
return Err(AccessControlError::NotAuthorized);
}
Ok(())
}
}