use core::fmt;
use alloc::vec::Vec;
#[cfg(feature = "dev")]
use arbitrary::Arbitrary;
use derive_more::{Display, Error};
use signature::{Keypair, Signer, Verifier};
use ufotofu::codec_prelude::*;
use crate::prelude::*;
#[derive(Clone, PartialEq, Eq)]
pub struct AuthorisationToken {
capability: WriteCapability,
signature: SubspaceSignature,
}
impl fmt::Debug for AuthorisationToken {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("McAuthorisationToken")
.field("capability", &self.capability)
.field("signature", &self.signature)
.finish()
}
}
#[cfg(feature = "dev")]
impl<'a> Arbitrary<'a> for AuthorisationToken {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self::new(
Arbitrary::arbitrary(u)?,
Arbitrary::arbitrary(u)?,
))
}
}
impl AuthorisationToken {
pub fn new(capability: WriteCapability, signature: SubspaceSignature) -> Self {
Self {
capability,
signature,
}
}
pub fn capability(&self) -> &WriteCapability {
&self.capability
}
pub fn signature(&self) -> &SubspaceSignature {
&self.signature
}
pub fn into_parts(self) -> (WriteCapability, SubspaceSignature) {
(self.capability, self.signature)
}
pub fn new_for_entry<E>(
entry: &E,
write_capability: &WriteCapability,
secret: &SubspaceSecret,
) -> Result<Self, DoesNotAuthorise>
where
E: EntrylikeExt + ?Sized,
{
if &secret.verifying_key() == write_capability.receiver()
&& write_capability.includes(entry)
{
let mut encoded_entry = Vec::with_capacity(entry.length_of_entry_encoding());
pollster::block_on(async {
entry
.encode_entry(&mut (&mut encoded_entry).into_consumer())
.await
.unwrap();
});
let signature = secret.sign(&encoded_entry);
Ok(Self::new(write_capability.clone(), signature))
} else {
Err(DoesNotAuthorise)
}
}
pub fn does_authorise<E>(&self, entry: &E) -> bool
where
E: EntrylikeExt + ?Sized,
{
if self.capability.includes(entry) {
let mut encoded_entry = Vec::with_capacity(entry.length_of_entry_encoding());
pollster::block_on(async {
entry
.encode_entry(&mut (&mut encoded_entry).into_consumer())
.await
.unwrap();
});
let verifier: &SubspaceId = self.capability.receiver();
matches!(verifier.verify(&encoded_entry, &self.signature), Ok(()))
} else {
false
}
}
}
#[derive(Clone, Display, Error, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[display("entry paired with invalid `AuthorisationToken`")]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct DoesNotAuthorise;
#[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct PriorAuthedEntryEntryPair {
prior_authed_entry: AuthorisedEntry,
entry: Entry,
}
impl PriorAuthedEntryEntryPair {
pub fn new(prior_authed_entry: AuthorisedEntry, entry: Entry) -> Self {
Self {
prior_authed_entry,
entry,
}
}
pub fn prior_authed_entry(&self) -> &AuthorisedEntry {
&self.prior_authed_entry
}
pub fn entry(&self) -> &Entry {
&self.entry
}
pub fn into_parts(self) -> (AuthorisedEntry, Entry) {
(self.prior_authed_entry, self.entry)
}
}
impl RelativeEncodable<PriorAuthedEntryEntryPair> for AuthorisationToken {
async fn relative_encode<C>(
&self,
rel: &PriorAuthedEntryEntryPair,
consumer: &mut C,
) -> Result<(), C::Error>
where
C: ufotofu::BulkConsumer<Item = u8> + ?Sized,
{
let prev = &rel.prior_authed_entry;
let entry = &rel.entry;
let pair = crate::authorisation::PriorCapEntryPair::new(
prev.authorisation_token().capability().clone(),
entry.clone(),
);
self.capability().relative_encode(&pair, consumer).await?;
self.signature().encode(consumer).await?;
Ok(())
}
fn can_be_encoded_relative_to(&self, rel: &PriorAuthedEntryEntryPair) -> bool {
self.capability().granted_namespace() == rel.entry.namespace_id()
&& self.capability().granted_area().includes(&rel.entry)
}
}
impl RelativeDecodable<PriorAuthedEntryEntryPair> for AuthorisationToken {
type ErrorReason = Blame;
async fn relative_decode<P>(
rel: &PriorAuthedEntryEntryPair,
producer: &mut P,
) -> Result<Self, ufotofu::codec::DecodeError<P::Final, P::Error, Self::ErrorReason>>
where
P: ufotofu::BulkProducer<Item = u8> + ?Sized,
Self: Sized,
{
let prev = &rel.prior_authed_entry;
let entry = &rel.entry;
let pair = crate::authorisation::PriorCapEntryPair::new(
prev.authorisation_token().capability().clone(),
entry.clone(),
);
let write_cap = WriteCapability::relative_decode(&pair, producer).await?;
let signature = SubspaceSignature::decode(producer)
.await
.map_err(|_| DecodeError::Other(Blame::TheirFault))?;
Ok(Self::new(write_cap, signature))
}
}