🏡 index : old_projects/mc-mailer.git


use openssl::{hash::*};
use rand::{thread_rng, Rng};
use base64::engine::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD as BASE64;
use chrono::prelude::*;
use serde::{Serialize, Deserialize};
use crate::secrets::SPECIAL_SALT_CODE;

#[derive(Debug, Serialize, Deserialize)]
pub struct VerificationRequestProto {
    pub(crate) email: String,
    pub(crate) minecraft_username: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WrittenReasonRequest {
    pub(crate) email: String,
    pub(crate) minecraft_username: String,
    pub(crate) reason: String,
}

#[derive(Debug)]
pub struct VerificationRequest {
    pub(crate) email: String,
    pub(crate) minecraft_username: String,
    pub(crate) date: DateTime<Utc>,
    pub(crate) seed: u32,
}

#[derive(Debug)]
pub struct VerificationReceipt {
    pub(crate) valid: bool,
    pub(crate) minecraft_username: String,
}

pub trait VerificationCodeGenerator {
    fn as_code(&self) -> String;
}

pub trait VerificationCodeValidator {
    fn from_code(code: String) -> VerificationReceipt;
}

pub trait VerificationCodeHydrator {
    fn hydrate(&self) -> VerificationRequest;
}

impl VerificationCodeGenerator for VerificationRequest {
    fn as_code(&self) -> String {
        println!("generating verification code from request: {:?}", self.clone());
        return format!(
                "{}{}{}",
                BASE64.encode(
                    &self.seed.to_be_bytes()
                ),
                BASE64.encode(
                    hash(MessageDigest::sha256(),
                    format!(
                        "{}{}{}{}",
                        self.seed,
                        self.date.format("%Y%m%d").to_string(),
                        SPECIAL_SALT_CODE,
                        self.minecraft_username
                    ).as_bytes()).unwrap().as_ref()
                ),
                BASE64.encode(
                    &self.minecraft_username.as_bytes()
                )
        );
    }
}

impl VerificationCodeValidator for VerificationReceipt {
    fn from_code(code: String) -> VerificationReceipt {
        println!("code: {}", code);
        println!("reconstructed username: {}", String::from_utf8(BASE64.decode(&code[49..]).unwrap()).unwrap());
        println!("reconstructed seed: {}", u32::from_be_bytes(BASE64.decode(&code[0..6]).unwrap().try_into().unwrap()));
        let username = String::from_utf8(BASE64.decode(&code[49..]).unwrap()).unwrap();
        return VerificationReceipt {
            minecraft_username: username.clone(),
            valid: code == VerificationRequest {
                email: String::from(""),
                minecraft_username: username,
                date: Utc::now(),
                seed: u32::from_be_bytes(BASE64.decode(&code[0..6]).unwrap().try_into().unwrap()),
            }.as_code()
        };
    }
}

impl VerificationCodeHydrator for VerificationRequestProto {
    fn hydrate(&self) -> VerificationRequest {
        return VerificationRequest {
            email: self.email.clone(),
            minecraft_username: self.minecraft_username.clone(),
            date: Utc::now(),
            seed: rand::thread_rng().gen::<u32>(),
        };
    }
}