Skip to main content

biscuit_ssh_core/
token.rs

1//! Creating, restricting and inspecting tokens.
2
3use biscuit_auth::{AuthorizerBuilder, Biscuit, BlockBuilder, KeyPair, PublicKey, UnverifiedBiscuit};
4
5use crate::revocation::hex;
6use crate::{Error, Restriction, Right};
7
8/// Creates a token signed by the root key, granting `rights` under
9/// `restrictions`.
10pub fn mint(root: &KeyPair, rights: &[Right], restrictions: &[Restriction]) -> Result<String, Error> {
11    if rights.is_empty() {
12        return Err(Error::invalid("rights", "a token needs at least one right"));
13    }
14    let mut builder = Biscuit::builder();
15    for right in rights {
16        builder = builder.fact(right.to_fact()).map_err(Error::Datalog)?;
17    }
18    for restriction in restrictions {
19        builder = builder.check(restriction.to_check()?).map_err(Error::Datalog)?;
20    }
21    builder
22        .build(root)
23        .and_then(|token| token.to_base64())
24        .map_err(Error::Datalog)
25}
26
27/// Appends a block adding `restrictions` to `token`.
28///
29/// Needs no key: anyone holding a token can restrict it before handing it
30/// over, and the result can never grant more than the original.
31pub fn attenuate(token: &str, restrictions: &[Restriction]) -> Result<String, Error> {
32    if restrictions.is_empty() {
33        return Err(Error::invalid(
34            "restrictions",
35            "attenuating needs at least one restriction",
36        ));
37    }
38    let token = UnverifiedBiscuit::from_base64(token.trim()).map_err(Error::InvalidToken)?;
39    let mut block = BlockBuilder::new();
40    for restriction in restrictions {
41        block = block.check(restriction.to_check()?).map_err(Error::Datalog)?;
42    }
43    token
44        .append(block)
45        .and_then(|token| token.to_base64())
46        .map_err(Error::Datalog)
47}
48
49/// The rights granted by the authority block of `token`, sorted, once its
50/// signature is checked against `root_key`. Restrictions are not evaluated.
51pub fn granted_rights(root_key: PublicKey, token: &str) -> Result<Vec<Right>, Error> {
52    let token = Biscuit::from_base64(token.trim(), root_key).map_err(Error::InvalidToken)?;
53    let mut authorizer = AuthorizerBuilder::new().build(&token).map_err(Error::Datalog)?;
54    let rights: Vec<(String, String)> = authorizer
55        .query("granted($s, $u) <- right($s, $u)")
56        .map_err(Error::Datalog)?;
57    let mut rights = rights
58        .into_iter()
59        .map(|(server, user)| {
60            Ok(Right {
61                server: server.parse()?,
62                user: user.parse()?,
63            })
64        })
65        .collect::<Result<Vec<_>, Error>>()?;
66    rights.sort();
67    Ok(rights)
68}
69
70/// One block of a token, as shown to humans.
71#[derive(Debug)]
72pub struct BlockInfo {
73    /// Identifier to add to the revocation list to revoke this block (and
74    /// every token derived from it).
75    pub revocation_id: String,
76    /// Datalog source of the block.
77    pub source: String,
78}
79
80/// Decodes `token` without verifying its signature, to show its content.
81pub fn inspect(token: &str) -> Result<Vec<BlockInfo>, Error> {
82    let token = UnverifiedBiscuit::from_base64(token.trim()).map_err(Error::InvalidToken)?;
83    token
84        .revocation_identifiers()
85        .iter()
86        .enumerate()
87        .map(|(index, id)| {
88            let source = token.print_block_source(index).map_err(Error::InvalidToken)?;
89            Ok(BlockInfo {
90                revocation_id: hex(id),
91                source,
92            })
93        })
94        .collect()
95}