-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: Update capabilites in line with Ucan 0.9.0/0.10.0. Fixes #22.
* Represents capabilities as map-of-maps rather than array of tuples. * Validates caveats in proof chain * Renames 'att' to 'cap' (Ucan spec 0.10.0). * Renames various capability semantics structs with spec names (With=>Resource, Can/Action=>Ability, Resource=>ResourceURI) * Renames 'Capability' to 'CapabilityView'.
- Loading branch information
Showing
21 changed files
with
889 additions
and
290 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -0,0 +1,172 @@ | ||
use std::ops::Deref; | ||
|
||
use anyhow::{anyhow, Error, Result}; | ||
use serde_json::{Map, Value}; | ||
|
||
#[derive(Clone)] | ||
pub struct Caveat(Map<String, Value>); | ||
|
||
impl Caveat { | ||
/// Determines if this [Caveat] enables/allows the provided caveat. | ||
/// | ||
/// ``` | ||
/// use ucan::capability::{Caveat}; | ||
/// use serde_json::json; | ||
/// | ||
/// let no_caveat = Caveat::try_from(json!({})).unwrap(); | ||
/// let x_caveat = Caveat::try_from(json!({ "x": true })).unwrap(); | ||
/// let x_diff_caveat = Caveat::try_from(json!({ "x": false })).unwrap(); | ||
/// let y_caveat = Caveat::try_from(json!({ "y": true })).unwrap(); | ||
/// let xz_caveat = Caveat::try_from(json!({ "x": true, "z": true })).unwrap(); | ||
/// | ||
/// assert!(no_caveat.enables(&no_caveat)); | ||
/// assert!(x_caveat.enables(&x_caveat)); | ||
/// assert!(no_caveat.enables(&x_caveat)); | ||
/// assert!(x_caveat.enables(&xz_caveat)); | ||
/// | ||
/// assert!(!x_caveat.enables(&x_diff_caveat)); | ||
/// assert!(!x_caveat.enables(&no_caveat)); | ||
/// assert!(!x_caveat.enables(&y_caveat)); | ||
/// ``` | ||
pub fn enables(&self, other: &Caveat) -> bool { | ||
if self.is_empty() { | ||
return true; | ||
} | ||
|
||
if other.is_empty() { | ||
return false; | ||
} | ||
|
||
if self == other { | ||
return true; | ||
} | ||
|
||
for (key, value) in self.iter() { | ||
if let Some(other_value) = other.get(key) { | ||
if value != other_value { | ||
return false; | ||
} | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
true | ||
} | ||
} | ||
|
||
impl Deref for Caveat { | ||
type Target = Map<String, Value>; | ||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl PartialEq for Caveat { | ||
fn eq(&self, other: &Caveat) -> bool { | ||
self.0 == other.0 | ||
} | ||
} | ||
|
||
impl TryFrom<Value> for Caveat { | ||
type Error = Error; | ||
fn try_from(value: Value) -> Result<Caveat> { | ||
Ok(Caveat(match value { | ||
Value::Object(obj) => obj, | ||
_ => return Err(anyhow!("Caveat must be an object")), | ||
})) | ||
} | ||
} | ||
|
||
impl TryFrom<&Value> for Caveat { | ||
type Error = Error; | ||
fn try_from(value: &Value) -> Result<Caveat> { | ||
Caveat::try_from(value.to_owned()) | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct Caveats(Vec<Caveat>); | ||
|
||
impl Caveats { | ||
/// Determines if these [Caveats] enables/allows the provided caveats. | ||
/// | ||
/// ``` | ||
/// use ucan::capability::{Caveat, Caveats}; | ||
/// use serde_json::json; | ||
/// | ||
/// let no_caveat = json!({}); | ||
/// let x_caveat = json!({ "x": true }); | ||
/// let y_caveat = json!({ "y": true }); | ||
/// let z_caveat = json!({ "z": true }); | ||
/// let yz_caveat = json!({ "y": true, "z": true }); | ||
/// | ||
/// let no_caveats = Caveats::from(vec![no_caveat.clone()]); | ||
/// let x_caveats = Caveats::from(vec![x_caveat.clone()]); | ||
/// let y_caveats = Caveats::from(vec![y_caveat.clone()]); | ||
/// let x_y_caveats = Caveats::from(vec![x_caveat.clone(), y_caveat.clone()]); | ||
/// let x_yz_caveats = Caveats::from(vec![x_caveat.clone(), yz_caveat.clone()]); | ||
/// let x_y_z_caveats = Caveats::from(vec![x_caveat.clone(), y_caveat.clone(), z_caveat.clone()]); | ||
/// | ||
/// assert!(no_caveats.enables(&no_caveats)); | ||
/// assert!(x_caveats.enables(&x_caveats)); | ||
/// assert!(no_caveats.enables(&x_caveats)); | ||
/// assert!(x_y_caveats.enables(&x_caveats)); // Removes capability | ||
/// assert!(x_y_caveats.enables(&x_yz_caveats)); // Attenuates existing caveat | ||
/// | ||
/// assert!(!x_caveats.enables(&no_caveats)); | ||
/// assert!(!x_caveats.enables(&y_caveats)); | ||
/// assert!(!x_y_caveats.enables(&x_y_z_caveats)); | ||
/// ``` | ||
pub fn enables(&self, other: &Caveats) -> bool { | ||
if self == other { | ||
return true; | ||
} | ||
|
||
for other_caveat in other.iter() { | ||
// Each delegated caveat must be enabled by some proof caveat | ||
let mut enabled = false; | ||
for proof_caveat in self.iter() { | ||
if proof_caveat.enables(other_caveat) { | ||
enabled = true; | ||
break; | ||
} | ||
} | ||
if !enabled { | ||
return false; | ||
} | ||
} | ||
true | ||
} | ||
} | ||
|
||
impl From<Vec<Value>> for Caveats { | ||
fn from(value: Vec<Value>) -> Caveats { | ||
Caveats( | ||
value | ||
.into_iter() | ||
.filter_map(|c| Caveat::try_from(c).ok()) | ||
.collect(), | ||
) | ||
} | ||
} | ||
|
||
impl From<&Vec<Value>> for Caveats { | ||
fn from(value: &Vec<Value>) -> Caveats { | ||
Caveats::from(value.to_owned()) | ||
} | ||
} | ||
|
||
impl Deref for Caveats { | ||
type Target = Vec<Caveat>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl PartialEq for Caveats { | ||
fn eq(&self, other: &Caveats) -> bool { | ||
self.0 == other.0 | ||
} | ||
} |
Oops, something went wrong.