-
Notifications
You must be signed in to change notification settings - Fork 316
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(BOUN-1236): add registry scraping for acls (#2425)
This PR makes the canister periodically poll the registry to list the principles of known API Boundary Nodes. These are then used in ACLs to only allow access to the canister by those principals (will be added in a follow-up PR).
- Loading branch information
Showing
4 changed files
with
176 additions
and
4 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,116 @@ | ||
// I put this here for CI, will remove on the next PR | ||
pub const TMP: u32 = 0; | ||
use std::{cell::RefCell, time::Duration}; | ||
|
||
use anonymization_interface::{ | ||
self as ifc, InitArg, QueryResponse, RegisterResponse, SubmitResponse, | ||
}; | ||
use candid::Principal; | ||
use ic_cdk::{id, spawn}; | ||
use ic_cdk_timers::set_timer_interval; | ||
use ic_nns_constants::REGISTRY_CANISTER_ID; | ||
use ic_stable_structures::{ | ||
memory_manager::{MemoryId, MemoryManager, VirtualMemory}, | ||
DefaultMemoryImpl, StableBTreeMap, | ||
}; | ||
use lazy_static::lazy_static; | ||
use registry::{Client, List}; | ||
|
||
mod registry; | ||
|
||
type Memory = VirtualMemory<DefaultMemoryImpl>; | ||
|
||
type StableMap<K, V> = StableBTreeMap<K, V, Memory>; | ||
type StableSet<T> = StableMap<T, ()>; | ||
|
||
thread_local! { | ||
static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> = | ||
RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); | ||
} | ||
|
||
const MEMORY_ID_ALLOWED_PRINCIPALS: u8 = 0; | ||
|
||
lazy_static! { | ||
static ref API_BOUNDARY_NODES_LISTER: Box<dyn List> = { | ||
let cid = Principal::from(REGISTRY_CANISTER_ID); | ||
|
||
let v = Client::new(cid); | ||
Box::new(v) | ||
}; | ||
} | ||
|
||
thread_local! { | ||
static ALLOWED_PRINCIPALS: RefCell<StableSet<Principal>> = RefCell::new( | ||
StableSet::init( | ||
MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(MEMORY_ID_ALLOWED_PRINCIPALS))), | ||
) | ||
); | ||
} | ||
|
||
// Timers | ||
|
||
fn timers() { | ||
// ACLs | ||
set_timer_interval(Duration::from_secs(10), || { | ||
// Switch to async | ||
spawn(async { | ||
// List registry entries | ||
let ids = match API_BOUNDARY_NODES_LISTER.list().await { | ||
Ok(ids) => ids, | ||
|
||
// Abort on failure | ||
Err(_) => return, | ||
}; | ||
|
||
// Update allowed principals | ||
ALLOWED_PRINCIPALS.with(|ps| { | ||
let mut ps = ps.borrow_mut(); | ||
|
||
// Clear allowed principals | ||
ps.clear_new(); | ||
|
||
ids.iter().for_each(|p| { | ||
ps.insert(p.to_owned(), ()); | ||
}); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
// Service | ||
|
||
#[allow(dead_code)] | ||
fn main() {} | ||
|
||
#[ic_cdk::init] | ||
fn init(_arg: InitArg) { | ||
// Self-authorize | ||
ALLOWED_PRINCIPALS.with(|m| { | ||
m.borrow_mut().insert( | ||
id(), // canister id | ||
(), // unit | ||
) | ||
}); | ||
|
||
// Start timers | ||
timers(); | ||
} | ||
|
||
#[ic_cdk::post_upgrade] | ||
fn post_upgrade() { | ||
// Start timers | ||
timers(); | ||
} | ||
|
||
#[ic_cdk::update] | ||
fn register(_pubkey: Vec<u8>) -> RegisterResponse { | ||
unimplemented!() | ||
} | ||
|
||
#[ic_cdk::query] | ||
fn query() -> QueryResponse { | ||
unimplemented!() | ||
} | ||
|
||
#[ic_cdk::update] | ||
fn submit(_vs: Vec<ifc::Pair>) -> SubmitResponse { | ||
unimplemented!() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use anyhow::anyhow; | ||
use async_trait::async_trait; | ||
use candid::{CandidType, Principal}; | ||
use serde::Deserialize; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum ListError { | ||
#[error(transparent)] | ||
UnexpectedError(#[from] anyhow::Error), | ||
} | ||
|
||
#[async_trait] | ||
pub trait List: Sync + Send { | ||
async fn list(&self) -> Result<Vec<Principal>, ListError>; | ||
} | ||
|
||
pub struct Client { | ||
cid: Principal, | ||
} | ||
|
||
impl Client { | ||
pub fn new(cid: Principal) -> Self { | ||
Self { cid } | ||
} | ||
} | ||
|
||
#[derive(CandidType)] | ||
pub struct EmptyStruct {} | ||
|
||
#[derive(Debug, CandidType, Deserialize)] | ||
pub struct IdRecord { | ||
id: Option<Principal>, | ||
} | ||
|
||
type CallResult<T> = (Result<T, String>,); | ||
|
||
#[async_trait] | ||
impl List for Client { | ||
async fn list(&self) -> Result<Vec<Principal>, ListError> { | ||
// Fetch IDs | ||
let r: CallResult<Vec<IdRecord>> = | ||
ic_cdk::call(self.cid, "get_api_boundary_node_ids", (EmptyStruct {},)) | ||
.await | ||
.map_err(|err| anyhow!("failed to call canister method: {err:?}"))?; | ||
|
||
// Flatten IDs | ||
let ids: Vec<Principal> = | ||
r.0.map_err(|err| anyhow!("canister method returned error: {err}"))? | ||
.into_iter() | ||
.filter_map(|r| r.id) | ||
.collect(); | ||
|
||
return Ok(ids); | ||
} | ||
} |