Skip to main content

cloudiful_redactor/session/
permit.rs

1use std::collections::HashSet;
2
3use anyhow::{Result, anyhow};
4
5use crate::{RedactionSession, RestorePermit};
6
7use super::crypto::{open_json, seal_json};
8
9pub(super) const PERMIT_VERSION: u32 = 1;
10const PERMIT_AAD: &[u8] = b"redactor:restore-permit:v1";
11
12pub fn create_restore_permit(session: &RedactionSession) -> RestorePermit {
13    RestorePermit {
14        version: PERMIT_VERSION,
15        permit_id: crate::replace::random_id(),
16        scope_id: session.scope_id.clone(),
17        external_id: session.external_id.clone(),
18        issued_tokens: session.issued_tokens.clone(),
19    }
20}
21
22pub fn encrypt_restore_permit(permit: &RestorePermit, passphrase: &str) -> Result<String> {
23    seal_json(permit, passphrase, PERMIT_AAD)
24}
25
26pub fn decrypt_restore_permit(data: &str, passphrase: &str) -> Result<RestorePermit> {
27    let permit: RestorePermit = open_json(data, passphrase, PERMIT_AAD)
28        .map_err(|error| anyhow!("failed to decrypt restore permit: {error}"))?;
29    if permit.version != PERMIT_VERSION {
30        return Err(anyhow!(
31            "unsupported restore permit version {}",
32            permit.version
33        ));
34    }
35    Ok(permit)
36}
37
38pub fn authorized_tokens<'a>(
39    session: &'a RedactionSession,
40    permits: &[RestorePermit],
41) -> Result<HashSet<&'a str>> {
42    let known = session
43        .entries
44        .iter()
45        .map(|entry| entry.token.as_str())
46        .collect::<HashSet<_>>();
47    let mut authorized = HashSet::new();
48    for permit in permits {
49        if permit.version != PERMIT_VERSION {
50            return Err(anyhow!(
51                "unsupported restore permit version {}",
52                permit.version
53            ));
54        }
55        if permit.scope_id != session.scope_id || permit.external_id != session.external_id {
56            return Err(anyhow!("restore permit does not match session context"));
57        }
58        for token in &permit.issued_tokens {
59            let Some(known_token) = known.get(token.as_str()) else {
60                return Err(anyhow!("restore permit authorizes unknown token `{token}`"));
61            };
62            authorized.insert(*known_token);
63        }
64    }
65    Ok(authorized)
66}