use biscuit_auth::{AuthorizerBuilder, Biscuit, BlockBuilder, KeyPair, PublicKey, UnverifiedBiscuit};
use crate::revocation::hex;
use crate::{Error, Restriction, Right};
pub fn mint(root: &KeyPair, rights: &[Right], restrictions: &[Restriction]) -> Result<String, Error> {
if rights.is_empty() {
return Err(Error::invalid("rights", "a token needs at least one right"));
}
let mut builder = Biscuit::builder();
for right in rights {
builder = builder.fact(right.to_fact()).map_err(Error::Datalog)?;
}
for restriction in restrictions {
builder = builder.check(restriction.to_check()?).map_err(Error::Datalog)?;
}
builder
.build(root)
.and_then(|token| token.to_base64())
.map_err(Error::Datalog)
}
pub fn attenuate(token: &str, restrictions: &[Restriction]) -> Result<String, Error> {
if restrictions.is_empty() {
return Err(Error::invalid(
"restrictions",
"attenuating needs at least one restriction",
));
}
let token = UnverifiedBiscuit::from_base64(token.trim()).map_err(Error::InvalidToken)?;
let mut block = BlockBuilder::new();
for restriction in restrictions {
block = block.check(restriction.to_check()?).map_err(Error::Datalog)?;
}
token
.append(block)
.and_then(|token| token.to_base64())
.map_err(Error::Datalog)
}
pub fn granted_rights(root_key: PublicKey, token: &str) -> Result<Vec<Right>, Error> {
let token = Biscuit::from_base64(token.trim(), root_key).map_err(Error::InvalidToken)?;
let mut authorizer = AuthorizerBuilder::new().build(&token).map_err(Error::Datalog)?;
let rights: Vec<(String, String)> = authorizer
.query("granted($s, $u) <- right($s, $u)")
.map_err(Error::Datalog)?;
let mut rights = rights
.into_iter()
.map(|(server, user)| {
Ok(Right {
server: server.parse()?,
user: user.parse()?,
})
})
.collect::<Result<Vec<_>, Error>>()?;
rights.sort();
Ok(rights)
}
#[derive(Debug)]
pub struct BlockInfo {
pub revocation_id: String,
pub source: String,
}
pub fn inspect(token: &str) -> Result<Vec<BlockInfo>, Error> {
let token = UnverifiedBiscuit::from_base64(token.trim()).map_err(Error::InvalidToken)?;
token
.revocation_identifiers()
.iter()
.enumerate()
.map(|(index, id)| {
let source = token.print_block_source(index).map_err(Error::InvalidToken)?;
Ok(BlockInfo {
revocation_id: hex(id),
source,
})
})
.collect()
}