Skip to content

Commit

Permalink
improve auth handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Frando committed Jun 21, 2024
1 parent d31084e commit ee4f865
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 221 deletions.
52 changes: 33 additions & 19 deletions iroh-willow/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
sync::ReadAuthorisation,
willow::{Entry, WriteCapability},
},
session::Interests,
session::{AreaOfInterestSelector, Interests},
store::traits::{SecretStorage, SecretStoreError, Storage},
};

Expand Down Expand Up @@ -45,16 +45,28 @@ impl CapSelector {
pub fn matches(&self, cap: &McCapability) -> bool {
self.namespace_id == cap.granted_namespace().id()
&& self.user.includes(&cap.receiver().id())
&& self.area.is_included_in(&cap.granted_area())
&& self.area.matches(&cap.granted_area())
}

pub fn widest(namespace_id: NamespaceId) -> Self {
pub fn new(namespace_id: NamespaceId, user: UserSelector, area: AreaSelector) -> Self {
Self {
namespace_id,
user: UserSelector::Any,
area: AreaSelector::Widest,
user,
area,
}
}

pub fn with_user(namespace_id: NamespaceId, user_id: UserId) -> Self {
Self::new(
namespace_id,
UserSelector::Exact(user_id),
AreaSelector::Widest,
)
}

pub fn widest(namespace_id: NamespaceId) -> Self {
Self::new(namespace_id, UserSelector::Any, AreaSelector::Widest)
}
}

#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, derive_more::From, Serialize, Deserialize)]
Expand Down Expand Up @@ -82,7 +94,7 @@ pub enum AreaSelector {
}

impl AreaSelector {
pub fn is_included_in(&self, other: &Area) -> bool {
pub fn matches(&self, other: &Area) -> bool {
match self {
AreaSelector::Widest => true,
AreaSelector::Area(area) => other.includes_area(area),
Expand Down Expand Up @@ -221,21 +233,23 @@ impl<S: Storage> Auth<S> {
.collect::<HashMap<_, _>>();
Ok(out)
}
Interests::Explicit(interests) => Ok(interests),
Interests::Some(interests) => {
let mut out: HashMap<ReadAuthorisation, BTreeSet<AreaOfInterest>> = HashMap::new();
for (namespace_id, aois) in interests {
for aoi in aois {
// TODO: check if aoi is already covered before trying to cover it
let selector = CapSelector {
namespace_id,
user: UserSelector::Any,
area: AreaSelector::Area(aoi.area.clone()),
};
let cap = self.get_read_cap(&selector)?;
if let Some(cap) = cap {
let set = out.entry(cap).or_default();
set.insert(aoi);
for (cap_selector, aoi_selector) in interests {
let cap = self.get_read_cap(&cap_selector)?;
if let Some(cap) = cap {
let entry = out.entry(cap.clone()).or_default();
match aoi_selector {
AreaOfInterestSelector::Widest => {
let area = cap.read_cap().granted_area();
let aoi = AreaOfInterest::new(area);
entry.insert(aoi);
}
AreaOfInterestSelector::Exact(aois) => {
for aoi in aois {
entry.insert(aoi);
}
}
}
}
}
Expand Down
203 changes: 51 additions & 152 deletions iroh-willow/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,8 @@ mod tests {

use futures_lite::StreamExt;
use iroh_base::key::SecretKey;
use iroh_blobs::store::Store as PayloadStore;
use iroh_net::{Endpoint, NodeAddr, NodeId};
use rand::SeedableRng;
use rand_core::CryptoRngCore;
use tracing::info;

use crate::{
Expand All @@ -309,17 +307,12 @@ mod tests {
form::{AuthForm, EntryForm, PayloadForm, SubspaceForm, TimestampForm},
net::run,
proto::{
grouping::{AreaOfInterest, ThreeDRange},
keys::{
NamespaceId, NamespaceKind, NamespaceSecretKey, UserId, UserPublicKey,
UserSecretKey,
},
meadowcap::{AccessMode, McCapability, OwnedCapability},
sync::ReadCapability,
willow::{Entry, InvalidPath, Path, WriteCapability},
grouping::ThreeDRange,
keys::{NamespaceId, NamespaceKind, UserId},
meadowcap::AccessMode,
willow::{Entry, InvalidPath, Path},
},
session::{Interests, Role, SessionInit, SessionMode},
store::memory,
};

const ALPN: &[u8] = b"iroh-willow/0";
Expand Down Expand Up @@ -357,7 +350,7 @@ mod tests {

handle_betty.import_caps(cap_for_betty).await?;

insert2(
insert(
&handle_alfie,
namespace_id,
user_alfie,
Expand All @@ -368,7 +361,7 @@ mod tests {
)
.await?;

insert2(
insert(
&handle_betty,
namespace_id,
user_betty,
Expand All @@ -379,14 +372,8 @@ mod tests {
)
.await?;

let init_alfie = SessionInit {
interests: Interests::All,
mode: SessionMode::ReconcileOnce,
};
let init_betty = SessionInit {
interests: Interests::All,
mode: SessionMode::ReconcileOnce,
};
let init_alfie = SessionInit::new(Interests::All, SessionMode::ReconcileOnce);
let init_betty = SessionInit::new(Interests::All, SessionMode::ReconcileOnce);

info!("init took {:?}", start.elapsed());

Expand Down Expand Up @@ -442,35 +429,49 @@ mod tests {
let (ep_alfie, node_id_alfie, _) = create_endpoint(&mut rng).await?;
let (ep_betty, node_id_betty, addr_betty) = create_endpoint(&mut rng).await?;

let namespace_secret = NamespaceSecretKey::generate(&mut rng, NamespaceKind::Owned);
let namespace_id = namespace_secret.id();
let handle_alfie = ActorHandle::spawn_memory(Default::default(), node_id_alfie);
let handle_betty = ActorHandle::spawn_memory(Default::default(), node_id_betty);

let start = Instant::now();
let mut expected_entries = BTreeSet::new();
let user_alfie = handle_alfie.create_user().await?;
let user_betty = handle_betty.create_user().await?;

let (handle_alfie, payloads_alfie) = create_willow(node_id_alfie);
let (handle_betty, payloads_betty) = create_willow(node_id_betty);
let namespace_id = handle_alfie
.create_namespace(NamespaceKind::Owned, user_alfie)
.await?;

let (init_alfie, cap_alfie) = setup_and_insert(
SessionMode::Live,
&mut rng,
let cap_for_betty = handle_alfie
.delegate_caps(
CapSelector::widest(namespace_id),
AccessMode::Write,
DelegateTo::new(user_betty, None),
)
.await?;

handle_betty.import_caps(cap_for_betty).await?;

let mut expected_entries = BTreeSet::new();
let start = Instant::now();

let n_init = 2;
insert(
&handle_alfie,
&payloads_alfie,
&namespace_secret,
2,
namespace_id,
user_alfie,
n_init,
|n| Path::new(&[b"alfie-init", n.to_string().as_bytes()]),
|n| format!("alfie{n}"),
&mut expected_entries,
|n| Path::new(&[b"alfie", n.to_string().as_bytes()]),
)
.await?;
let (init_betty, _cap_betty) = setup_and_insert(
SessionMode::Live,
&mut rng,

insert(
&handle_betty,
&payloads_betty,
&namespace_secret,
2,
namespace_id,
user_betty,
n_init,
|n| Path::new(&[b"betty-init", n.to_string().as_bytes()]),
|n| format!("betty{n}"),
&mut expected_entries,
|n| Path::new(&[b"betty", n.to_string().as_bytes()]),
)
.await?;

Expand All @@ -488,23 +489,21 @@ mod tests {

// alfie insert 3 enries after waiting a second
let _insert_task_alfie = tokio::task::spawn({
let store = handle_alfie.clone();
let payload_store = payloads_alfie.clone();
let handle_alfie = handle_alfie.clone();
let count = 3;
let content_fn = |i: usize| format!("alfie live insert {i} for alfie");
let content_fn = |i: usize| format!("alfie live {i}");
let path_fn = |i: usize| Path::new(&[b"alfie-live", i.to_string().as_bytes()]);
let mut track_entries = vec![];

async move {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
insert(
&store,
&payload_store,
&handle_alfie,
namespace_id,
cap_alfie,
user_alfie,
count,
content_fn,
path_fn,
content_fn,
&mut track_entries,
)
.await
Expand All @@ -513,6 +512,9 @@ mod tests {
}
});

let init_alfie = SessionInit::new(Interests::All, SessionMode::Live);
let init_betty = SessionInit::new(Interests::All, SessionMode::Live);

let (session_alfie, session_betty) = tokio::join!(
run(
node_id_alfie,
Expand Down Expand Up @@ -567,13 +569,6 @@ mod tests {
Ok((ep, node_id, addr))
}

pub fn create_willow(me: NodeId) -> (ActorHandle, iroh_blobs::store::mem::Store) {
let payloads = iroh_blobs::store::mem::Store::default();
let payloads_clone = payloads.clone();
let handle = ActorHandle::spawn(move || memory::Store::new(payloads_clone), me);
(handle, payloads)
}

async fn get_entries(
store: &ActorHandle,
namespace: NamespaceId,
Expand All @@ -586,7 +581,7 @@ mod tests {
entries
}

async fn insert2(
async fn insert(
handle: &ActorHandle,
namespace_id: NamespaceId,
user_id: UserId,
Expand All @@ -612,102 +607,6 @@ mod tests {
Ok(())
}

#[allow(clippy::too_many_arguments)]
async fn insert<P: PayloadStore>(
actor: &ActorHandle,
payload_store: &P,
namespace_id: NamespaceId,
write_cap: WriteCapability,
count: usize,
content_fn: impl Fn(usize) -> String,
path_fn: impl Fn(usize) -> Result<Path, InvalidPath>,
track_entries: &mut impl Extend<Entry>,
) -> anyhow::Result<()> {
for i in 0..count {
let payload = content_fn(i).as_bytes().to_vec();
let payload_len = payload.len() as u64;
let temp_tag = payload_store
.import_bytes(payload.into(), iroh_base::hash::BlobFormat::Raw)
.await?;
let payload_digest = *temp_tag.hash();
let path = path_fn(i).expect("invalid path");
let entry = Entry::new_current(
namespace_id,
write_cap.receiver().id(),
path,
payload_digest,
payload_len,
);
track_entries.extend([entry.clone()]);
actor.insert_entry(entry, write_cap.clone()).await?;
drop(temp_tag);
}
Ok(())
}

#[allow(clippy::too_many_arguments)]
async fn setup_and_insert<P: PayloadStore>(
mode: SessionMode,
rng: &mut impl CryptoRngCore,
store: &ActorHandle,
payload_store: &P,
namespace_secret: &NamespaceSecretKey,
count: usize,
track_entries: &mut impl Extend<Entry>,
path_fn: impl Fn(usize) -> Result<Path, InvalidPath>,
) -> anyhow::Result<(SessionInit, WriteCapability)> {
let (read_cap, write_cap) = setup_capabilities(rng, store, namespace_secret).await?;
let content_fn = |i| {
format!(
"initial entry {i} for {}",
write_cap.receiver().id().fmt_short()
)
};
insert(
store,
payload_store,
namespace_secret.id(),
write_cap.clone(),
count,
content_fn,
path_fn,
track_entries,
)
.await?;
let init =
SessionInit::with_explicit_interest(mode, read_cap.into(), AreaOfInterest::full());
Ok((init, write_cap))
}

async fn setup_capabilities(
rng: &mut impl CryptoRngCore,
store: &ActorHandle,
namespace_secret: &NamespaceSecretKey,
) -> anyhow::Result<(ReadCapability, WriteCapability)> {
let user_secret = UserSecretKey::generate(rng);
let user_public_key = user_secret.public_key();
store.insert_secret(user_secret.clone()).await?;
let (read_cap, write_cap) = create_capabilities(namespace_secret, user_public_key);
Ok((read_cap, write_cap))
}

fn create_capabilities(
namespace_secret: &NamespaceSecretKey,
user_public_key: UserPublicKey,
) -> (ReadCapability, WriteCapability) {
let read_capability = McCapability::Owned(OwnedCapability::new(
namespace_secret,
user_public_key,
AccessMode::Read,
));
let write_capability = McCapability::Owned(OwnedCapability::new(
namespace_secret,
user_public_key,
AccessMode::Write,
));
(read_capability, write_capability)
}

fn parse_env_var<T>(var: &str, default: T) -> T
where
T: std::str::FromStr,
Expand Down
Loading

0 comments on commit ee4f865

Please sign in to comment.