use serde::{Deserialize, Serialize};
use crate::error::{AccessError, AccessResult};
use crate::identity::{Did, Identity};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Scope {
Collection(String),
Document { collection: String, doc_id: String },
Function(String),
}
impl Scope {
pub fn covers(&self, requested: &Scope) -> bool {
match (self, requested) {
(Scope::Collection(c), Scope::Collection(rc)) => c == rc,
(Scope::Collection(c), Scope::Document { collection, .. }) => c == collection,
(
Scope::Document { collection, doc_id },
Scope::Document {
collection: rc,
doc_id: rd,
},
) => collection == rc && doc_id == rd,
(Scope::Function(f), Scope::Function(rf)) => f == rf,
_ => false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Ops(u8);
impl Ops {
pub const NONE: Ops = Ops(0);
pub const READ: Ops = Ops(1);
pub const WRITE: Ops = Ops(2);
pub const COMPUTE: Ops = Ops(4);
pub fn contains(self, needed: Ops) -> bool {
needed.0 != 0 && (self.0 & needed.0) == needed.0
}
pub fn is_empty(self) -> bool {
self.0 == 0
}
pub fn is_subset_of(self, other: Ops) -> bool {
(self.0 & other.0) == self.0
}
}
impl std::ops::BitOr for Ops {
type Output = Ops;
fn bitor(self, rhs: Ops) -> Ops {
Ops(self.0 | rhs.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Capability {
pub id: [u8; 16],
pub issuer: Did,
pub bearer: Did,
pub scope: Scope,
pub ops: Ops,
pub expiry: Option<u64>,
pub budget_micro_mata: Option<u64>,
pub delegation_depth: u8,
}
impl Capability {
pub fn grant(
issuer: impl Into<Did>,
bearer: impl Into<Did>,
scope: Scope,
ops: Ops,
) -> AccessResult<Self> {
let mut id = [0u8; 16];
getrandom::fill(&mut id).map_err(|e| AccessError::KeyGen(e.to_string()))?;
Ok(Self {
id,
issuer: issuer.into(),
bearer: bearer.into(),
scope,
ops,
expiry: None,
budget_micro_mata: None,
delegation_depth: 0,
})
}
pub fn with_expiry(mut self, unix_seconds: u64) -> Self {
self.expiry = Some(unix_seconds);
self
}
pub fn with_budget(mut self, micro_mata: u64) -> Self {
self.budget_micro_mata = Some(micro_mata);
self
}
pub fn with_delegation_depth(mut self, depth: u8) -> Self {
self.delegation_depth = depth;
self
}
pub(crate) fn canonical_bytes(&self) -> AccessResult<Vec<u8>> {
postcard::to_allocvec(self).map_err(|e| AccessError::Canonical(e.to_string()))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignedCapability {
pub capability: Capability,
pub issuer_signature: Vec<u8>,
}
impl SignedCapability {
pub fn sign(capability: Capability, issuer: &Identity) -> AccessResult<Self> {
let bytes = capability.canonical_bytes()?;
let issuer_signature = issuer.sign(&bytes);
Ok(Self {
capability,
issuer_signature,
})
}
pub fn encode(&self) -> AccessResult<Vec<u8>> {
postcard::to_allocvec(self).map_err(|e| AccessError::Canonical(e.to_string()))
}
pub fn decode(bytes: &[u8]) -> AccessResult<Self> {
postcard::from_bytes(bytes).map_err(|e| AccessError::Canonical(e.to_string()))
}
}