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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
use std::collections::HashMap; use std::clone::Clone; use std::borrow::Cow; use chrono::{Utc, Duration}; use super::{Issuer, Grant, Request, Scope, Time, TokenGenerator, Url, IssuedToken}; use super::generator::Assertion; use ring::digest::SHA256; use ring::hmac::SigningKey; #[derive(Clone)] struct SpecificGrant { owner_id: String, client_id: String, scope: Scope, redirect_url: Url, until: Time } impl<'a> Into<Grant<'a>> for &'a SpecificGrant { fn into(self) -> Grant<'a> { Grant { owner_id: Cow::Borrowed(&self.owner_id), client_id: Cow::Borrowed(&self.client_id), scope: Cow::Borrowed(&self.scope), redirect_url: Cow::Borrowed(&self.redirect_url), until: Cow::Borrowed(&self.until), } } } pub struct TokenMap<G: TokenGenerator> { generator: G, access: HashMap<String, SpecificGrant>, refresh: HashMap<String, SpecificGrant>, } impl<G: TokenGenerator> TokenMap<G> { pub fn new(generator: G) -> Self { Self { generator: generator, access: HashMap::new(), refresh: HashMap::new(), } } } impl<G: TokenGenerator> Issuer for TokenMap<G> { fn issue(&mut self, req: Request) -> IssuedToken { let grant = SpecificGrant { owner_id: req.owner_id.to_string(), client_id: req.client_id.to_string(), scope: req.scope.clone(), redirect_url: req.redirect_url.clone(), until: Utc::now() + Duration::hours(1), }; let (token, refresh) = { let generator_grant = (&grant).into(); let token = self.generator.generate(&generator_grant); let refresh = self.generator.generate(&generator_grant); (token, refresh) }; let until = grant.until.clone(); self.access.insert(token.clone(), grant.clone()); self.refresh.insert(refresh.clone(), grant); IssuedToken { token, refresh, until } } fn recover_token<'a>(&'a self, token: &'a str) -> Option<Grant<'a>> { self.access.get(token).map(|v| v.into()) } fn recover_refresh<'a>(&'a self, token: &'a str) -> Option<Grant<'a>> { self.refresh.get(token).map(|v| v.into()) } } pub struct TokenSigner { signer: Assertion, } impl TokenSigner { pub fn new(key: SigningKey) -> TokenSigner { TokenSigner { signer: Assertion::new(key) } } pub fn new_from_passphrase(passwd: &str) -> TokenSigner { let key = SigningKey::new(&SHA256, passwd.as_bytes()); TokenSigner { signer: Assertion::new(key) } } } impl Issuer for TokenSigner { fn issue(&mut self, req: Request) -> IssuedToken { let grant = Grant { owner_id: req.owner_id.into(), client_id: req.client_id.into(), scope: Cow::Borrowed(req.scope), redirect_url: Cow::Borrowed(req.redirect_url), until: Cow::Owned(Utc::now() + Duration::hours(1)), }; let token = self.signer.tag("token").generate(&grant); let refresh = self.signer.tag("refresh").generate(&grant); IssuedToken {token, refresh, until: grant.until.into_owned() } } fn recover_token<'a>(&'a self, token: &'a str) -> Option<Grant<'a>> { self.signer.tag("token").extract(token).ok() } fn recover_refresh<'a>(&'a self, token: &'a str) -> Option<Grant<'a>> { self.signer.tag("refresh").extract(token).ok() } } #[cfg(test)] mod tests { use super::*; #[test] fn token_signer_roundtrip() { let passwd = "Some secret password"; let mut issuer = TokenSigner::new_from_passphrase(passwd); let request = Request { client_id: "Client".into(), owner_id: "Owner".into(), redirect_url: &"https://example.com".parse().unwrap(), scope: &"default".parse().unwrap(), }; let issued = issuer.issue(request); assert!(Utc::now() < issued.until); let from_token = issuer.recover_token(&issued.token).unwrap(); assert_eq!(from_token.client_id, "Client"); assert_eq!(from_token.owner_id, "Owner"); assert!(Utc::now() < *from_token.until.as_ref()); } }