use chrono::{Duration, Utc};
use derive_builder::Builder;
use jsonwebtoken::{encode, EncodingKey, Header};
pub use jsonwebtoken::Algorithm;
use crate::error;
mod claims;
use claims::Claims;
mod fetcher;
pub use fetcher::SecurityFetcher;
pub use fetcher::SecurityFileFetcher;
#[derive(Builder)]
#[builder(pattern = "owned")]
pub struct Security {
project_id: u64,
#[builder(setter(strip_option))]
fetcher: Option<Box<dyn SecurityFetcher + Send + Sync>>,
#[builder(default = "Duration::days(365)")]
expiration: Duration,
#[builder(default = "Algorithm::RS256")]
algorithm: Algorithm,
}
impl Default for Security {
fn default() -> Self {
Self {
project_id: Default::default(),
fetcher: Option::default(),
expiration: Duration::days(365),
algorithm: Algorithm::RS256,
}
}
}
impl Security {
pub async fn generate_token(&self) -> Result<Token, error::Error> {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let iat = Utc::now().timestamp() as usize;
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let exp = (Utc::now() + self.expiration).timestamp() as usize;
let claims = Claims {
aud: Some(self.project_id.to_string()), iat: Some(iat), exp: Some(exp),
..Default::default()
};
let header: Header = Header::new(self.algorithm);
let private_key = self
.fetcher
.as_ref()
.expect("must provide a fetcher")
.read_private_key()
.await?;
let encoding_key = EncodingKey::from_rsa_pem(&private_key)?;
let token = encode(&header, &claims, &encoding_key)?;
Ok(token)
}
}
pub type Token = String;