hessra_token_core/
revocation.rs1use crate::Biscuit;
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct RevocationId {
13 inner: Vec<u8>,
14}
15
16impl RevocationId {
17 pub fn new(bytes: Vec<u8>) -> Self {
19 Self { inner: bytes }
20 }
21
22 pub fn as_bytes(&self) -> &[u8] {
24 &self.inner
25 }
26
27 pub fn to_hex(&self) -> String {
29 hex::encode(&self.inner)
30 }
31
32 pub fn from_hex(hex_str: &str) -> Result<Self, hex::FromHexError> {
34 hex::decode(hex_str).map(Self::new)
35 }
36}
37
38impl fmt::Display for RevocationId {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "{}", self.to_hex())
41 }
42}
43
44impl From<Vec<u8>> for RevocationId {
45 fn from(bytes: Vec<u8>) -> Self {
46 Self::new(bytes)
47 }
48}
49
50pub fn get_revocation_ids(biscuit: &Biscuit) -> Vec<RevocationId> {
55 biscuit
56 .revocation_identifiers()
57 .into_iter()
58 .map(RevocationId::from)
59 .collect()
60}
61
62pub fn get_authority_revocation_id(biscuit: &Biscuit) -> Option<RevocationId> {
67 biscuit
68 .revocation_identifiers()
69 .into_iter()
70 .next()
71 .map(RevocationId::from)
72}
73
74pub fn get_block_revocation_id(biscuit: &Biscuit, index: usize) -> Option<RevocationId> {
79 biscuit
80 .revocation_identifiers()
81 .into_iter()
82 .nth(index)
83 .map(RevocationId::from)
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::KeyPair;
90 use biscuit_auth::macros::biscuit;
91
92 #[test]
93 fn test_revocation_id_hex_conversion() {
94 let bytes = vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
95 let rev_id = RevocationId::new(bytes.clone());
96
97 assert_eq!(rev_id.to_hex(), "0123456789abcdef");
99 assert_eq!(rev_id.to_string(), "0123456789abcdef");
100
101 let from_hex = RevocationId::from_hex("0123456789abcdef").unwrap();
103 assert_eq!(from_hex.as_bytes(), &bytes[..]);
104 assert_eq!(rev_id, from_hex);
105 }
106
107 #[test]
108 fn test_get_revocation_ids() {
109 let keypair = KeyPair::new();
110
111 let biscuit = biscuit!(
113 r#"
114 right("alice", "resource1", "read");
115 "#
116 )
117 .build(&keypair)
118 .unwrap();
119
120 let rev_ids = get_revocation_ids(&biscuit);
121
122 assert!(!rev_ids.is_empty());
124 assert_eq!(rev_ids.len(), 1);
125
126 assert!(!rev_ids[0].as_bytes().is_empty());
128 }
129
130 #[test]
131 fn test_get_authority_revocation_id() {
132 let keypair = KeyPair::new();
133
134 let biscuit = biscuit!(
135 r#"
136 right("alice", "resource1", "read");
137 "#
138 )
139 .build(&keypair)
140 .unwrap();
141
142 let auth_id = get_authority_revocation_id(&biscuit);
143 assert!(auth_id.is_some());
144
145 let all_ids = get_revocation_ids(&biscuit);
147 assert_eq!(auth_id.unwrap(), all_ids[0]);
148 }
149
150 #[test]
151 fn test_unique_revocation_ids() {
152 let keypair = KeyPair::new();
153
154 let biscuit1 = biscuit!(
156 r#"
157 right("alice", "resource1", "read");
158 "#
159 )
160 .build(&keypair)
161 .unwrap();
162
163 let biscuit2 = biscuit!(
164 r#"
165 right("alice", "resource1", "read");
166 "#
167 )
168 .build(&keypair)
169 .unwrap();
170
171 let rev_id1 = get_authority_revocation_id(&biscuit1).unwrap();
172 let rev_id2 = get_authority_revocation_id(&biscuit2).unwrap();
173
174 assert_ne!(rev_id1, rev_id2);
177 }
178}