use serde::{Deserialize, Serialize};
use std::time::SystemTime;
use url::Url;
use crate::internals;
use crate::Auth as AuthTrait;
use crate::Error;
#[derive(Clone, Serialize)]
pub struct ApproleLogin {
pub role_id: String,
pub secret_id: String,
}
#[allow(dead_code)]
#[derive(Deserialize)]
struct Auth {
pub renewable: bool,
pub lease_duration: u64,
pub token_policies: Vec<String>,
pub accessor: String,
pub client_token: String,
}
#[allow(dead_code)]
#[derive(Deserialize)]
struct ApproleResponse {
pub auth: Auth,
pub lease_duration: i64,
pub renewable: bool,
pub lease_id: String,
}
#[derive(Deserialize)]
struct RenewAuth {
pub renewable: bool,
pub lease_duration: u64,
pub policies: Vec<String>,
pub client_token: String,
}
#[derive(Deserialize)]
struct RenewResponse {
pub auth: RenewAuth,
}
pub struct Session {
approle: ApproleLogin,
token: internals::TokenContainer,
}
impl AuthTrait for Session {
fn is_expired(&self) -> bool {
let start_time = self.token.get_start();
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let elapsed = current_time - start_time;
let duration = self.token.get_duration();
elapsed >= duration
}
fn get_token(&self) -> String {
match self.token.get_token() {
None => String::from(""),
Some(s) => s,
}
}
fn auth(&self, vault_url: &str) -> Result<(), Error> {
let mut login_url = match Url::parse(vault_url) {
Err(e) => {
return Err(Error::from(e));
}
Ok(url) => url,
};
login_url = match login_url.join("v1/auth/approle/login") {
Err(e) => {
return Err(Error::from(e));
}
Ok(u) => u,
};
let http_client = reqwest::blocking::Client::new();
let res = http_client.post(login_url).json(&self.approle).send();
let response = match res {
Err(e) => {
return Err(Error::from(e));
}
Ok(resp) => resp,
};
let status_code = response.status().as_u16();
if status_code != 200 && status_code != 204 {
return Err(Error::from(status_code));
}
let data = match response.json::<ApproleResponse>() {
Err(e) => Err(Error::from(e)),
Ok(json) => Ok(json),
};
let data = data.unwrap();
let token = data.auth.client_token;
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let duration = data.auth.lease_duration;
self.token.set_token(token);
self.token.set_renewable(data.auth.renewable);
self.token.set_start(current_time);
self.token.set_duration(duration);
Ok(())
}
fn is_renewable(&self) -> bool {
self.token.get_renewable()
}
fn get_total_duration(&self) -> u64 {
self.token.get_duration()
}
fn renew(&self, vault_url: &str) -> Result<(), Error> {
let mut renew_url = match Url::parse(vault_url) {
Err(e) => {
return Err(Error::from(e));
}
Ok(url) => url,
};
renew_url = match renew_url.join("v1/auth/token/renew-self") {
Err(e) => {
return Err(Error::from(e));
}
Ok(u) => u,
};
let http_client = reqwest::blocking::Client::new();
let res = http_client
.post(renew_url)
.header("X-Vault-Token", self.token.get_token().unwrap())
.send();
let response = match res {
Err(e) => {
return Err(Error::from(e));
}
Ok(resp) => resp,
};
let status_code = response.status().as_u16();
if status_code != 200 && status_code != 204 {
return Err(Error::from(status_code));
}
let data = match response.json::<RenewResponse>() {
Err(e) => Err(Error::from(e)),
Ok(json) => Ok(json),
};
let data = data.unwrap();
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let duration = data.auth.lease_duration;
let renewable = data.auth.renewable;
self.token.set_renewable(renewable);
self.token.set_start(current_time);
self.token.set_duration(duration);
Ok(())
}
}
impl Session {
pub fn new(role_id: String, secret_id: String) -> Result<Session, Error> {
let approle = ApproleLogin { role_id, secret_id };
Ok(Session {
approle,
token: internals::TokenContainer::new(),
})
}
}