Skip to content

Commit

Permalink
implement Serialize on errors
Browse files Browse the repository at this point in the history
this will allow biscuit-wasm to send bck errors to JS, and the
specifications samples to carry errors that can be tested against
  • Loading branch information
Geal committed Dec 24, 2021
1 parent 1b84203 commit 5d5a97b
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 14 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -59,3 +62,8 @@ include = [
"src/*/*.rs",
"tests/*.rs"
]

[[examples]]
name = "testcases"
required-features = ["serde-error"]
path = "examples/testcases.rs"
15 changes: 4 additions & 11 deletions examples/testcases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ struct AuthorizerWorld {
#[derive(Debug, Serialize)]
enum AuthorizerResult {
Ok(usize),
Err(Vec<String>),
Err(error::Token),
}

fn validate_token(
Expand All @@ -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),
}
}
};
Expand All @@ -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),
}
}
};
Expand Down Expand Up @@ -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,
}
Expand Down
47 changes: 44 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<Infallible> for Token {
Expand All @@ -53,11 +54,44 @@ impl From<Logic> for Token {

impl From<base64::DecodeError> 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,
Expand All @@ -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),
Expand Down Expand Up @@ -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,
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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."
);
}
Expand Down

0 comments on commit 5d5a97b

Please sign in to comment.