diff --git a/Cargo.toml b/Cargo.toml index 0adf268a..8d186a12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ regex-full = [ "regex/perf", "regex/unicode"] # used by cargo-c to signal the compilation of C bindings capi = ["inline-c"] wasm = ["wasm-bindgen"] +# used by biscuit-wasm to serialize errors to JSON +serde-error = ["serde"] [dependencies] rand_core = "^0.5" @@ -33,6 +35,7 @@ inline-c = { version = "0.1", optional = true } wasm-bindgen = { version = "0.2", optional = true } base64 = "0.13.0" ed25519-dalek = "1.0.1" +serde = { version = "1.0.132", optional = true, features = ["derive"] } [dev-dependencies] rand = "0.7" @@ -59,3 +62,8 @@ include = [ "src/*/*.rs", "tests/*.rs" ] + +[[examples]] +name = "testcases" +required-features = ["serde-error"] +path = "examples/testcases.rs" diff --git a/examples/testcases.rs b/examples/testcases.rs index 36806347..b3edff53 100644 --- a/examples/testcases.rs +++ b/examples/testcases.rs @@ -202,7 +202,7 @@ struct AuthorizerWorld { #[derive(Debug, Serialize)] enum AuthorizerResult { Ok(usize), - Err(Vec), + Err(error::Token), } fn validate_token( @@ -218,7 +218,7 @@ fn validate_token( return Validation { world: None, authorizer_code: String::new(), - result: AuthorizerResult::Err(vec![format!("{:?}", e)]), + result: AuthorizerResult::Err(e), } } }; @@ -229,7 +229,7 @@ fn validate_token( return Validation { world: None, authorizer_code: String::new(), - result: AuthorizerResult::Err(vec![format!("{:?}", e)]), + result: AuthorizerResult::Err(e), } } }; @@ -274,14 +274,7 @@ fn validate_token( }), result: match res { Ok(i) => AuthorizerResult::Ok(i), - Err(e) => { - if let error::Token::FailedLogic(error::Logic::FailedChecks(mut v)) = e { - AuthorizerResult::Err(v.drain(..).map(|e| format!("{:?}", e)).collect()) - } else { - let s = format!("{:?}", e); - AuthorizerResult::Err(vec![s]) - } - } + Err(e) => AuthorizerResult::Err(e), }, authorizer_code, } diff --git a/src/error.rs b/src/error.rs index a999478f..6cea1112 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,7 @@ use thiserror::Error; /// the global error type for Biscuit #[derive(Error, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub enum Token { #[error("internal error")] InternalError, @@ -30,7 +31,7 @@ pub enum Token { #[error("Cannot convert from Term: {0}")] ConversionError(String), #[error("Cannot decode base64 token: {0}")] - Base64(base64::DecodeError), + Base64(Base64Error), } impl From for Token { @@ -53,11 +54,44 @@ impl From for Token { impl From for Token { fn from(e: base64::DecodeError) -> Self { - Token::Base64(e) + let err = match e { + base64::DecodeError::InvalidByte(offset, byte) => { + Base64Error::InvalidByte(offset, byte) + } + base64::DecodeError::InvalidLength => Base64Error::InvalidLength, + base64::DecodeError::InvalidLastSymbol(offset, byte) => { + Base64Error::InvalidLastSymbol(offset, byte) + } + }; + + Token::Base64(err) } } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] +pub enum Base64Error { + InvalidByte(usize, u8), + InvalidLength, + InvalidLastSymbol(usize, u8), +} + +impl std::fmt::Display for Base64Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match *self { + Base64Error::InvalidByte(index, byte) => { + write!(f, "Invalid byte {}, offset {}.", byte, index) + } + Base64Error::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."), + Base64Error::InvalidLastSymbol(index, byte) => { + write!(f, "Invalid last symbol {}, offset {}.", byte, index) + } + } + } +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub struct InvalidBlockIndex { pub expected: u32, pub found: u32, @@ -66,6 +100,7 @@ pub struct InvalidBlockIndex { /// Errors related to the token's serialization format or cryptographic /// signature #[derive(Error, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub enum Format { #[error("failed verifying the signature")] Signature(Signature), @@ -95,6 +130,7 @@ pub enum Format { /// Signature errors #[derive(Error, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub enum Signature { #[error("could not parse the signature elements")] InvalidFormat, @@ -106,6 +142,7 @@ pub enum Signature { /// errors in the Datalog evaluation #[derive(Error, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub enum Logic { #[error("a fact of the authority block did not have the authority tag")] InvalidAuthorityFact(String), @@ -127,6 +164,7 @@ pub enum Logic { /// check check errors #[derive(Error, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub enum FailedCheck { #[error("a check failed in a block")] Block(FailedBlockCheck), @@ -135,6 +173,7 @@ pub enum FailedCheck { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub struct FailedBlockCheck { pub block_id: u32, pub check_id: u32, @@ -143,6 +182,7 @@ pub struct FailedBlockCheck { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub struct FailedAuthorizerCheck { pub check_id: u32, /// pretty print of the rule that failed @@ -151,6 +191,7 @@ pub struct FailedAuthorizerCheck { /// runtime limits errors #[derive(Error, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))] pub enum RunLimit { #[error("too many facts generated")] TooManyFacts, @@ -172,7 +213,7 @@ mod tests { ); assert_eq!( - format!("{}", Token::Base64(base64::DecodeError::InvalidLength)), + format!("{}", Token::Base64(Base64Error::InvalidLength)), "Cannot decode base64 token: Encoded text cannot have a 6-bit remainder." ); }