feat(context): add Context model with admin-based ACL system
This commit is contained in:
18
lib/models/context/Cargo.toml
Normal file
18
lib/models/context/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "hero-context"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Context model for Hero platform"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
chrono.workspace = true
|
||||
rhai = { version = "1.19", features = ["sync"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
uuid.workspace = true
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
uuid = { workspace = true, features = ["js"] }
|
||||
181
lib/models/context/src/access.rs
Normal file
181
lib/models/context/src/access.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
//! Access Control Logic for Context
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Unified ACL configuration for objects
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ObjectAcl {
|
||||
/// Per-user permissions for this object type
|
||||
/// Maps public key -> list of permissions
|
||||
pub permissions: HashMap<String, Vec<ObjectPermission>>,
|
||||
|
||||
/// Multi-signature requirements (optional)
|
||||
pub multi_sig: Option<MultiSigRequirement>,
|
||||
}
|
||||
|
||||
/// Permissions for object operations
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ObjectPermission {
|
||||
/// Can create new objects of this type
|
||||
Create,
|
||||
|
||||
/// Can read objects of this type
|
||||
Read,
|
||||
|
||||
/// Can update existing objects of this type
|
||||
Update,
|
||||
|
||||
/// Can delete objects of this type
|
||||
Delete,
|
||||
|
||||
/// Can list all objects of this type
|
||||
List,
|
||||
}
|
||||
|
||||
/// SAL access control - binary permission (can call or not)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SalAcl {
|
||||
/// List of public keys allowed to call this SAL
|
||||
pub allowed_callers: Vec<String>,
|
||||
|
||||
/// Multi-signature requirements (optional)
|
||||
pub multi_sig: Option<MultiSigRequirement>,
|
||||
}
|
||||
|
||||
/// Global permissions - simple RWX model
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum GlobalPermission {
|
||||
/// Can read data
|
||||
Read,
|
||||
|
||||
/// Can write/modify data
|
||||
Write,
|
||||
|
||||
/// Can execute operations
|
||||
Execute,
|
||||
}
|
||||
|
||||
/// Multi-signature requirements
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum MultiSigRequirement {
|
||||
/// Require ALL specified signers to sign unanimously
|
||||
Unanimous {
|
||||
/// List of public keys that must ALL sign
|
||||
required_signers: Vec<String>,
|
||||
},
|
||||
|
||||
/// Require a minimum number of signatures from a set
|
||||
Threshold {
|
||||
/// Minimum number of signatures required
|
||||
min_signatures: usize,
|
||||
|
||||
/// Optional: specific set of allowed signers
|
||||
/// If None, any signers from the context are allowed
|
||||
allowed_signers: Option<Vec<String>>,
|
||||
},
|
||||
|
||||
/// Require a percentage of signers from a set
|
||||
Percentage {
|
||||
/// Percentage required (0.0 to 1.0, e.g., 0.66 for 66%)
|
||||
percentage: f64,
|
||||
|
||||
/// Optional: specific set of allowed signers
|
||||
/// If None, any signers from the context are allowed
|
||||
allowed_signers: Option<Vec<String>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl MultiSigRequirement {
|
||||
/// Check if signatories satisfy this multi-sig requirement
|
||||
pub fn check(&self, signatories: &[String], total_members: usize) -> bool {
|
||||
match self {
|
||||
MultiSigRequirement::Unanimous { required_signers } => {
|
||||
// ALL required signers must be present
|
||||
required_signers.iter().all(|signer| signatories.contains(signer))
|
||||
}
|
||||
MultiSigRequirement::Threshold { min_signatures, allowed_signers } => {
|
||||
// Check if we have enough signatures
|
||||
if signatories.len() < *min_signatures {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If allowed_signers is specified, check all signatories are in the list
|
||||
if let Some(allowed) = allowed_signers {
|
||||
signatories.iter().all(|sig| allowed.contains(sig))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
MultiSigRequirement::Percentage { percentage, allowed_signers } => {
|
||||
if let Some(allowed) = allowed_signers {
|
||||
// Filter signatories to only those in allowed list
|
||||
let valid_sigs: Vec<_> = signatories
|
||||
.iter()
|
||||
.filter(|sig| allowed.contains(sig))
|
||||
.collect();
|
||||
|
||||
let required_count = (allowed.len() as f64 * percentage).ceil() as usize;
|
||||
valid_sigs.len() >= required_count
|
||||
} else {
|
||||
// Use all context members
|
||||
let required_count = (total_members as f64 * percentage).ceil() as usize;
|
||||
signatories.len() >= required_count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_multi_sig_unanimous() {
|
||||
let multi_sig = MultiSigRequirement::Unanimous {
|
||||
required_signers: vec!["alice".to_string(), "bob".to_string()],
|
||||
};
|
||||
|
||||
// Both signers present - should pass
|
||||
assert!(multi_sig.check(&["alice".to_string(), "bob".to_string()], 3));
|
||||
|
||||
// Only one signer - should fail
|
||||
assert!(!multi_sig.check(&["alice".to_string()], 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_sig_threshold() {
|
||||
let multi_sig = MultiSigRequirement::Threshold {
|
||||
min_signatures: 2,
|
||||
allowed_signers: Some(vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()]),
|
||||
};
|
||||
|
||||
// 2 signatures - should pass
|
||||
assert!(multi_sig.check(&["alice".to_string(), "bob".to_string()], 3));
|
||||
|
||||
// 1 signature - should fail
|
||||
assert!(!multi_sig.check(&["alice".to_string()], 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_sig_percentage() {
|
||||
let multi_sig = MultiSigRequirement::Percentage {
|
||||
percentage: 0.66, // 66%
|
||||
allowed_signers: Some(vec![
|
||||
"alice".to_string(),
|
||||
"bob".to_string(),
|
||||
"charlie".to_string(),
|
||||
]),
|
||||
};
|
||||
|
||||
// 2 out of 3 (66%) - should pass
|
||||
assert!(multi_sig.check(&["alice".to_string(), "bob".to_string()], 3));
|
||||
|
||||
// 1 out of 3 (33%) - should fail
|
||||
assert!(!multi_sig.check(&["alice".to_string()], 3));
|
||||
}
|
||||
}
|
||||
343
lib/models/context/src/lib.rs
Normal file
343
lib/models/context/src/lib.rs
Normal file
@@ -0,0 +1,343 @@
|
||||
//! Context Model
|
||||
//!
|
||||
//! A Context represents an isolated instance/workspace where users can:
|
||||
//! - Store and retrieve objects (via Osiris)
|
||||
//! - Execute SALs (System Abstraction Layer functions)
|
||||
//! - Collaborate with specific permissions
|
||||
//!
|
||||
//! The Context is the authorization boundary - all operations go through it
|
||||
//! and are subject to ACL checks.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod access;
|
||||
pub mod rhai;
|
||||
|
||||
pub use access::*;
|
||||
|
||||
/// A Context represents an isolated workspace with ACL-controlled access
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Context {
|
||||
/// Human-readable name
|
||||
pub name: String,
|
||||
|
||||
/// Description of this context's purpose
|
||||
pub description: Option<String>,
|
||||
|
||||
/// List of admin public keys - only admins can modify ACLs
|
||||
pub admins: Vec<String>,
|
||||
|
||||
/// Global permissions (RWX) - what can users do in this context?
|
||||
/// Maps public key -> list of global permissions
|
||||
pub global_permissions: HashMap<String, Vec<GlobalPermission>>,
|
||||
|
||||
/// Per-object-type ACLs - fine-grained control over data operations
|
||||
/// Maps object type (e.g., "notes", "events") -> ACL configuration
|
||||
pub object_acls: HashMap<String, ObjectAcl>,
|
||||
|
||||
/// SAL ACLs - binary permission (can call or not)
|
||||
/// Maps SAL name (e.g., "launch_vm", "send_email") -> ACL configuration
|
||||
pub sal_acls: HashMap<String, SalAcl>,
|
||||
}
|
||||
|
||||
impl Default for Context {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::new(),
|
||||
description: None,
|
||||
admins: Vec::new(),
|
||||
global_permissions: HashMap::new(),
|
||||
object_acls: HashMap::new(),
|
||||
sal_acls: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Create a new context with a name and initial admin
|
||||
pub fn new(name: String, admin: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
admins: vec![admin],
|
||||
global_permissions: HashMap::new(),
|
||||
object_acls: HashMap::new(),
|
||||
sal_acls: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a user is an admin
|
||||
pub fn is_admin(&self, pubkey: &str) -> bool {
|
||||
self.admins.contains(&pubkey.to_string())
|
||||
}
|
||||
|
||||
/// Check if a user has a global permission
|
||||
pub fn has_global_permission(&self, pubkey: &str, permission: &GlobalPermission) -> bool {
|
||||
self.global_permissions
|
||||
.get(pubkey)
|
||||
.map(|perms| perms.contains(permission))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if a user has permission for an object type
|
||||
pub fn has_object_permission(
|
||||
&self,
|
||||
pubkey: &str,
|
||||
object_type: &str,
|
||||
permission: &ObjectPermission,
|
||||
) -> bool {
|
||||
self.object_acls
|
||||
.get(object_type)
|
||||
.and_then(|acl| acl.permissions.get(pubkey))
|
||||
.map(|perms| perms.contains(permission))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if a user can call a SAL
|
||||
pub fn can_call_sal(&self, pubkey: &str, sal_name: &str) -> bool {
|
||||
self.sal_acls
|
||||
.get(sal_name)
|
||||
.map(|acl| acl.allowed_callers.contains(&pubkey.to_string()))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if signatories satisfy multi-sig requirements for an object
|
||||
pub fn check_object_multi_sig(
|
||||
&self,
|
||||
signatories: &[String],
|
||||
object_type: &str,
|
||||
) -> bool {
|
||||
if let Some(acl) = self.object_acls.get(object_type) {
|
||||
if let Some(multi_sig) = &acl.multi_sig {
|
||||
return multi_sig.check(signatories, self.global_permissions.len());
|
||||
}
|
||||
}
|
||||
// No multi-sig requirement
|
||||
true
|
||||
}
|
||||
|
||||
/// Check if signatories satisfy multi-sig requirements for a SAL
|
||||
pub fn check_sal_multi_sig(
|
||||
&self,
|
||||
signatories: &[String],
|
||||
sal_name: &str,
|
||||
) -> bool {
|
||||
if let Some(acl) = self.sal_acls.get(sal_name) {
|
||||
if let Some(multi_sig) = &acl.multi_sig {
|
||||
return multi_sig.check(signatories, self.global_permissions.len());
|
||||
}
|
||||
}
|
||||
// No multi-sig requirement
|
||||
true
|
||||
}
|
||||
|
||||
/// Add an admin (only admins can call this)
|
||||
pub fn add_admin(&mut self, caller: &str, new_admin: String) -> Result<(), String> {
|
||||
if !self.is_admin(caller) {
|
||||
return Err("Only admins can add admins".to_string());
|
||||
}
|
||||
if !self.admins.contains(&new_admin) {
|
||||
self.admins.push(new_admin);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Grant a global permission to a user (only admins can call this)
|
||||
pub fn grant_global_permission(
|
||||
&mut self,
|
||||
caller: &str,
|
||||
pubkey: String,
|
||||
permission: GlobalPermission,
|
||||
) -> Result<(), String> {
|
||||
if !self.is_admin(caller) {
|
||||
return Err("Only admins can grant permissions".to_string());
|
||||
}
|
||||
self.global_permissions
|
||||
.entry(pubkey)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(permission);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Grant an object permission to a user (only admins can call this)
|
||||
pub fn grant_object_permission(
|
||||
&mut self,
|
||||
caller: &str,
|
||||
pubkey: String,
|
||||
object_type: String,
|
||||
permission: ObjectPermission,
|
||||
) -> Result<(), String> {
|
||||
if !self.is_admin(caller) {
|
||||
return Err("Only admins can grant permissions".to_string());
|
||||
}
|
||||
self.object_acls
|
||||
.entry(object_type)
|
||||
.or_insert_with(|| ObjectAcl {
|
||||
permissions: HashMap::new(),
|
||||
multi_sig: None,
|
||||
})
|
||||
.permissions
|
||||
.entry(pubkey)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(permission);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Grant SAL access to a user (only admins can call this)
|
||||
pub fn grant_sal_access(
|
||||
&mut self,
|
||||
caller: &str,
|
||||
pubkey: String,
|
||||
sal_name: String,
|
||||
) -> Result<(), String> {
|
||||
if !self.is_admin(caller) {
|
||||
return Err("Only admins can grant SAL access".to_string());
|
||||
}
|
||||
self.sal_acls
|
||||
.entry(sal_name)
|
||||
.or_insert_with(|| SalAcl {
|
||||
allowed_callers: Vec::new(),
|
||||
multi_sig: None,
|
||||
})
|
||||
.allowed_callers
|
||||
.push(pubkey);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set multi-sig requirement for an object (only admins can call this)
|
||||
pub fn set_object_multi_sig(
|
||||
&mut self,
|
||||
caller: &str,
|
||||
object_type: String,
|
||||
multi_sig: MultiSigRequirement,
|
||||
) -> Result<(), String> {
|
||||
if !self.is_admin(caller) {
|
||||
return Err("Only admins can set multi-sig requirements".to_string());
|
||||
}
|
||||
self.object_acls
|
||||
.entry(object_type)
|
||||
.or_insert_with(|| ObjectAcl {
|
||||
permissions: HashMap::new(),
|
||||
multi_sig: None,
|
||||
})
|
||||
.multi_sig = Some(multi_sig);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set multi-sig requirement for a SAL (only admins can call this)
|
||||
pub fn set_sal_multi_sig(
|
||||
&mut self,
|
||||
caller: &str,
|
||||
sal_name: String,
|
||||
multi_sig: MultiSigRequirement,
|
||||
) -> Result<(), String> {
|
||||
if !self.is_admin(caller) {
|
||||
return Err("Only admins can set multi-sig requirements".to_string());
|
||||
}
|
||||
self.sal_acls
|
||||
.entry(sal_name)
|
||||
.or_insert_with(|| SalAcl {
|
||||
allowed_callers: Vec::new(),
|
||||
multi_sig: None,
|
||||
})
|
||||
.multi_sig = Some(multi_sig);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_context_creation() {
|
||||
let ctx = Context::new("Test Context".to_string(), "admin_pk".to_string());
|
||||
assert_eq!(ctx.name, "Test Context");
|
||||
assert!(ctx.is_admin("admin_pk"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_admin_permissions() {
|
||||
let mut ctx = Context::new("Test".to_string(), "admin".to_string());
|
||||
|
||||
// Admin can add another admin
|
||||
assert!(ctx.add_admin("admin", "admin2".to_string()).is_ok());
|
||||
assert!(ctx.is_admin("admin2"));
|
||||
|
||||
// Non-admin cannot add admin
|
||||
assert!(ctx.add_admin("user1", "admin3".to_string()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_global_permissions() {
|
||||
let mut ctx = Context::new("Test".to_string(), "admin".to_string());
|
||||
|
||||
// Admin can grant permissions
|
||||
assert!(ctx.grant_global_permission("admin", "user1".to_string(), GlobalPermission::Read).is_ok());
|
||||
assert!(ctx.has_global_permission("user1", &GlobalPermission::Read));
|
||||
assert!(!ctx.has_global_permission("user1", &GlobalPermission::Write));
|
||||
|
||||
// Non-admin cannot grant permissions
|
||||
assert!(ctx.grant_global_permission("user1", "user2".to_string(), GlobalPermission::Read).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_permissions() {
|
||||
let mut ctx = Context::new("Test".to_string(), "admin".to_string());
|
||||
|
||||
// Admin can grant object permissions
|
||||
assert!(ctx.grant_object_permission("admin", "user1".to_string(), "notes".to_string(), ObjectPermission::Create).is_ok());
|
||||
assert!(ctx.has_object_permission("user1", "notes", &ObjectPermission::Create));
|
||||
assert!(!ctx.has_object_permission("user1", "notes", &ObjectPermission::Delete));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sal_permissions() {
|
||||
let mut ctx = Context::new("Test".to_string(), "admin".to_string());
|
||||
|
||||
// Admin can grant SAL access
|
||||
assert!(ctx.grant_sal_access("admin", "user1".to_string(), "launch_vm".to_string()).is_ok());
|
||||
assert!(ctx.can_call_sal("user1", "launch_vm"));
|
||||
assert!(!ctx.can_call_sal("user1", "send_email"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_multi_sig_unanimous() {
|
||||
let mut ctx = Context::new("Test".to_string(), "admin".to_string());
|
||||
|
||||
assert!(ctx.set_object_multi_sig(
|
||||
"admin",
|
||||
"sensitive_data".to_string(),
|
||||
MultiSigRequirement::Unanimous {
|
||||
required_signers: vec!["alice".to_string(), "bob".to_string()],
|
||||
},
|
||||
).is_ok());
|
||||
|
||||
// Both signers present - should pass
|
||||
assert!(ctx.check_object_multi_sig(&["alice".to_string(), "bob".to_string()], "sensitive_data"));
|
||||
|
||||
// Only one signer - should fail
|
||||
assert!(!ctx.check_object_multi_sig(&["alice".to_string()], "sensitive_data"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sal_multi_sig_threshold() {
|
||||
let mut ctx = Context::new("Test".to_string(), "admin".to_string());
|
||||
|
||||
assert!(ctx.set_sal_multi_sig(
|
||||
"admin",
|
||||
"launch_vm".to_string(),
|
||||
MultiSigRequirement::Threshold {
|
||||
min_signatures: 2,
|
||||
allowed_signers: Some(vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()]),
|
||||
},
|
||||
).is_ok());
|
||||
|
||||
// 2 signatures - should pass
|
||||
assert!(ctx.check_sal_multi_sig(&["alice".to_string(), "bob".to_string()], "launch_vm"));
|
||||
|
||||
// 1 signature - should fail
|
||||
assert!(!ctx.check_sal_multi_sig(&["alice".to_string()], "launch_vm"));
|
||||
}
|
||||
}
|
||||
327
lib/models/context/src/rhai.rs
Normal file
327
lib/models/context/src/rhai.rs
Normal file
@@ -0,0 +1,327 @@
|
||||
use ::rhai::plugin::*;
|
||||
use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder};
|
||||
|
||||
use crate::Context;
|
||||
|
||||
// ============================================================================
|
||||
// Context Module
|
||||
// ============================================================================
|
||||
|
||||
type RhaiContext = Context;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_context_module {
|
||||
use super::RhaiContext;
|
||||
use crate::MultiSigRequirement;
|
||||
use ::rhai::{Dynamic, EvalAltResult};
|
||||
|
||||
/// Create a new context with name and initial admin
|
||||
#[rhai_fn(name = "new_context", return_raw)]
|
||||
pub fn new_context(name: String, admin: String) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
Ok(RhaiContext::new(name, admin))
|
||||
}
|
||||
|
||||
/// Set context description
|
||||
#[rhai_fn(name = "description", return_raw)]
|
||||
pub fn set_description(
|
||||
ctx: &mut RhaiContext,
|
||||
description: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
ctx.description = Some(description);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
// ========== Admin Management ==========
|
||||
|
||||
/// Check if a user is an admin
|
||||
#[rhai_fn(name = "is_admin")]
|
||||
pub fn is_admin(ctx: &mut RhaiContext, pubkey: String) -> bool {
|
||||
ctx.is_admin(&pubkey)
|
||||
}
|
||||
|
||||
/// Add an admin (only admins can call this)
|
||||
#[rhai_fn(name = "add_admin", return_raw)]
|
||||
pub fn add_admin(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
new_admin: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
ctx.add_admin(&caller, new_admin)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
// ========== Global Permission Management (RWX) ==========
|
||||
|
||||
/// Grant a global permission to a user (only admins can call this)
|
||||
#[rhai_fn(name = "grant_global_permission", return_raw)]
|
||||
pub fn grant_global_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
pubkey: String,
|
||||
permission: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let perm = parse_global_permission(&permission)?;
|
||||
ctx.grant_global_permission(&caller, pubkey, perm)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Check if a user has a global permission
|
||||
#[rhai_fn(name = "has_global_permission", return_raw)]
|
||||
pub fn has_global_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
permission: String,
|
||||
) -> Result<bool, Box<EvalAltResult>> {
|
||||
let perm = parse_global_permission(&permission)?;
|
||||
Ok(ctx.has_global_permission(&pubkey, &perm))
|
||||
}
|
||||
|
||||
// ========== Object Permission Management ==========
|
||||
|
||||
/// Grant an object permission to a user (only admins can call this)
|
||||
#[rhai_fn(name = "grant_object_permission", return_raw)]
|
||||
pub fn grant_object_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
pubkey: String,
|
||||
object_type: String,
|
||||
permission: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let perm = parse_object_permission(&permission)?;
|
||||
ctx.grant_object_permission(&caller, pubkey, object_type, perm)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Check if a user has an object permission
|
||||
#[rhai_fn(name = "has_object_permission", return_raw)]
|
||||
pub fn has_object_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
object_type: String,
|
||||
permission: String,
|
||||
) -> Result<bool, Box<EvalAltResult>> {
|
||||
let perm = parse_object_permission(&permission)?;
|
||||
Ok(ctx.has_object_permission(&pubkey, &object_type, &perm))
|
||||
}
|
||||
|
||||
// ========== SAL Permission Management (Binary) ==========
|
||||
|
||||
/// Grant SAL access to a user (only admins can call this)
|
||||
#[rhai_fn(name = "grant_sal_access", return_raw)]
|
||||
pub fn grant_sal_access(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
pubkey: String,
|
||||
sal_name: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
ctx.grant_sal_access(&caller, pubkey, sal_name)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Check if a user can call a SAL
|
||||
#[rhai_fn(name = "can_call_sal")]
|
||||
pub fn can_call_sal(ctx: &mut RhaiContext, pubkey: String, sal_name: String) -> bool {
|
||||
ctx.can_call_sal(&pubkey, &sal_name)
|
||||
}
|
||||
|
||||
// ========== Multi-Sig Management for Objects ==========
|
||||
|
||||
/// Set unanimous multi-sig requirement for an object (only admins can call this)
|
||||
#[rhai_fn(name = "set_object_multisig_unanimous", return_raw)]
|
||||
pub fn set_object_multisig_unanimous(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
object_type: String,
|
||||
required_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers = parse_signers(required_signers)?;
|
||||
ctx.set_object_multi_sig(
|
||||
&caller,
|
||||
object_type,
|
||||
MultiSigRequirement::Unanimous { required_signers: signers },
|
||||
)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set threshold multi-sig requirement for an object (only admins can call this)
|
||||
#[rhai_fn(name = "set_object_multisig_threshold", return_raw)]
|
||||
pub fn set_object_multisig_threshold(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
object_type: String,
|
||||
min_signatures: i64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers = parse_signers(allowed_signers)?;
|
||||
ctx.set_object_multi_sig(
|
||||
&caller,
|
||||
object_type,
|
||||
MultiSigRequirement::Threshold {
|
||||
min_signatures: min_signatures as usize,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set percentage multi-sig requirement for an object (only admins can call this)
|
||||
#[rhai_fn(name = "set_object_multisig_percentage", return_raw)]
|
||||
pub fn set_object_multisig_percentage(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
object_type: String,
|
||||
percentage: f64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
if percentage < 0.0 || percentage > 1.0 {
|
||||
return Err("Percentage must be between 0.0 and 1.0".into());
|
||||
}
|
||||
let signers = parse_signers(allowed_signers)?;
|
||||
ctx.set_object_multi_sig(
|
||||
&caller,
|
||||
object_type,
|
||||
MultiSigRequirement::Percentage {
|
||||
percentage,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
// ========== Multi-Sig Management for SALs ==========
|
||||
|
||||
/// Set unanimous multi-sig requirement for a SAL (only admins can call this)
|
||||
#[rhai_fn(name = "set_sal_multisig_unanimous", return_raw)]
|
||||
pub fn set_sal_multisig_unanimous(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
sal_name: String,
|
||||
required_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers = parse_signers(required_signers)?;
|
||||
ctx.set_sal_multi_sig(
|
||||
&caller,
|
||||
sal_name,
|
||||
MultiSigRequirement::Unanimous { required_signers: signers },
|
||||
)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set threshold multi-sig requirement for a SAL (only admins can call this)
|
||||
#[rhai_fn(name = "set_sal_multisig_threshold", return_raw)]
|
||||
pub fn set_sal_multisig_threshold(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
sal_name: String,
|
||||
min_signatures: i64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers = parse_signers(allowed_signers)?;
|
||||
ctx.set_sal_multi_sig(
|
||||
&caller,
|
||||
sal_name,
|
||||
MultiSigRequirement::Threshold {
|
||||
min_signatures: min_signatures as usize,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set percentage multi-sig requirement for a SAL (only admins can call this)
|
||||
#[rhai_fn(name = "set_sal_multisig_percentage", return_raw)]
|
||||
pub fn set_sal_multisig_percentage(
|
||||
ctx: &mut RhaiContext,
|
||||
caller: String,
|
||||
sal_name: String,
|
||||
percentage: f64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
if percentage < 0.0 || percentage > 1.0 {
|
||||
return Err("Percentage must be between 0.0 and 1.0".into());
|
||||
}
|
||||
let signers = parse_signers(allowed_signers)?;
|
||||
ctx.set_sal_multi_sig(
|
||||
&caller,
|
||||
sal_name,
|
||||
MultiSigRequirement::Percentage {
|
||||
percentage,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
// ========== Getters ==========
|
||||
|
||||
#[rhai_fn(name = "get_name")]
|
||||
pub fn get_name(ctx: &mut RhaiContext) -> String {
|
||||
ctx.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_description")]
|
||||
pub fn get_description(ctx: &mut RhaiContext) -> String {
|
||||
ctx.description.clone().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions to parse permissions
|
||||
fn parse_global_permission(permission: &str) -> Result<crate::GlobalPermission, Box<EvalAltResult>> {
|
||||
match permission {
|
||||
"read" => Ok(crate::GlobalPermission::Read),
|
||||
"write" => Ok(crate::GlobalPermission::Write),
|
||||
"execute" => Ok(crate::GlobalPermission::Execute),
|
||||
_ => Err(format!("Invalid global permission: {}", permission).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_object_permission(permission: &str) -> Result<crate::ObjectPermission, Box<EvalAltResult>> {
|
||||
match permission {
|
||||
"create" => Ok(crate::ObjectPermission::Create),
|
||||
"read" => Ok(crate::ObjectPermission::Read),
|
||||
"update" => Ok(crate::ObjectPermission::Update),
|
||||
"delete" => Ok(crate::ObjectPermission::Delete),
|
||||
"list" => Ok(crate::ObjectPermission::List),
|
||||
_ => Err(format!("Invalid object permission: {}", permission).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_signers(signers: Vec<Dynamic>) -> Result<Vec<String>, Box<EvalAltResult>> {
|
||||
signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect::<Result<Vec<String>, _>>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))
|
||||
}
|
||||
|
||||
impl CustomType for Context {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("Context");
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the Context module with the Rhai engine
|
||||
pub fn register_context_module(engine: &mut Engine) {
|
||||
let module = exported_module!(rhai_context_module);
|
||||
engine.register_static_module("context", module.into());
|
||||
engine.register_type::<Context>();
|
||||
}
|
||||
|
||||
/// Register Context functions directly on the engine (for global access)
|
||||
pub fn register_context_functions(engine: &mut Engine) {
|
||||
engine.register_type::<Context>();
|
||||
|
||||
// Register the module functions
|
||||
let module = exported_module!(rhai_context_module);
|
||||
engine.register_global_module(module.into());
|
||||
}
|
||||
333
lib/models/context/src/rhai_old.rs
Normal file
333
lib/models/context/src/rhai_old.rs
Normal file
@@ -0,0 +1,333 @@
|
||||
use ::rhai::plugin::*;
|
||||
use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder};
|
||||
|
||||
use crate::Context;
|
||||
|
||||
// ============================================================================
|
||||
// Context Module
|
||||
// ============================================================================
|
||||
|
||||
type RhaiContext = Context;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_context_module {
|
||||
use super::RhaiContext;
|
||||
use crate::{GlobalPermission, MultiSigRequirement, ObjectPermission};
|
||||
use ::rhai::{Dynamic, EvalAltResult};
|
||||
|
||||
/// Create a new context with name and initial admin
|
||||
#[rhai_fn(name = "new_context", return_raw)]
|
||||
pub fn new_context(name: String, admin: String) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
Ok(RhaiContext::new(name, admin))
|
||||
}
|
||||
|
||||
/// Set context description
|
||||
#[rhai_fn(name = "description", return_raw)]
|
||||
pub fn set_description(
|
||||
ctx: &mut RhaiContext,
|
||||
description: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
ctx.description = Some(description);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
// ========== Global Permission Management ==========
|
||||
|
||||
/// Grant a global permission to a user
|
||||
#[rhai_fn(name = "grant_permission", return_raw)]
|
||||
pub fn grant_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
permission: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let perm = match permission.as_str() {
|
||||
"read" => Permission::Read,
|
||||
"write" => Permission::Write,
|
||||
"delete" => Permission::Delete,
|
||||
"execute" => Permission::Execute,
|
||||
"admin" => Permission::Admin,
|
||||
"invite" => Permission::Invite,
|
||||
_ => return Err(format!("Invalid permission: {}", permission).into()),
|
||||
};
|
||||
ctx.grant_permission(pubkey, perm);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Check if a user has a global permission
|
||||
#[rhai_fn(name = "has_permission", return_raw)]
|
||||
pub fn has_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
permission: String,
|
||||
) -> Result<bool, Box<EvalAltResult>> {
|
||||
let perm = match permission.as_str() {
|
||||
"read" => Permission::Read,
|
||||
"write" => Permission::Write,
|
||||
"delete" => Permission::Delete,
|
||||
"execute" => Permission::Execute,
|
||||
"admin" => Permission::Admin,
|
||||
"invite" => Permission::Invite,
|
||||
_ => return Err(format!("Invalid permission: {}", permission).into()),
|
||||
};
|
||||
Ok(ctx.has_permission(&pubkey, &perm))
|
||||
}
|
||||
|
||||
// ========== Object Permission Management ==========
|
||||
|
||||
/// Grant an object permission to a user
|
||||
#[rhai_fn(name = "grant_object_permission", return_raw)]
|
||||
pub fn grant_object_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
object_type: String,
|
||||
permission: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let perm = parse_resource_permission(&permission)?;
|
||||
ctx.grant_resource_permission(pubkey, object_type, perm, false);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Check if a user has an object permission
|
||||
#[rhai_fn(name = "has_object_permission", return_raw)]
|
||||
pub fn has_object_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
object_type: String,
|
||||
permission: String,
|
||||
) -> Result<bool, Box<EvalAltResult>> {
|
||||
let perm = parse_resource_permission(&permission)?;
|
||||
Ok(ctx.has_resource_permission(&pubkey, &object_type, &perm, false))
|
||||
}
|
||||
|
||||
// ========== SAL Permission Management ==========
|
||||
|
||||
/// Grant a SAL permission to a user
|
||||
#[rhai_fn(name = "grant_sal_permission", return_raw)]
|
||||
pub fn grant_sal_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
sal_name: String,
|
||||
permission: String,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let perm = parse_resource_permission(&permission)?;
|
||||
ctx.grant_resource_permission(pubkey, sal_name, perm, true);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Check if a user has a SAL permission
|
||||
#[rhai_fn(name = "has_sal_permission", return_raw)]
|
||||
pub fn has_sal_permission(
|
||||
ctx: &mut RhaiContext,
|
||||
pubkey: String,
|
||||
sal_name: String,
|
||||
permission: String,
|
||||
) -> Result<bool, Box<EvalAltResult>> {
|
||||
let perm = parse_resource_permission(&permission)?;
|
||||
Ok(ctx.has_resource_permission(&pubkey, &sal_name, &perm, true))
|
||||
}
|
||||
|
||||
// ========== Multi-Sig Management ==========
|
||||
|
||||
/// Set unanimous multi-sig requirement for an object
|
||||
#[rhai_fn(name = "set_object_multisig_unanimous", return_raw)]
|
||||
pub fn set_object_multisig_unanimous(
|
||||
ctx: &mut RhaiContext,
|
||||
object_type: String,
|
||||
required_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers: Result<Vec<String>, _> = required_signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect();
|
||||
|
||||
let signers = signers.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
|
||||
ctx.set_multi_sig(
|
||||
object_type,
|
||||
MultiSigRequirement::Unanimous { required_signers: signers },
|
||||
false,
|
||||
);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set threshold multi-sig requirement for an object
|
||||
#[rhai_fn(name = "set_object_multisig_threshold", return_raw)]
|
||||
pub fn set_object_multisig_threshold(
|
||||
ctx: &mut RhaiContext,
|
||||
object_type: String,
|
||||
min_signatures: i64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers: Result<Vec<String>, _> = allowed_signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect();
|
||||
|
||||
let signers = signers.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
|
||||
ctx.set_multi_sig(
|
||||
object_type,
|
||||
MultiSigRequirement::Threshold {
|
||||
min_signatures: min_signatures as usize,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
false,
|
||||
);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set percentage multi-sig requirement for an object
|
||||
#[rhai_fn(name = "set_object_multisig_percentage", return_raw)]
|
||||
pub fn set_object_multisig_percentage(
|
||||
ctx: &mut RhaiContext,
|
||||
object_type: String,
|
||||
percentage: f64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
if percentage < 0.0 || percentage > 1.0 {
|
||||
return Err("Percentage must be between 0.0 and 1.0".into());
|
||||
}
|
||||
|
||||
let signers: Result<Vec<String>, _> = allowed_signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect();
|
||||
|
||||
let signers = signers.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
|
||||
ctx.set_multi_sig(
|
||||
object_type,
|
||||
MultiSigRequirement::Percentage {
|
||||
percentage,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
false,
|
||||
);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set unanimous multi-sig requirement for a SAL
|
||||
#[rhai_fn(name = "set_sal_multisig_unanimous", return_raw)]
|
||||
pub fn set_sal_multisig_unanimous(
|
||||
ctx: &mut RhaiContext,
|
||||
sal_name: String,
|
||||
required_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers: Result<Vec<String>, _> = required_signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect();
|
||||
|
||||
let signers = signers.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
|
||||
ctx.set_multi_sig(
|
||||
sal_name,
|
||||
MultiSigRequirement::Unanimous { required_signers: signers },
|
||||
true,
|
||||
);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set threshold multi-sig requirement for a SAL
|
||||
#[rhai_fn(name = "set_sal_multisig_threshold", return_raw)]
|
||||
pub fn set_sal_multisig_threshold(
|
||||
ctx: &mut RhaiContext,
|
||||
sal_name: String,
|
||||
min_signatures: i64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
let signers: Result<Vec<String>, _> = allowed_signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect();
|
||||
|
||||
let signers = signers.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
|
||||
ctx.set_multi_sig(
|
||||
sal_name,
|
||||
MultiSigRequirement::Threshold {
|
||||
min_signatures: min_signatures as usize,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
true,
|
||||
);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
/// Set percentage multi-sig requirement for a SAL
|
||||
#[rhai_fn(name = "set_sal_multisig_percentage", return_raw)]
|
||||
pub fn set_sal_multisig_percentage(
|
||||
ctx: &mut RhaiContext,
|
||||
sal_name: String,
|
||||
percentage: f64,
|
||||
allowed_signers: Vec<Dynamic>,
|
||||
) -> Result<RhaiContext, Box<EvalAltResult>> {
|
||||
if percentage < 0.0 || percentage > 1.0 {
|
||||
return Err("Percentage must be between 0.0 and 1.0".into());
|
||||
}
|
||||
|
||||
let signers: Result<Vec<String>, _> = allowed_signers
|
||||
.into_iter()
|
||||
.map(|d| d.into_string().map_err(|e| format!("Invalid signer: {:?}", e)))
|
||||
.collect();
|
||||
|
||||
let signers = signers.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), rhai::Position::NONE)))?;
|
||||
|
||||
ctx.set_multi_sig(
|
||||
sal_name,
|
||||
MultiSigRequirement::Percentage {
|
||||
percentage,
|
||||
allowed_signers: Some(signers),
|
||||
},
|
||||
true,
|
||||
);
|
||||
Ok(ctx.clone())
|
||||
}
|
||||
|
||||
// ========== Getters ==========
|
||||
|
||||
#[rhai_fn(name = "get_name")]
|
||||
pub fn get_name(ctx: &mut RhaiContext) -> String {
|
||||
ctx.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_description")]
|
||||
pub fn get_description(ctx: &mut RhaiContext) -> String {
|
||||
ctx.description.clone().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to parse resource permissions
|
||||
fn parse_resource_permission(permission: &str) -> Result<crate::ResourcePermission, Box<EvalAltResult>> {
|
||||
match permission {
|
||||
"create" => Ok(crate::ResourcePermission::Create),
|
||||
"read" => Ok(crate::ResourcePermission::Read),
|
||||
"update" => Ok(crate::ResourcePermission::Update),
|
||||
"delete" => Ok(crate::ResourcePermission::Delete),
|
||||
"list" => Ok(crate::ResourcePermission::List),
|
||||
"execute" => Ok(crate::ResourcePermission::Execute),
|
||||
_ => Err(format!("Invalid resource permission: {}", permission).into()),
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for Context {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("Context");
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the Context module with the Rhai engine
|
||||
pub fn register_context_module(engine: &mut Engine) {
|
||||
let module = exported_module!(rhai_context_module);
|
||||
engine.register_static_module("context", module.into());
|
||||
engine.register_type::<Context>();
|
||||
}
|
||||
|
||||
/// Register Context functions directly on the engine (for global access)
|
||||
pub fn register_context_functions(engine: &mut Engine) {
|
||||
engine.register_type::<Context>();
|
||||
|
||||
// Register the module functions
|
||||
let module = exported_module!(rhai_context_module);
|
||||
engine.register_global_module(module.into());
|
||||
}
|
||||
Reference in New Issue
Block a user