medallion/src/claims.rs

130 lines
4 KiB
Rust
Raw Normal View History

2017-02-20 18:05:06 +00:00
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
2017-02-13 23:40:07 +00:00
use Component;
use error::Error;
use serde::{Deserialize, Serialize};
use serde_json;
2017-02-14 18:33:00 +00:00
use serde_json::value::{Value};
2017-02-17 16:53:12 +00:00
use super::Result;
2017-02-13 23:40:07 +00:00
2017-02-15 20:41:52 +00:00
/// A default claim set, including the standard, or registered, claims and the ability to specify
/// your own as private claims.
2017-02-13 23:40:07 +00:00
#[derive(Debug, Default, PartialEq)]
2017-02-14 18:33:00 +00:00
pub struct Claims<T: Serialize + Deserialize> {
2017-02-13 23:40:07 +00:00
pub reg: Registered,
2017-02-14 18:33:00 +00:00
pub private: T
2017-02-13 23:40:07 +00:00
}
2017-02-15 20:41:52 +00:00
/// The registered claims from the spec.
2017-02-13 23:40:07 +00:00
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Registered {
pub iss: Option<String>,
pub sub: Option<String>,
pub aud: Option<String>,
pub exp: Option<u64>,
pub nbf: Option<u64>,
pub iat: Option<u64>,
pub jti: Option<String>,
}
2017-02-14 18:33:00 +00:00
impl<T: Serialize + Deserialize> Claims<T>{
2017-02-15 20:41:52 +00:00
/// Convenience factory method
2017-02-14 18:33:00 +00:00
pub fn new(reg: Registered, private: T) -> Claims<T> {
2017-02-13 23:40:07 +00:00
Claims {
reg: reg,
private: private
}
}
}
2017-02-14 18:33:00 +00:00
impl<T: Serialize + Deserialize> Component for Claims<T> {
2017-02-15 20:41:52 +00:00
/// This implementation simply parses the base64 data twice, each time applying it to the
/// registered and private claims.
2017-02-17 16:53:12 +00:00
fn from_base64(raw: &str) -> Result<Claims<T>> {
2017-02-20 18:05:06 +00:00
let data = decode_config(raw, URL_SAFE_NO_PAD)?;
2017-02-17 17:39:28 +00:00
let reg_claims: Registered = serde_json::from_slice(&data)?;
2017-02-13 23:40:07 +00:00
2017-02-17 17:39:28 +00:00
let pri_claims: T = serde_json::from_slice(&data)?;
2017-02-13 23:40:07 +00:00
2017-02-14 18:33:00 +00:00
Ok(Claims {
2017-02-13 23:40:07 +00:00
reg: reg_claims,
private: pri_claims
})
}
2017-02-15 20:41:52 +00:00
/// Renders both the registered and private claims into a single consolidated JSON
/// representation before encoding.
2017-02-17 16:53:12 +00:00
fn to_base64(&self) -> Result<String> {
2017-02-14 18:33:00 +00:00
if let Value::Object(mut reg_map) = serde_json::to_value(&self.reg)? {
if let Value::Object(pri_map) = serde_json::to_value(&self.private)? {
reg_map.extend(pri_map);
2017-02-17 17:39:28 +00:00
let s = serde_json::to_string(&reg_map)?;
2017-02-20 18:05:06 +00:00
let enc = encode_config((&*s).as_bytes(), URL_SAFE_NO_PAD);
2017-02-14 18:33:00 +00:00
Ok(enc)
} else {
Err(Error::Custom("Could not access registered claims.".to_owned()))
}
} else {
Err(Error::Custom("Could not access private claims.".to_owned()))
}
2017-02-13 23:40:07 +00:00
}
}
#[cfg(test)]
mod tests {
use std::default::Default;
use claims::{Claims, Registered};
use Component;
2017-02-14 18:33:00 +00:00
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
struct EmptyClaim { }
2017-02-14 18:53:40 +00:00
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
struct NonEmptyClaim {
user_id: String,
is_admin: bool,
first_name: Option<String>,
last_name: Option<String>
}
2017-02-13 23:40:07 +00:00
#[test]
fn from_base64() {
let enc = "eyJpc3MiOiJleGFtcGxlLmNvbSIsImV4cCI6MTMwMjMxOTEwMH0";
2017-02-14 18:33:00 +00:00
let claims: Claims<EmptyClaim> = Claims::from_base64(enc).unwrap();
2017-02-13 23:40:07 +00:00
assert_eq!(claims.reg.iss.unwrap(), "example.com");
2017-02-13 23:40:07 +00:00
assert_eq!(claims.reg.exp.unwrap(), 1302319100);
}
#[test]
fn multiple_types() {
let enc = "eyJpc3MiOiJleGFtcGxlLmNvbSIsImV4cCI6MTMwMjMxOTEwMH0";
2017-02-14 18:33:00 +00:00
let claims = Registered::from_base64(enc).unwrap();
2017-02-13 23:40:07 +00:00
assert_eq!(claims.iss.unwrap(), "example.com");
2017-02-13 23:40:07 +00:00
assert_eq!(claims.exp.unwrap(), 1302319100);
}
#[test]
fn roundtrip() {
2017-02-14 18:33:00 +00:00
let mut claims: Claims<EmptyClaim> = Default::default();
claims.reg.iss = Some("example.com".into());
2017-02-13 23:40:07 +00:00
claims.reg.exp = Some(1302319100);
let enc = claims.to_base64().unwrap();
assert_eq!(claims, Claims::from_base64(&*enc).unwrap());
}
2017-02-14 18:53:40 +00:00
#[test]
fn roundtrip_custom() {
let mut claims: Claims<NonEmptyClaim> = Default::default();
claims.reg.iss = Some("example.com".into());
claims.reg.exp = Some(1302319100);
claims.private.user_id = "123456".into();
claims.private.is_admin = false;
claims.private.first_name = Some("Random".into());
let enc = claims.to_base64().unwrap();
assert_eq!(claims, Claims::<NonEmptyClaim>::from_base64(&*enc).unwrap());
}
2017-02-13 23:40:07 +00:00
}