Switch to failure (#6)

* Switch to failure

* Bump version
This commit is contained in:
Thomas Gideon 2018-11-16 17:28:01 -05:00 committed by GitHub
parent 1b594ff60e
commit 7fcae534f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 118 deletions

View file

@ -2,23 +2,24 @@
authors = ["Thomas Gideon <cmdln@thecommandline.net>"] authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
categories = ["cryptography", "authentication", "web-programming", "data-structures"] categories = ["cryptography", "authentication", "web-programming", "data-structures"]
description = "JWT library for rust using serde, serde_json and openssl" description = "JWT library for rust using serde, serde_json and openssl"
documentation = "https://commandline.github.io/medallion/" documentation = "https://cmdln.github.io/medallion/"
homepage = "http://github.com/commandline/medallion" homepage = "http://github.com/cmdln/medallion"
keywords = ["JWT", "token", "web", "JSON", "RSA"] keywords = ["JWT", "token", "web", "JSON", "RSA"]
license = "MIT" license = "MIT"
name = "medallion" name = "medallion"
readme = "README.md" readme = "README.md"
repository = "http://github.com/commandline/medallion" repository = "http://github.com/cmdn/medallion"
version = "2.2.3" version = "2.3.0"
[badges] [badges]
[badges.travis-ci] [badges.travis-ci]
branch = "master" branch = "master"
repository = "https://travis-ci.org/commandline/medallion" repository = "https://travis-ci.org/cmdln/medallion"
[dependencies] [dependencies]
base64 = "0.9" base64 = "~0.10.0"
openssl = "0.10" failure = "~0.1.3"
serde = "1.0" openssl = "~0.10.15"
serde_derive = "1.0" serde = "^1.0.80"
serde_json = "1.0" serde_derive = "^1.0.80"
time = "0.1" serde_json = "^1.0.33"
time = "~0.1.40"

View file

@ -1,10 +1,13 @@
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD}; use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
use header::Algorithm; use header::Algorithm;
use openssl::hash::MessageDigest; use openssl::{
use openssl::memcmp; hash::MessageDigest,
use openssl::pkey::PKey; memcmp,
use openssl::rsa::Rsa; pkey::PKey,
use openssl::sign::{Signer, Verifier}; rsa::Rsa,
sign::{Signer, Verifier},
};
use super::Result; use super::Result;
pub fn sign(data: &str, key: &[u8], algorithm: &Algorithm) -> Result<String> { pub fn sign(data: &str, key: &[u8], algorithm: &Algorithm) -> Result<String> {
@ -72,9 +75,9 @@ fn verify_rsa(signature: &str, data: &str, key: &[u8], digest: MessageDigest) ->
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::{sign, verify};
use header::Algorithm; use header::Algorithm;
use openssl; use openssl;
use super::{sign, verify};
#[test] #[test]
pub fn sign_data_hmac() { pub fn sign_data_hmac() {
@ -83,7 +86,7 @@ pub mod tests {
let real_sig = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"; let real_sig = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let data = format!("{}.{}", header, claims); let data = format!("{}.{}", header, claims);
let sig = sign(&*data, "secret".as_bytes(), &Algorithm::HS256); let sig = sign(&*data, b"secret", &Algorithm::HS256);
assert_eq!(sig.unwrap(), real_sig); assert_eq!(sig.unwrap(), real_sig);
} }
@ -120,6 +123,6 @@ pub mod tests {
let target = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"; let target = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let data = format!("{}.{}", header, claims); let data = format!("{}.{}", header, claims);
assert!(verify(target, &*data, "secret".as_bytes(), &Algorithm::HS256).unwrap()); assert!(verify(target, &*data, b"secret", &Algorithm::HS256).unwrap());
} }
} }

View file

