use serde_derive::{Serialize, Deserialize};
use chrono::prelude::*;
use sled::Db;
use secrecy::{Secret, ExposeSecret};
use zeroize::Zeroize;
use crate::{Role, Target, secrets, SGSecret, SGError};
fn sg_auth() -> &'static str {
"./SchemeGuardianDB/SG_AUTH"
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthPayload<R> {
role: Role<R>,
target: Target,
lease: Lease,
random_key: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthEngine<R> {
bearer: SGSecret,
payload: AuthPayload<R>,
}
impl<'e, R> AuthEngine<R> where R: std::fmt::Debug + std::cmp::PartialEq + std::cmp::Eq + serde::Serialize + serde::de::DeserializeOwned {
pub fn new() -> Self {
Self {
bearer: Default::default(),
payload: AuthPayload {
role: Default::default(),
target: Default::default(),
lease: Default::default(),
random_key: secrets::random64alpha().expose_secret().to_owned(),
}
}
}
pub fn bearer(mut self, bearer: Secret<String>) -> Self {
self.bearer = SGSecret(bearer.expose_secret().clone().to_owned());
self
}
pub fn role(mut self, role: Role<R>) -> Self {
self.payload.role = role;
self
}
pub fn expiry(mut self, expiry: chrono::Duration) -> Self {
self.payload.lease = Lease::DateExpiry(Utc::now() + expiry);
self
}
pub fn target(mut self, attr: Target) -> Self {
self.payload.random_key = secrets::random64alpha().expose_secret().to_owned();
self.payload.target = attr;
self
}
pub fn insert(self) -> Result<(custom_codes::DbOps, Secret<String>, Option<AuthPayload<R>>), SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let key = bincode::serialize(&self.bearer.0)?;
let value = bincode::serialize::<AuthPayload<R>>(&self.payload)?;
let dbop = db.insert(key, value)?;
let bearer_key = self.bearer.0.clone() + ":::" + &self.payload.random_key;
if let Some(updated) = dbop {
let data = bincode::deserialize::<AuthPayload<R>>(&updated)?;
Ok((custom_codes::DbOps::Modified, Secret::new(bearer_key), Some(data)))
}else {
Ok((custom_codes::DbOps::Inserted, Secret::new(bearer_key), None))
}
}
pub fn issue(self) -> Result<(custom_codes::DbOps, SGSecret, Option<AuthPayload<R>>), SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let key = bincode::serialize(&self.bearer.0)?;
let value = bincode::serialize::<AuthPayload<R>>(&self.payload)?;
let dbop = db.insert(key, value)?;
let raw_key = self.bearer.0.clone() + ":::" + &self.payload.random_key;
let bearer_key = crate::secrets::branca_encode(Secret::new(raw_key))?;
if let Some(updated) = dbop {
let data = bincode::deserialize::<AuthPayload<R>>(&updated)?;
Ok((custom_codes::DbOps::Modified, bearer_key, Some(data)))
}else {
Ok((custom_codes::DbOps::Inserted, bearer_key, None))
}
}
pub fn get(self, raw_key: Secret<String>) -> Result<(custom_codes::DbOps, Option<Payload<R>>), SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let raw_key = raw_key.expose_secret();
let dual = raw_key.split(":::").collect::<Vec<&str>>();
let key = bincode::serialize(dual[0])?;
let check_key = db.get(key)?;
if let Some(binary) = check_key {
let data = bincode::deserialize::<AuthPayload<R>>(&binary)?;
Ok((custom_codes::DbOps::KeyFound, Some((data.role, data.target))))
}else {
Ok((custom_codes::DbOps::KeyNotFound, None))
}
}
pub fn authenticate(self, raw_key: Secret<String>) -> Result<(custom_codes::AccessStatus, Option<Payload<R>>), SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let raw_key = raw_key.expose_secret();
let dual = raw_key.split(":::").collect::<Vec<&str>>();
let key = bincode::serialize(dual[0])?;
let user_random_key = dual[1];
let check_key = db.get(key)?;
if let Some(binary) = check_key {
let payload = bincode::deserialize::<AuthPayload<R>>(&binary)?;
match payload.lease {
Lease::DateExpiry(datetime) => {
if Utc::now() > datetime {
Ok((custom_codes::AccessStatus::Expired, None))
}else {
if payload.random_key == user_random_key {
Ok((custom_codes::AccessStatus::Granted, Some((payload.role, payload.target))))
}else {
Ok((custom_codes::AccessStatus::RejectedRAC, None))
}
}
},
Lease::Lifetime => Ok((custom_codes::AccessStatus::Granted, Some((payload.role, payload.target)))),
_ => Ok((custom_codes::AccessStatus::Rejected, None))
}
}else {
Ok((custom_codes::AccessStatus::Rejected, None))
}
}
pub fn rm<'d>(self, raw_key: Secret<String>) -> Result<(custom_codes::DbOps, Option<FullPayload<R>>), SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let raw_key = raw_key.expose_secret();
let dual = raw_key.split(":::").collect::<Vec<&str>>();
let key = bincode::serialize(dual[0])?;
let check_key = db.remove(key)?;
if let Some(binary) = check_key {
let data = bincode::deserialize::<AuthPayload<R>>(&binary)?;
Ok((custom_codes::DbOps::Deleted, Some((data.role, data.lease, data.target, data.random_key))))
}else {
Ok((custom_codes::DbOps::KeyNotFound, None))
}
}
pub fn list_keys(self) -> Result<Vec<u8>, SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let mut sled_vec = vec![];
db.iter().keys().for_each(|data| {
if let Ok(inner) = data {
sled_vec.push(inner);
}else {
sled_vec.clear();
}
});
Ok(vec![])
}
pub fn list_values(self) -> Result<Vec<u8>, SGError> {
let auth_db = sg_auth();
let db = Db::open(auth_db)?;
let mut sled_vec = vec![];
db.iter().values().for_each(|data| {
if let Ok(inner) = data {
sled_vec.push(inner);
}else {
sled_vec.clear();
}
});
Ok(vec![])
}
}
pub type FullPayload<R> = (Role<R>, Lease, Target, String);
pub type Payload<R> = (Role<R>, Target);
#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone, Eq)]
pub enum Lease {
DateExpiry(DateTime<Utc>),
Lifetime,
OnDownload,
OnUpload,
OnDisconnection,
UnSpecified,
}
impl Default for Lease {
fn default() -> Self{ Lease::DateExpiry(Utc::now() + chrono::Duration::hours(24)) }
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Zeroize)]
#[zeroize(drop)]
pub enum SecretType {
Cookie,
Branca,
TOTP,
USBkey,
NFC,
Fingerprint,
Iris,
IRscan,
Noise,
TlsCert,
DMARC,
DANE,
Passphrase,
BluetoothPairKey,
Bearer,
CustomToken,
ApiKey,
EmailAddr,
BlockchainTx,
QrCode,
Barcode,
HardwareMacAddr,
IpAddress,
Subnet,
Port,
UnSpecified,
}