1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
use super::{Grant, TokenGenerator}; use chrono::{Utc, TimeZone}; use std::borrow::Cow; use rand::{thread_rng, Rng}; use ring; use rmp_serde; use url::Url; use base64::{encode, decode}; pub struct RandomGenerator { len: usize } impl RandomGenerator { pub fn new(length: usize) -> RandomGenerator { RandomGenerator {len: length} } } impl TokenGenerator for RandomGenerator { fn generate(&self, _grant: &Grant) -> String { let result = thread_rng().gen_iter::<u8>().take(self.len).collect::<Vec<u8>>(); encode(&result) } } pub struct Assertion { secret: ring::hmac::SigningKey, } #[derive(Serialize, Deserialize)] pub struct InternalAssertionGrant<'a>(&'a str, &'a str, &'a str, &'a str, (i64, u32), &'a str); #[derive(Serialize, Deserialize)] pub struct AssertGrant(Vec<u8>, Vec<u8>); pub struct TaggedAssertion<'a>(&'a Assertion, &'a str); impl Assertion { pub fn new(key: ring::hmac::SigningKey) -> Assertion { Assertion { secret: key} } pub fn tag<'a>(&'a self, tag: &'a str) -> TaggedAssertion<'a> { TaggedAssertion(self, tag) } fn extract<'a>(&self, token: &'a str) -> Result<(Grant<'a>, String), ()> { let readbytes = decode(token).map_err(|_| ())?; let AssertGrant(message, digest) = rmp_serde::from_slice(&readbytes).unwrap(); ring::hmac::verify_with_own_key(&self.secret, &message, &digest).map_err(|_| ())?; let InternalAssertionGrant(owner_id, client_id, redirectbytes, scope, (ts, tsnanos), tag) = rmp_serde::from_slice(&message).map_err(|_| ())?; let redirect_url = Url::parse(redirectbytes).map_err(|_| ())?; let scope = scope.parse().map_err(|_| ())?; let until = Utc::timestamp(&Utc, ts, tsnanos); Ok((Grant{ owner_id: Cow::Owned(owner_id.to_string()), client_id: Cow::Owned(client_id.to_string()), redirect_url: Cow::Owned(redirect_url), scope: Cow::Owned(scope), until: Cow::Owned(until), }, tag.to_string())) } fn generate_tagged(&self, grant: &Grant, tag: &str) -> String { let tosign = rmp_serde::to_vec(&InternalAssertionGrant( &grant.owner_id, &grant.client_id, grant.redirect_url.as_str(), &grant.scope.to_string(), (grant.until.timestamp(), grant.until.timestamp_subsec_nanos()), tag)).unwrap(); let signature = ring::hmac::sign(&self.secret, &tosign); encode(&rmp_serde::to_vec(&AssertGrant(tosign, signature.as_ref().to_vec())).unwrap()) } } impl<'a> TaggedAssertion<'a> { pub fn extract<'b>(&self, token: &'b str) -> Result<Grant<'b>, ()> { self.0.extract(token).and_then(|(token, tag)| { if tag == self.1 { Ok(token) } else { Err(()) } }) } } impl<'a> TokenGenerator for TaggedAssertion<'a> { fn generate(&self, grant: &Grant) -> String { self.0.generate_tagged(grant, self.1) } }