@ -1,67 +1,3 @@
use base64::DecodeError; use failure::Error;
use serde_json;
use std::error;
use std::fmt;
use std::string::FromUtf8Error;
use openssl::error::ErrorStack;
#[derive(Debug)] pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
/// Custom, Medallion specific errors.
Custom(String),
/// String encoding errors.
Utf8(FromUtf8Error),
/// Base64 encoding or decoding errors.
Base64(DecodeError),
/// JSON parsing or stringifying errors.
JSON(serde_json::Error),
/// Errors from RSA operations.
Crypto(ErrorStack),
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Custom(ref message) => message,
Error::Utf8(ref err) => err.description(),
Error::Base64(ref err) => err.description(),
Error::JSON(ref err) => err.description(),
Error::Crypto(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::Custom(_) => None,
Error::Utf8(ref err) => Some(err),
Error::Base64(ref err) => Some(err),
Error::JSON(ref err) => Some(err),
Error::Crypto(ref err) => Some(err),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Custom(ref message) => f.write_str(message),
Error::Utf8(ref err) => err.fmt(f),
Error::Base64(ref err) => err.fmt(f),
Error::JSON(ref err) => err.fmt(f),
Error::Crypto(ref err) => err.fmt(f),
}
}
}
macro_rules! error_wrap {
($f: ty, $e: expr) => {
impl From<$f> for Error {
fn from(f: $f) -> Error { $e(f) }
}
}
}
error_wrap!(FromUtf8Error, Error::Utf8);
error_wrap!(DecodeError, Error::Base64);
error_wrap!(serde_json::Error, Error::JSON);
error_wrap!(ErrorStack, Error::Crypto);

View file

