biscuit_auth/
error.rs

1//! error types
2//!
3
4use std::{
5    convert::{From, Infallible},
6    fmt::Display,
7};
8use thiserror::Error;
9
10/// the global error type for Biscuit
11#[derive(Error, Clone, Debug, PartialEq, Eq)]
12#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
13pub enum Token {
14    #[error("internal error")]
15    InternalError,
16    #[error("error deserializing or verifying the token")]
17    Format(Format),
18    #[error("tried to append a block to a sealed token")]
19    AppendOnSealed,
20    #[error("tried to seal an already sealed token")]
21    AlreadySealed,
22    #[error("authorization failed: {0}")]
23    FailedLogic(Logic),
24    #[error("error generating Datalog: {0}")]
25    Language(biscuit_parser::error::LanguageError),
26    #[error("Reached Datalog execution limits")]
27    RunLimit(RunLimit),
28    #[error("Cannot convert from Term: {0}")]
29    ConversionError(String),
30    #[error("Cannot decode base64 token: {0}")]
31    Base64(Base64Error),
32    #[error("Datalog  execution failure: {0}")]
33    Execution(Expression),
34}
35
36impl From<Infallible> for Token {
37    fn from(_: Infallible) -> Self {
38        unreachable!()
39    }
40}
41
42impl From<Format> for Token {
43    fn from(e: Format) -> Self {
44        Token::Format(e)
45    }
46}
47
48impl From<Logic> for Token {
49    fn from(e: Logic) -> Self {
50        Token::FailedLogic(e)
51    }
52}
53
54impl From<biscuit_parser::error::LanguageError> for Token {
55    fn from(e: biscuit_parser::error::LanguageError) -> Self {
56        Token::Language(e)
57    }
58}
59
60impl From<base64::DecodeError> for Token {
61    fn from(e: base64::DecodeError) -> Self {
62        let err = match e {
63            base64::DecodeError::InvalidByte(offset, byte) => {
64                Base64Error::InvalidByte(offset, byte)
65            }
66            base64::DecodeError::InvalidLength => Base64Error::InvalidLength,
67            base64::DecodeError::InvalidLastSymbol(offset, byte) => {
68                Base64Error::InvalidLastSymbol(offset, byte)
69            }
70        };
71
72        Token::Base64(err)
73    }
74}
75
76impl From<Execution> for Token {
77    fn from(e: Execution) -> Self {
78        match e {
79            Execution::RunLimit(limit) => Token::RunLimit(limit),
80            Execution::Expression(e) => Token::Execution(e),
81        }
82    }
83}
84
85#[derive(Clone, Debug, PartialEq, Eq)]
86#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
87pub enum Base64Error {
88    InvalidByte(usize, u8),
89    InvalidLength,
90    InvalidLastSymbol(usize, u8),
91}
92
93impl std::fmt::Display for Base64Error {
94    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
95        match *self {
96            Base64Error::InvalidByte(index, byte) => {
97                write!(f, "Invalid byte {}, offset {}.", byte, index)
98            }
99            Base64Error::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."),
100            Base64Error::InvalidLastSymbol(index, byte) => {
101                write!(f, "Invalid last symbol {}, offset {}.", byte, index)
102            }
103        }
104    }
105}
106
107/// Errors related to the token's serialization format or cryptographic
108/// signature
109#[derive(Error, Clone, Debug, PartialEq, Eq)]
110#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
111pub enum Format {
112    #[error("failed verifying the signature")]
113    Signature(Signature),
114    #[error("failed verifying the signature of a sealed token")]
115    SealedSignature,
116    #[error("the token does not provide intermediate public keys")]
117    EmptyKeys,
118    #[error("the root public key was not recognized")]
119    UnknownPublicKey,
120    #[error("could not deserialize the wrapper object")]
121    DeserializationError(String),
122    #[error("could not serialize the wrapper object")]
123    SerializationError(String),
124    #[error("could not deserialize the block")]
125    BlockDeserializationError(String),
126    #[error("could not serialize the block")]
127    BlockSerializationError(String),
128    #[error("Block format version is higher than supported")]
129    Version {
130        maximum: u32,
131        minimum: u32,
132        actual: u32,
133    },
134    #[error("invalid key size")]
135    InvalidKeySize(usize),
136    #[error("invalid signature size")]
137    InvalidSignatureSize(usize),
138    #[error("invalid key")]
139    InvalidKey(String),
140    #[error("could not deserialize signature")]
141    SignatureDeserializationError(String),
142    #[error("could not deserialize the block signature")]
143    BlockSignatureDeserializationError(String),
144    #[error("invalid block id")]
145    InvalidBlockId(usize),
146    #[error("the public key is already present in previous blocks")]
147    ExistingPublicKey(String),
148    #[error("multiple blocks declare the same symbols")]
149    SymbolTableOverlap,
150    #[error("multiple blocks declare the same public keys")]
151    PublicKeyTableOverlap,
152    #[error("the external public key was not recognized")]
153    UnknownExternalKey,
154    #[error("the symbol id was not in the table")]
155    UnknownSymbol(u64),
156    #[cfg(feature = "pem")]
157    #[error("PKCS8 serialization error")]
158    PKCS8(String),
159}
160
161/// Signature errors
162#[derive(Error, Clone, Debug, PartialEq, Eq)]
163#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
164pub enum Signature {
165    #[error("could not parse the signature elements")]
166    InvalidFormat,
167    #[error("the signature did not match")]
168    InvalidSignature(String),
169    #[error("could not sign")]
170    InvalidSignatureGeneration(String),
171}
172
173/// errors in the Datalog evaluation
174#[derive(Error, Clone, Debug, PartialEq, Eq)]
175#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
176pub enum Logic {
177    #[error("a rule provided by a block is producing a fact with unbound variables")]
178    InvalidBlockRule(u32, String),
179    #[error("{policy}, and the following checks failed: {}", display_failed_checks(.checks))]
180    Unauthorized {
181        /// the policy that matched
182        policy: MatchedPolicy,
183        /// list of checks that failed validation
184        checks: Vec<FailedCheck>,
185    },
186    #[error("the authorizer already contains a token")]
187    AuthorizerNotEmpty,
188    #[error("no matching policy was found, and the following checks failed: {}", display_failed_checks(.checks))]
189    NoMatchingPolicy {
190        /// list of checks that failed validation
191        checks: Vec<FailedCheck>,
192    },
193}
194
195#[derive(Error, Clone, Debug, PartialEq, Eq)]
196#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
197pub enum MatchedPolicy {
198    #[error("an allow policy matched (policy index: {0})")]
199    Allow(usize),
200    #[error("a deny policy matched (policy index: {0})")]
201    Deny(usize),
202}
203
204/// check errors
205#[derive(Error, Clone, Debug, PartialEq, Eq)]
206#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
207pub enum FailedCheck {
208    #[error("{0}")]
209    Block(FailedBlockCheck),
210    #[error("{0}")]
211    Authorizer(FailedAuthorizerCheck),
212}
213
214fn display_failed_checks(c: &[FailedCheck]) -> String {
215    c.iter()
216        .map(|c| c.to_string())
217        .collect::<Vec<_>>()
218        .join(", ")
219}
220
221#[derive(Clone, Debug, PartialEq, Eq)]
222#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
223pub struct FailedBlockCheck {
224    pub block_id: u32,
225    pub check_id: u32,
226    /// pretty print of the rule that failed
227    pub rule: String,
228}
229
230impl Display for FailedBlockCheck {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        write!(
233            f,
234            "Check n°{} in block n°{}: {}",
235            self.check_id, self.block_id, self.rule
236        )
237    }
238}
239
240#[derive(Clone, Debug, PartialEq, Eq)]
241#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
242pub struct FailedAuthorizerCheck {
243    pub check_id: u32,
244    /// pretty print of the rule that failed
245    pub rule: String,
246}
247
248impl Display for FailedAuthorizerCheck {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        write!(f, "Check n°{} in authorizer: {}", self.check_id, self.rule)
251    }
252}
253
254/// Datalog execution errors
255#[derive(Error, Clone, Debug, PartialEq, Eq)]
256#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
257pub enum Execution {
258    #[error("Reached Datalog execution limits")]
259    RunLimit(RunLimit),
260    #[error("Expression execution failure")]
261    Expression(Expression),
262}
263
264/// Datalog expression execution failure
265#[derive(Error, Clone, Debug, PartialEq, Eq)]
266#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
267pub enum Expression {
268    #[error("Unknown symbol")]
269    UnknownSymbol(u64),
270    #[error("Unknown variable")]
271    UnknownVariable(u32),
272    #[error("Invalid type")]
273    InvalidType,
274    #[error("Overflow")]
275    Overflow,
276    #[error("Division by zero")]
277    DivideByZero,
278    #[error("Wrong number of elements on stack")]
279    InvalidStack,
280    #[error("Shadowed variable")]
281    ShadowedVariable,
282    #[error("Undefined extern func: {0}")]
283    UndefinedExtern(String),
284    #[error("Error while evaluating extern func {0}: {1}")]
285    ExternEvalError(String, String),
286}
287
288/// runtime limits errors
289#[derive(Error, Clone, Debug, PartialEq, Eq)]
290#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
291pub enum RunLimit {
292    #[error("too many facts generated")]
293    TooManyFacts,
294    #[error("too many engine iterations")]
295    TooManyIterations,
296    #[error("spent too much time verifying")]
297    Timeout,
298    #[error("Unexpected query results, expected {0} got {1}")]
299    UnexpectedQueryResult(usize, usize),
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305
306    #[test]
307    fn error_format_strings() {
308        assert_eq!(
309            format!("{}", Token::ConversionError("test".to_owned())),
310            "Cannot convert from Term: test"
311        );
312
313        assert_eq!(
314            format!("{}", Token::Base64(Base64Error::InvalidLength)),
315            "Cannot decode base64 token: Encoded text cannot have a 6-bit remainder."
316        );
317
318        assert_eq!(
319            format!(
320                "{}",
321                Token::FailedLogic(Logic::Unauthorized {
322                    policy: MatchedPolicy::Allow(0),
323                    checks: vec![
324                        FailedCheck::Authorizer(FailedAuthorizerCheck {
325                            check_id: 0,
326                            rule: "check if false".to_string()
327                        }),
328                        FailedCheck::Block(FailedBlockCheck {
329                            block_id: 0,
330                            check_id: 0,
331                            rule: "check if false".to_string()
332                        })
333                    ]
334                })
335            )
336            .to_string(),
337            "authorization failed: an allow policy matched (policy index: 0), and the following checks failed: Check n°0 in authorizer: check if false, Check n°0 in block n°0: check if false"
338        );
339    }
340}