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
42 changes: 21 additions & 21 deletions tinycloud-core/src/encryption_network/network_id.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Parsing for `urn:tinycloud:encryption:<principal>:<network>` network identifiers.
//! Parsing for `urn:tinycloud:encryption:<ownerDid>:<network>` network identifiers.
//!
//! The principal is the root authority for the network. The network name disambiguates
//! multiple networks owned by the same principal.
//! The owner DID is the root authority for the network. The network name disambiguates
//! multiple networks owned by the same owner.

use serde::{Deserialize, Serialize};
use std::fmt;
Expand All @@ -14,11 +14,11 @@ const NETWORK_ID_PREFIX: &str = "urn:tinycloud:encryption:";
pub enum NetworkIdError {
#[error("missing urn:tinycloud:encryption: prefix")]
MissingPrefix,
#[error("empty principal")]
EmptyPrincipal,
#[error("empty owner DID")]
EmptyOwnerDid,
#[error("empty network name")]
EmptyName,
#[error("missing principal/name separator")]
#[error("missing owner DID/name separator")]
MissingSeparator,
#[error("network name may not contain ':' or '/'")]
InvalidName,
Expand All @@ -28,31 +28,31 @@ pub enum NetworkIdError {
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct NetworkId {
principal: String,
owner_did: String,
name: String,
}

impl NetworkId {
pub fn new(
principal: impl Into<String>,
owner_did: impl Into<String>,
name: impl Into<String>,
) -> Result<Self, NetworkIdError> {
let principal = principal.into();
let owner_did = owner_did.into();
let name = name.into();
if principal.is_empty() {
return Err(NetworkIdError::EmptyPrincipal);
if owner_did.is_empty() {
return Err(NetworkIdError::EmptyOwnerDid);
}
if name.is_empty() {
return Err(NetworkIdError::EmptyName);
}
if name.contains(':') || name.contains('/') {
return Err(NetworkIdError::InvalidName);
}
Ok(Self { principal, name })
Ok(Self { owner_did, name })
}

pub fn principal(&self) -> &str {
&self.principal
pub fn owner_did(&self) -> &str {
&self.owner_did
}

pub fn name(&self) -> &str {
Expand All @@ -62,7 +62,7 @@ impl NetworkId {

impl fmt::Display for NetworkId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{NETWORK_ID_PREFIX}{}:{}", self.principal, self.name)
write!(f, "{NETWORK_ID_PREFIX}{}:{}", self.owner_did, self.name)
}
}

Expand All @@ -79,13 +79,13 @@ impl FromStr for NetworkId {
let rest = s
.strip_prefix(NETWORK_ID_PREFIX)
.ok_or(NetworkIdError::MissingPrefix)?;
// The principal itself is a DID-like value that may contain colons
// The owner DID itself may contain colons
// (e.g. did:key:z6Mk...). The network name is the final colon-delimited
// segment, which is constrained to contain no further ':' or '/'.
let (principal, name) = rest
let (owner_did, name) = rest
.rsplit_once(':')
.ok_or(NetworkIdError::MissingSeparator)?;
Self::new(principal.to_string(), name.to_string())
Self::new(owner_did.to_string(), name.to_string())
}
}

Expand All @@ -112,7 +112,7 @@ mod tests {
let id: NetworkId = "urn:tinycloud:encryption:did:key:z6MkExampleAbcd:default"
.parse()
.unwrap();
assert_eq!(id.principal(), "did:key:z6MkExampleAbcd");
assert_eq!(id.owner_did(), "did:key:z6MkExampleAbcd");
assert_eq!(id.name(), "default");
assert_eq!(
id.to_string(),
Expand All @@ -133,9 +133,9 @@ mod tests {
}

#[test]
fn rejects_empty_principal_with_explicit_name() {
fn rejects_empty_owner_did_with_explicit_name() {
let err: Result<NetworkId, _> = "urn:tinycloud:encryption::default".parse();
assert_eq!(err.unwrap_err(), NetworkIdError::EmptyPrincipal);
assert_eq!(err.unwrap_err(), NetworkIdError::EmptyOwnerDid);
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions tinycloud-core/src/encryption_network/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub struct DecryptResponseBody {
///
/// `issuer` is the requester session DID. `audience` MUST equal the serving
/// node's DID. `proof_cid` references the delegation chain that authorizes the
/// decrypt action; verification rooted in the principal embedded in
/// decrypt action; verification rooted in the owner DID embedded in
/// `network_id` is performed by [`crate::encryption_network::service`].
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecryptInvocation {
Expand Down Expand Up @@ -207,7 +207,7 @@ mod tests {
use serde_json::json;

fn sample_invocation() -> DecryptInvocation {
let net: NetworkId = "urn:tinycloud:encryption:did:key:z6MkPrincipal:default"
let net: NetworkId = "urn:tinycloud:encryption:did:key:z6MkOwner:default"
.parse()
.unwrap();
DecryptInvocation {
Expand Down
32 changes: 16 additions & 16 deletions tinycloud-core/src/encryption_network/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ pub enum EncryptionServiceError {
AudienceMismatch,
#[error("invocation target node does not match this node")]
TargetNodeMismatch,
#[error("invocation root principal does not match network principal")]
PrincipalMismatch,
#[error("invocation root owner DID does not match network owner DID")]
OwnerMismatch,
#[error("invocation references a different network")]
NetworkMismatch,
#[error("invocation expired")]
Expand Down Expand Up @@ -97,7 +97,7 @@ pub struct EncryptionService {
#[derive(Debug, Clone)]
pub struct CreateNetworkRequest {
pub name: String,
pub principal: String,
pub owner_did: String,
pub threshold: Threshold,
}

Expand Down Expand Up @@ -147,7 +147,7 @@ impl EncryptionService {
&self,
req: CreateNetworkRequest,
) -> Result<NetworkDescriptor, EncryptionServiceError> {
let network_id = NetworkId::new(req.principal.clone(), req.name.clone())
let network_id = NetworkId::new(req.owner_did.clone(), req.name.clone())
.map_err(|e| EncryptionServiceError::InvalidBody(format!("invalid network id: {e}")))?;

// Reject duplicates up-front so callers see a clear error instead of a
Expand Down Expand Up @@ -181,7 +181,7 @@ impl EncryptionService {

let model = encryption_network::ActiveModel {
network_id: Set(network_id.to_string()),
principal: Set(req.principal.clone()),
owner_did: Set(req.owner_did.clone()),
name: Set(req.name.clone()),
alg: Set(generated.alg.clone()),
key_version: Set(1),
Expand Down Expand Up @@ -219,7 +219,7 @@ impl EncryptionService {

Ok(NetworkDescriptor {
network_id,
principal: req.principal,
owner_did: req.owner_did,
name: req.name,
members: vec![NetworkMemberDescriptor {
node_id: self.node_did.clone(),
Expand Down Expand Up @@ -264,7 +264,7 @@ impl EncryptionService {

Ok(NetworkDescriptor {
network_id: network_id.clone(),
principal: model.principal,
owner_did: model.owner_did,
name: model.name,
members,
threshold: Threshold {
Expand All @@ -284,11 +284,11 @@ impl EncryptionService {
pub async fn get_network_by_name(
&self,
name: &str,
principal: Option<&str>,
owner_did: Option<&str>,
) -> Result<NetworkDescriptor, EncryptionServiceError> {
if let Some(principal) = principal {
if let Some(owner_did) = owner_did {
let network_id =
NetworkId::new(principal.to_string(), name.to_string()).map_err(|e| {
NetworkId::new(owner_did.to_string(), name.to_string()).map_err(|e| {
EncryptionServiceError::InvalidBody(format!("invalid network id: {e}"))
})?;
return self.get_network(&network_id).await;
Expand Down Expand Up @@ -352,8 +352,8 @@ impl EncryptionService {
if cap.with != network_id.to_string() {
return Err(EncryptionServiceError::NetworkMismatch);
}
if invocation.issuer != network_id.principal() {
return Err(EncryptionServiceError::PrincipalMismatch);
if invocation.issuer != network_id.owner_did() {
return Err(EncryptionServiceError::OwnerMismatch);
}
let expected_body_hash = canonical_hash(body_value);
if expected_body_hash != invocation.facts.body_hash {
Expand Down Expand Up @@ -460,8 +460,8 @@ impl EncryptionService {
if cap.with != network_id.to_string() {
return Err(EncryptionServiceError::NetworkMismatch);
}
if invocation.issuer != network_id.principal() {
return Err(EncryptionServiceError::PrincipalMismatch);
if invocation.issuer != network_id.owner_did() {
return Err(EncryptionServiceError::OwnerMismatch);
}

// ---- Network state ----
Expand Down Expand Up @@ -964,7 +964,7 @@ fn native_invocation_cid(invocation: &InvocationInfo) -> Result<String, Encrypti
#[serde(rename_all = "camelCase")]
pub struct WellKnownRecord {
pub network_id: String,
pub principal: String,
pub owner_did: String,
pub name: String,
pub alg: String,
pub key_version: i64,
Expand All @@ -978,7 +978,7 @@ impl From<&NetworkDescriptor> for WellKnownRecord {
fn from(d: &NetworkDescriptor) -> Self {
Self {
network_id: d.network_id.to_string(),
principal: d.principal.clone(),
owner_did: d.owner_did.clone(),
name: d.name.clone(),
alg: d.alg.clone(),
key_version: d.key_version,
Expand Down
Loading
Loading