Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
561 changes: 561 additions & 0 deletions src/components/admin_permission_manager.cairo

Large diffs are not rendered by default.

8 changes: 2 additions & 6 deletions src/components/member_manager.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,10 @@ pub mod MemberManagerComponent {
ref self: ComponentState<TContractState>, member_id: u256, amount: u256, timestamp: u64,
) {
let mut member_node = self.members.entry(member_id);
member_node
.total_received
.write(member_node.total_received.read() + 1);
member_node.total_received.write(member_node.total_received.read() + 1);
member_node.no_of_payouts.write(member_node.no_of_payouts.read() + 1);
member_node.last_disbursement_timestamp.write(timestamp);
member_node
.total_disbursements
.write(member_node.total_disbursements.read() + 1);
member_node.total_disbursements.write(member_node.total_disbursements.read() + 1);
}

/// Returns the address of the factory contract.
Expand Down
2 changes: 1 addition & 1 deletion src/components/organization.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ pub mod OrganizationComponent {
}

/// Used to get a contract ipfs hash for access purpose
/// ### Returns
/// ### Returns
/// - Contract: all the important info of the contract suitable for storage onchain.
/// - The rest goes to IPFS
fn get_contract(self: @ComponentState<TContractState>, contract_id: u256) -> Contract {
Expand Down
4 changes: 1 addition & 3 deletions src/contracts/core.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ mod Core {
use openzeppelin::upgrades::UpgradeableComponent;
use openzeppelin::upgrades::interface::IUpgradeable;
use starknet::storage::StoragePointerWriteAccess;
use starknet::{
ClassHash, ContractAddress, get_block_timestamp, get_contract_address,
};
use starknet::{ClassHash, ContractAddress, get_block_timestamp, get_contract_address};
use crate::interfaces::imember_manager::IMemberManager;

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
Expand Down
169 changes: 169 additions & 0 deletions src/interfaces/iadmin_permission_manager.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use littlefinger::structs::admin_permissions::AdminPermission;
use starknet::ContractAddress;

/// # IAdminPermissionManager
///
/// This trait defines the public interface for an admin permission management component.
/// It outlines the essential functions for handling administrative permissions within an
/// organization, including granting, revoking, and querying permissions for administrators. This
/// interface enables fine-grained control over what actions different administrators can perform.
/// This interface is designed to be implemented by a Starknet component that manages
/// the lifecycle and assignment of administrative permissions.
#[starknet::interface]
pub trait IAdminPermissionManager<TContractState> {
/// # has_admin_permission
///
/// Checks if a specific admin has a particular permission.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
/// - `admin`: The contract address of the admin to check.
/// - `permission`: The specific permission to verify.
///
/// ## Returns
///
/// A boolean indicating whether the admin has the specified permission.
fn has_admin_permission(
self: @TContractState, admin: ContractAddress, permission: AdminPermission,
) -> bool;

/// # get_admin_permissions
///
/// Retrieves all permissions currently granted to a specific admin.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
/// - `admin`: The contract address of the admin whose permissions to retrieve.
///
/// ## Returns
///
/// An array of `AdminPermission` values representing all permissions granted to the admin.
fn get_admin_permissions(
self: @TContractState, admin: ContractAddress,
) -> Array<AdminPermission>;

/// # grant_admin_permission
///
/// Grants a specific permission to an admin. This is a privileged action that requires
/// the caller to have GRANT_PERMISSIONS permission or be the owner.
///
/// ## Parameters
///
/// - `ref self: TContractState`: The current state of the contract.
/// - `admin`: The contract address of the admin to grant permission to.
/// - `permission`: The specific permission to grant.
fn grant_admin_permission(
ref self: TContractState, admin: ContractAddress, permission: AdminPermission,
);

/// # revoke_admin_permission
///
/// Revokes a specific permission from an admin. This is a privileged action that requires
/// the caller to have GRANT_PERMISSIONS permission or be the owner. Owner permissions cannot be
/// revoked.
///
/// ## Parameters
///
/// - `ref self: TContractState`: The current state of the contract.
/// - `admin`: The contract address of the admin to revoke permission from.
/// - `permission`: The specific permission to revoke.
fn revoke_admin_permission(
ref self: TContractState, admin: ContractAddress, permission: AdminPermission,
);

/// # grant_all_admin_permissions
///
/// Grants all available permissions to an admin at once. This is a privileged action
/// that requires the caller to have GRANT_PERMISSIONS permission or be the owner.
///
/// ## Parameters
///
/// - `ref self: TContractState`: The current state of the contract.
/// - `admin`: The contract address of the admin to grant all permissions to.
fn grant_all_admin_permissions(ref self: TContractState, admin: ContractAddress);

/// # revoke_all_admin_permissions
///
/// Revokes all permissions from an admin at once. This is a privileged action
/// that requires the caller to have GRANT_PERMISSIONS permission or be the owner.
/// Owner permissions cannot be revoked.
///
/// ## Parameters
///
/// - `ref self: TContractState`: The current state of the contract.
/// - `admin`: The contract address of the admin to revoke all permissions from.
fn revoke_all_admin_permissions(ref self: TContractState, admin: ContractAddress);

/// # get_owner
///
/// Retrieves the owner address of the contract. The owner has all permissions by default
/// and their permissions cannot be revoked.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
///
/// ## Returns
///
/// The `ContractAddress` of the contract owner.
fn get_owner(self: @TContractState) -> ContractAddress;

/// # is_owner
///
/// Checks if a given address is the contract owner.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
/// - `address`: The contract address to check for ownership.
///
/// ## Returns
///
/// A boolean indicating whether the address is the contract owner.
fn is_owner(self: @TContractState, address: ContractAddress) -> bool;

/// # permissions_to_mask
///
/// Converts an array of permissions to a bitmask representation for efficient storage and
/// operations.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
/// - `permissions`: An array of `AdminPermission` values to convert.
///
/// ## Returns
///
/// A `u16` bitmask representing the permissions.
fn permissions_to_mask(self: @TContractState, permissions: Array<AdminPermission>) -> u16;

/// # permissions_from_mask
///
/// Converts a bitmask representation back to an array of permissions.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
/// - `mask`: A `u16` bitmask representing permissions.
///
/// ## Returns
///
/// An array of `AdminPermission` values represented by the bitmask.
fn permissions_from_mask(self: @TContractState, mask: u16) -> Array<AdminPermission>;

/// # is_valid_admin_mask
///
/// Validates whether a bitmask represents a valid combination of admin permissions.
///
/// ## Parameters
///
/// - `self: @TContractState`: A snapshot of the contract's state.
/// - `mask`: A `u16` bitmask to validate.
///
/// ## Returns
///
/// A boolean indicating whether the bitmask is valid.
fn is_valid_admin_mask(self: @TContractState, mask: u16) -> bool;
}
5 changes: 5 additions & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod contracts {
}
pub mod interfaces {
pub mod dao_controller;
pub mod iadmin_permission_manager;
pub mod icore;
pub mod idisbursement;
pub mod ifactory;
Expand All @@ -14,13 +15,15 @@ pub mod interfaces {
}

pub mod components {
pub mod admin_permission_manager;
pub mod dao_controller;
pub mod disbursement;
pub mod member_manager;
pub mod organization;
}

pub mod structs {
pub mod admin_permissions;
pub mod base;
pub mod core;
pub mod dao_controller;
Expand All @@ -32,10 +35,12 @@ pub mod structs {

#[cfg(test)]
pub mod tests {
pub mod test_admin_permission_manager;
pub mod test_dao_controller;
pub mod test_disbursement;
pub mod test_member_manager;
pub mod mocks {
pub mod mock_admin_permission_manager;
pub mod mock_dao_controller;
pub mod mock_disbursement;
pub mod mock_member_manager;
Expand Down
145 changes: 145 additions & 0 deletions src/structs/admin_permissions.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use starknet::ContractAddress;

/// Admin permission enums for different administrative actions
#[derive(Drop, Copy, Serde, PartialEq)]
pub enum AdminPermission {
ADD_MEMBER,
REMOVE_MEMBER,
SEND_MEMBER_INVITES,
SET_BASE_SALARIES,
CHANGE_BASE_SALARIES,
SET_DISBURSEMENT_SCHEDULES,
ADD_VAULT_TOKENS,
VAULT_FUNCTIONS, // All vault functions except deposit
GRANT_ADMIN_STATUS,
REVOKE_ADMIN_STATUS,
GRANT_PERMISSIONS,
REVOKE_PERMISSIONS,
}

/// Trait for converting AdminPermission to felt252 for storage
pub impl AdminPermissionIntoFelt252 of Into<AdminPermission, felt252> {
fn into(self: AdminPermission) -> felt252 {
match self {
AdminPermission::ADD_MEMBER => 'ADD_MEMBER',
AdminPermission::REMOVE_MEMBER => 'REMOVE_MEMBER',
AdminPermission::SEND_MEMBER_INVITES => 'SEND_INVITES',
AdminPermission::SET_BASE_SALARIES => 'SET_SALARIES',
AdminPermission::CHANGE_BASE_SALARIES => 'CHANGE_SALARIES',
AdminPermission::SET_DISBURSEMENT_SCHEDULES => 'SET_SCHEDULES',
AdminPermission::ADD_VAULT_TOKENS => 'ADD_VAULT_TOKENS',
AdminPermission::VAULT_FUNCTIONS => 'VAULT_FUNCTIONS',
AdminPermission::GRANT_ADMIN_STATUS => 'GRANT_ADMIN_STATUS',
AdminPermission::REVOKE_ADMIN_STATUS => 'REVOKE_ADMIN_STATUS',
AdminPermission::GRANT_PERMISSIONS => 'GRANT_PERMISSIONS',
AdminPermission::REVOKE_PERMISSIONS => 'REVOKE_PERMISSIONS',
}
}
}

/// Trait for converting felt252 back to AdminPermission
pub impl Felt252IntoAdminPermission of Into<felt252, AdminPermission> {
fn into(self: felt252) -> AdminPermission {
if self == 'ADD_MEMBER' {
AdminPermission::ADD_MEMBER
} else if self == 'REMOVE_MEMBER' {
AdminPermission::REMOVE_MEMBER
} else if self == 'SEND_INVITES' {
AdminPermission::SEND_MEMBER_INVITES
} else if self == 'SET_SALARIES' {
AdminPermission::SET_BASE_SALARIES
} else if self == 'CHANGE_SALARIES' {
AdminPermission::CHANGE_BASE_SALARIES
} else if self == 'SET_SCHEDULES' {
AdminPermission::SET_DISBURSEMENT_SCHEDULES
} else if self == 'ADD_VAULT_TOKENS' {
AdminPermission::ADD_VAULT_TOKENS
} else if self == 'VAULT_FUNCTIONS' {
AdminPermission::VAULT_FUNCTIONS
} else if self == 'GRANT_ADMIN_STATUS' {
AdminPermission::GRANT_ADMIN_STATUS
} else if self == 'REVOKE_ADMIN_STATUS' {
AdminPermission::REVOKE_ADMIN_STATUS
} else if self == 'GRANT_PERMISSIONS' {
AdminPermission::GRANT_PERMISSIONS
} else if self == 'REVOKE_PERMISSIONS' {
AdminPermission::REVOKE_PERMISSIONS
} else {
AdminPermission::ADD_MEMBER // Default fallback
}
}
}

/// Trait for AdminPermission utilities
pub trait AdminPermissionTrait {
fn to_mask(self: AdminPermission) -> u16;
fn has_permission_from_mask(self: AdminPermission, mask: u16) -> bool;
fn get_all_permissions() -> Array<AdminPermission>;
}

pub impl AdminPermissionImpl of AdminPermissionTrait {
fn to_mask(self: AdminPermission) -> u16 {
match self {
AdminPermission::ADD_MEMBER => 1,
AdminPermission::REMOVE_MEMBER => 2,
AdminPermission::SEND_MEMBER_INVITES => 4,
AdminPermission::SET_BASE_SALARIES => 8,
AdminPermission::CHANGE_BASE_SALARIES => 16,
AdminPermission::SET_DISBURSEMENT_SCHEDULES => 32,
AdminPermission::ADD_VAULT_TOKENS => 64,
AdminPermission::VAULT_FUNCTIONS => 128,
AdminPermission::GRANT_ADMIN_STATUS => 256,
AdminPermission::REVOKE_ADMIN_STATUS => 512,
AdminPermission::GRANT_PERMISSIONS => 1024,
AdminPermission::REVOKE_PERMISSIONS => 2048,
}
}

fn has_permission_from_mask(self: AdminPermission, mask: u16) -> bool {
(mask & self.to_mask()) != 0
}

fn get_all_permissions() -> Array<AdminPermission> {
array![
AdminPermission::ADD_MEMBER,
AdminPermission::REMOVE_MEMBER,
AdminPermission::SEND_MEMBER_INVITES,
AdminPermission::SET_BASE_SALARIES,
AdminPermission::CHANGE_BASE_SALARIES,
AdminPermission::SET_DISBURSEMENT_SCHEDULES,
AdminPermission::ADD_VAULT_TOKENS,
AdminPermission::VAULT_FUNCTIONS,
AdminPermission::GRANT_ADMIN_STATUS,
AdminPermission::REVOKE_ADMIN_STATUS,
AdminPermission::GRANT_PERMISSIONS,
AdminPermission::REVOKE_PERMISSIONS,
]
}
}

/// Events for permission management
#[derive(Drop, starknet::Event)]
pub struct AdminPermissionGranted {
pub permission: felt252,
pub admin: ContractAddress,
pub granted_by: ContractAddress,
}

#[derive(Drop, starknet::Event)]
pub struct AdminPermissionRevoked {
pub permission: felt252,
pub admin: ContractAddress,
pub revoked_by: ContractAddress,
}

#[derive(Drop, starknet::Event)]
pub struct AllAdminPermissionsGranted {
pub admin: ContractAddress,
pub granted_by: ContractAddress,
}

#[derive(Drop, starknet::Event)]
pub struct AllAdminPermissionsRevoked {
pub admin: ContractAddress,
pub revoked_by: ContractAddress,
}
Loading
Loading