Browse Source

Added a field to Path containing its associated principal.

Matthew Carr 2 years ago
parent
commit
d54fa2bb0c

+ 5 - 6
crates/node/src/crypto.rs

@@ -62,8 +62,8 @@ pub enum Hash {
 }
 
 impl Hash {
-    /// The character that's used to separate a hash type from its value in the leading component
-    /// of a path.
+    /// The character that's used to separate a hash type from its value in its string
+    /// representation.
     const HASH_SEP: char = ':';
 }
 
@@ -96,8 +96,8 @@ impl AsMut<[u8]> for Hash {
 
 impl TryFrom<&str> for Hash {
     type Error = Error;
-    fn try_from(leading: &str) -> Result<Hash> {
-        let mut split: Vec<&str> = leading.split(Self::HASH_SEP).collect();
+    fn try_from(string: &str) -> Result<Hash> {
+        let mut split: Vec<&str> = string.split(Self::HASH_SEP).collect();
         if split.len() != 2 {
             return Err(Error::InvalidFormat)
         };
@@ -693,8 +693,7 @@ mod tests {
         let key = Key::generate(KeyId::Rsa)?;
         let write_cap = WriteCap {
             issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
-            path: Path::try_from("contacts/emergency")
-                .map_err(|err| Error::Message(err.to_string()))?,
+            path: make_path(vec!["contacts", "emergency"]),
             expires: Epoch(1649904316),
             signing_key: key,
             signature: Signature::Rsa(SIGNATURE),

+ 40 - 17
crates/node/src/main.rs

@@ -137,7 +137,7 @@ struct Principal(Hash);
 
 /// An identifier for a block in a tree.
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-struct Path(Vec<String>);
+struct Path(Vec<String>, Principal);
 
 impl Path {
     /// The character that is used to separate path components.
@@ -220,7 +220,25 @@ impl<'s> TryFrom<&'s str> for Path {
         if string.len() - 1 == last_end {
             components.push("".to_string());
         }
-        Ok(Path(components))
+        let leading = components.get(0).ok_or(PathError::InvalidLeadingComponent)?;
+        let hash = Hash::try_from(leading.as_str())
+            .map_err(|_| PathError::InvalidLeadingComponent)?;
+        Ok(Path(components, Principal(hash)))
+    }
+}
+
+impl Display for Path {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.0.is_empty() {
+            return write!(f, "{}", Path::SEP);
+        };
+        let mut iter = self.0.iter();
+        let first = iter.next().unwrap();
+        let mut output = write!(f, "{}{}", Path::SEP, first);
+        for component in iter {
+            output = write!(f, "{}{}", Path::SEP, component)
+        }
+        output
     }
 }
 
@@ -268,6 +286,7 @@ fn main() {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use test_helpers::*;
 
     fn path_from_str_test_case(
         expected: Result<Path, PathError>, input: &str
@@ -279,48 +298,52 @@ mod tests {
 
     #[test]
     fn path_from_str_multiple_components_ok() -> Result<(), PathError> {
-        let expected = Path(vec!["red".to_string(), "green".to_string(), "blue".to_string()]);
-        path_from_str_test_case(Ok(expected), "red/green/blue")?;
+        let expected = make_path(vec!["red", "green", "blue"]);
+        let input = format!("{}/red/green/blue", expected.1.0);
+        path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
 
     #[test]
     fn path_from_str_one_component_ok() -> Result<(), PathError> {
-        let expected = Path(vec!["red".to_string()]);
-        path_from_str_test_case(Ok(expected), "red")?;
+        let expected = make_path(vec![]);
+        let input = expected.1.0.to_string();
+        path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
 
     #[test]
     fn path_from_str_leading_slash_ok() -> Result<(), PathError> {
-        let expected = Path(vec![
-            "orange".to_string(), "banana".to_string(), "shotgun".to_string()
-        ]);
-        path_from_str_test_case(Ok(expected), "/orange/banana/shotgun")?;
+        let expected = make_path(vec!["orange", "banana", "shotgun"]);
+        let principal = expected.1.clone();
+        path_from_str_test_case(
+            Ok(expected), format!("/{}/orange/banana/shotgun", principal.0).as_str())?;
         Ok(())
     }
 
     #[test]
     fn path_from_str_trailing_slash_ok() -> Result<(), PathError> {
-        let expected = Path(vec![
-            "orange".to_string(), "banana".to_string(), "shotgun".to_string(), "".to_string()
-        ]);
-        path_from_str_test_case(Ok(expected), "/orange/banana/shotgun/")?;
+        let expected = make_path(vec!["orange", "banana", "shotgun", ""]);
+        let principal = expected.1.clone();
+        path_from_str_test_case(
+            Ok(expected), format!("{}/orange/banana/shotgun/", principal.0).as_str())?;
         Ok(())
     }
 
     #[test]
     fn path_from_str_path_too_long_fail() -> Result<(), PathError> {
-        let expected = Err(PathError::PathTooLong(4097));
-        let input = "*".repeat(4097);
+        let principal = make_principal();
+        let input = format!("{}/{}", principal.0, "*".repeat(4097));
+        let expected = Err(PathError::PathTooLong(input.len()));
         path_from_str_test_case(expected, input.as_str())?;
         Ok(())
     }
 
     #[test]
     fn path_from_str_multiple_slashes_fail() -> Result<(), PathError> {
+        let principal = make_principal();
         let expected = Err(PathError::EmptyComponent);
-        path_from_str_test_case(expected, "//orange")?;
+        path_from_str_test_case(expected, format!("{}//orange", principal.0).as_str())?;
         Ok(())
     }
 }

+ 2 - 1
crates/node/src/serde_tests.rs

@@ -34,7 +34,8 @@ fn roundtrip_directory() -> Result<()> {
 #[test]
 fn roundtrip_fragment() -> Result<()> {
     let body = Vec::from(PAYLOAD);
-    let expected = Fragment::new("apps/bodhi", 42, body)
+    let path = make_path(vec!["apps", "bohdi"]);
+    let expected = Fragment::new(path.to_string().as_str(), 42, body)
         .map_err(|err| Error::Message(err.to_string()))?;
     let ser_result = to_vec(&expected);
     let de_result = from_vec(&ser_result?);

+ 14 - 4
crates/node/src/test_helpers.rs

@@ -2,7 +2,7 @@
 
 use super::*;
 use crypto::RsaPadding;
-use serde_block_tree::{Error, Result};
+use serde_block_tree::Result;
 
 pub const PRINCIPAL: [u8; 32] = [
     0x75, 0x28, 0xA9, 0xE0, 0x9D, 0x24, 0xBA, 0xB3, 0x79, 0x56, 0x15, 0x68, 0xFD, 0xA4, 0xE2, 0xA4,
@@ -122,10 +122,20 @@ pub(crate) fn make_principal() -> Principal {
     Principal(Hash::Sha2_256(PRINCIPAL))
 }
 
+pub(crate) fn make_path(rel_components: Vec<&str>) -> Path {
+    let principal = make_principal();
+    let mut components = Vec::with_capacity(rel_components.len() + 1);
+    components.push(principal.0.to_string());
+    for component in rel_components {
+        components.push(component.to_string());
+    }
+    Path(components, principal)
+}
+
 pub(crate) fn make_write_cap() -> Result<WriteCap> {
     Ok(WriteCap {
         issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
-        path: Path::try_from("contacts/emergency").map_err(|err| Error::Message(err.to_string()))?,
+        path: make_path(vec!["contacts", "emergency"]),
         expires: Epoch(1649904316),
         signing_key: Key::Rsa {
             public: Vec::from(RSA_KEY), private: None, padding: RsaPadding::Pkcs1
@@ -133,7 +143,7 @@ pub(crate) fn make_write_cap() -> Result<WriteCap> {
         signature: Signature::Rsa(SIGNATURE),
         next: Some(Box::from(WriteCap {
             issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
-            path: Path::try_from("contacts").map_err(|err| Error::Message(err.to_string()))?,
+            path: make_path(vec!["contacts"]),
             expires: Epoch(1649994316),
             signing_key: Key::Rsa {
                 public: Vec::from(RSA_KEY), private: None, padding: RsaPadding::Pkcs1
@@ -158,7 +168,7 @@ pub(crate) fn make_block() -> Result<Block> {
     }
     let write_cap = make_write_cap()?;
     Ok(Block {
-        path: Path::try_from("apps/verse").map_err(|err| Error::Message(err.to_string()))?,
+        path: make_path(vec!["apps", "verse"]),
         read_caps,
         write_cap,
         body: Cryptotext::Plain(Vec::from(PAYLOAD)),