@ -1,10 +1,7 @@
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD}; use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
use serde::de::DeserializeOwned; use serde::{de::DeserializeOwned, Serialize};
use serde::Serialize;
use serde_json::{self, Value}; use serde_json::{self, Value};
use std::default::Default;
use super::error::Error;
use super::Result; use super::Result;
/// An extensible Header that provides only algorithm field and allows for additional fields to be /// An extensible Header that provides only algorithm field and allows for additional fields to be
@ -55,9 +52,7 @@ impl<T: Serialize + DeserializeOwned> Header<T> {
let enc = encode_config((&*s).as_bytes(), URL_SAFE_NO_PAD); let enc = encode_config((&*s).as_bytes(), URL_SAFE_NO_PAD);
Ok(enc) Ok(enc)
} else { } else {
Err(Error::Custom( Err(format_err!("Could not access additional headers."))
"Could not access additional headers.".to_owned(),
))
} }
} }
None => { None => {
@ -67,7 +62,7 @@ impl<T: Serialize + DeserializeOwned> Header<T> {
} }
} }
} else { } else {
Err(Error::Custom("Could not access default header.".to_owned())) Err(format_err!("Could not access default header."))
} }
} }
} }
@ -113,7 +108,7 @@ mod tests {
#[test] #[test]
fn to_base64() { fn to_base64() {
let enc = "eyJhbGciOiJIUzI1NiJ9"; let enc = "eyJhbGciOiJIUzI1NiJ9";
let header: Header<()> = Default::default(); let header: Header<()> = Header::default();
assert_eq!(enc, header.to_base64().unwrap()); assert_eq!(enc, header.to_base64().unwrap());
} }
@ -126,7 +121,7 @@ mod tests {
kid: "1KSF3g".into(), kid: "1KSF3g".into(),
typ: "JWT".into(), typ: "JWT".into(),
}), }),
..Default::default() ..Header::default()
}; };
assert_eq!(enc, header.to_base64().unwrap()); assert_eq!(enc, header.to_base64().unwrap());
@ -134,7 +129,7 @@ mod tests {
#[test] #[test]
fn roundtrip() { fn roundtrip() {
let header: Header<()> = Default::default(); let header: Header<()> = Header::default();
let enc = header.to_base64().unwrap(); let enc = header.to_base64().unwrap();
assert_eq!(header, Header::from_base64(&*enc).unwrap()); assert_eq!(header, Header::from_base64(&*enc).unwrap());
} }

View file

@ -7,6 +7,8 @@
///! Tries to support the standard uses for JWTs while providing reasonable ways to extend, ///! Tries to support the standard uses for JWTs while providing reasonable ways to extend,
///! primarily by adding custom headers and claims to tokens. ///! primarily by adding custom headers and claims to tokens.
extern crate base64; extern crate base64;
#[macro_use]
extern crate failure;
extern crate openssl; extern crate openssl;
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
@ -14,19 +16,16 @@ extern crate serde_derive;
extern crate serde_json; extern crate serde_json;
extern crate time; extern crate time;
pub use error::Error; pub use header::{Algorithm, Header};
pub use header::Algorithm;
pub use header::Header;
pub use payload::{DefaultPayload, Payload}; pub use payload::{DefaultPayload, Payload};
use serde::de::DeserializeOwned; use serde::{de::DeserializeOwned, Serialize};
use serde::Serialize;
mod crypt; mod crypt;
pub mod error; mod error;
mod header; mod header;
mod payload; mod payload;
pub type Result<T> = std::result::Result<T, Error>; pub use error::Result;
/// A convenient type that binds the same type parameter for the custom claims, an empty tuple, as /// A convenient type that binds the same type parameter for the custom claims, an empty tuple, as
/// `DefaultPayload` so that the two aliases may be used together to reduce boilerplate when no /// `DefaultPayload` so that the two aliases may be used together to reduce boilerplate when no
@ -118,7 +117,7 @@ mod tests {
let token = DefaultToken::<()>::parse(raw).unwrap(); let token = DefaultToken::<()>::parse(raw).unwrap();
assert_eq!(token.header.alg, HS256); assert_eq!(token.header.alg, HS256);
assert!(token.verify("secret".as_bytes()).unwrap()); assert!(token.verify(b"secret").unwrap());
} }
#[test] #[test]
@ -131,7 +130,7 @@ mod tests {
..DefaultPayload::default() ..DefaultPayload::default()
}; };
let token = Token::new(header, payload); let token = Token::new(header, payload);
let key = "secret".as_bytes(); let key = b"secret";
let raw = token.sign(key).unwrap(); let raw = token.sign(key).unwrap();
let same = Token::parse(&*raw).unwrap(); let same = Token::parse(&*raw).unwrap();
@ -143,7 +142,7 @@ mod tests {
pub fn roundtrip_expired() { pub fn roundtrip_expired() {
let now = time::now(); let now = time::now();
let token = create_for_range(now, now + Duration::minutes(-5)); let token = create_for_range(now, now + Duration::minutes(-5));
let key = "secret".as_bytes(); let key = b"secret";
let raw = token.sign(key).unwrap(); let raw = token.sign(key).unwrap();
let same = Token::parse(&*raw).unwrap(); let same = Token::parse(&*raw).unwrap();
@ -155,7 +154,7 @@ mod tests {
pub fn roundtrip_not_yet_valid() { pub fn roundtrip_not_yet_valid() {
let now = time::now(); let now = time::now();
let token = create_for_range(now + Duration::minutes(5), now + Duration::minutes(10)); let token = create_for_range(now + Duration::minutes(5), now + Duration::minutes(10));
let key = "secret".as_bytes(); let key = b"secret";
let raw = token.sign(key).unwrap(); let raw = token.sign(key).unwrap();
let same = Token::parse(&*raw).unwrap(); let same = Token::parse(&*raw).unwrap();
@ -171,7 +170,7 @@ mod tests {
..Header::default() ..Header::default()
}; };
let token = DefaultToken { let token = DefaultToken {
header: header, header,
..Token::default() ..Token::default()
}; };
let raw = token let raw = token

View file

@ -1,6 +1,5 @@
use super::Result; use super::Result;
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD}; use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
use error::Error;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use serde_json; use serde_json;
@ -68,7 +67,7 @@ impl<T: Serialize + DeserializeOwned> Payload<T> {
let enc = encode_config((&*s).as_bytes(), URL_SAFE_NO_PAD); let enc = encode_config((&*s).as_bytes(), URL_SAFE_NO_PAD);
Ok(enc) Ok(enc)
} else { } else {
Err(Error::Custom("Could not access custom claims.".to_owned())) Err(format_err!("Could not access custom claims."))
} }
} }
None => { None => {
@ -78,9 +77,7 @@ impl<T: Serialize + DeserializeOwned> Payload<T> {
} }
} }
} else { } else {
Err(Error::Custom( Err(format_err!("Could not access standard claims.",))
"Could not access standard claims.".to_owned(),
))
} }
} }
@ -234,10 +231,10 @@ mod tests {
fn create_default() -> DefaultPayload { fn create_default() -> DefaultPayload {
DefaultPayload { DefaultPayload {
aud: Some("login_service".into()), aud: Some("login_service".into()),
iat: Some(1302317100), iat: Some(1_302_317_100),
iss: Some("example.com".into()), iss: Some("example.com".into()),
exp: Some(1302319100), exp: Some(1_302_319_100),
nbf: Some(1302317100), nbf: Some(1_302_317_100),
sub: Some("Random User".into()), sub: Some("Random User".into()),
..Default::default() ..Default::default()
} }
@ -246,8 +243,8 @@ mod tests {
fn create_custom() -> Payload<CustomClaims> { fn create_custom() -> Payload<CustomClaims> {
Payload { Payload {
iss: Some("example.com".into()), iss: Some("example.com".into()),
iat: Some(1302317100), iat: Some(1_302_317_100),
exp: Some(1302319100), exp: Some(1_302_319_100),
claims: Some(CustomClaims { claims: Some(CustomClaims {
user_id: "123456".into(), user_id: "123456".into(),
is_admin: false, is_admin: false,