diff --git a/src/token_management.rs b/src/token_management.rs new file mode 100644 index 0000000000000000000000000000000000000000..fc37f9354bd9d82e5c61103b9c1f51b7194060c2 --- /dev/null +++ b/src/token_management.rs @@ -0,0 +1,71 @@ +use chrono::{ + prelude::{SecondsFormat, Utc}, + DateTime, +}; +use rand::Rng; +use serenity::model::user::User; +use std::str; + +lazy_static! { + static ref key: [u8; 32] = rand::thread_rng().gen::<[u8; 32]>(); +} + +fn encrypt(plaintext: &str) -> &str { + return plaintext; +} +fn decrypt(ciphertext: &str) -> &str { + return ciphertext; +} + +pub fn generate_token<'a>(discord_user: &User, username: &str) -> String { + // if username doesn't exist : throw error + let timestamp = Utc::now().to_rfc3339(); + let payload = format!( + "{},{},{}", + timestamp, + discord_user.id.0.to_string(), + username + ); + info!("Token generated for {}: {}", discord_user.name, &payload); + encrypt(&payload).to_string() +} + +#[derive(Debug)] +pub enum TokenError { + DiscordIdMismatch, + TokenExpired, +} +impl std::fmt::Display for TokenError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +pub fn parse_token(discord_user: &User, token: &str) -> Result<String, TokenError> { + let token_components: Vec<_> = decrypt(token).splitn(3, ',').collect(); + info!( + "Verification attempt from '{}'(uid: {}) for account '{}' with token from {}", + discord_user.name, token_components[1], token_components[2], token_components[0] + ); + let token_timestamp = + DateTime::parse_from_rfc3339(token_components[0]).expect("Invalid date format"); + let token_discord_user = token_components[1]; + let token_username = token_components[2]; + if token_discord_user != discord_user.id.0.to_string() { + warn!("... attempt failed : DiscordID mismatch"); + return Err(TokenError::DiscordIdMismatch); + } + let time_delta_seconds = Utc::now().timestamp() - token_timestamp.timestamp(); + if time_delta_seconds > 5 * 60 { + warn!( + "... attempt failed : token expired ({} seconds old)", + time_delta_seconds + ); + return Err(TokenError::TokenExpired); + } + info!( + "... verification successful (token {} seconds old)", + time_delta_seconds + ); + return Ok(token_username.to_owned()); +} diff --git a/src/user_management.rs b/src/user_management.rs index 830f9640b274a2746e9ccb2288f3c847a6b56d8d..0ba3f2e114697638782e2752ef9f6feb1bb4ef3c 100644 --- a/src/user_management.rs +++ b/src/user_management.rs @@ -6,6 +6,7 @@ use serenity::{ }; use crate::config::CONFIG; +use crate::token_management::*; macro_rules! e { ($error: literal, $x:expr) => { @@ -59,48 +60,63 @@ impl Commands { ); return; } - // token stuff + e!( + "Error sending message: {:?}", + // TODO convert to email + msg.channel_id + .say(&ctx.http, generate_token(&msg.author, name)) + ); e!("Error deleting register message: {:?}", msg.delete(ctx)); } pub fn verify(ctx: Context, msg: Message, content: &str) { let token = content; - // if token is valid - e!( - "Unable to get member: {:?}", - serenity::model::id::GuildId(CONFIG.server_id) - .member(ctx.http.clone(), msg.author.id) - .map(|mut member| { - e!( - "Unable to remove role: {:?}", - member.remove_role(&ctx.http, CONFIG.unregistered_member_role) - ); - e!( - "Unable to edit nickname: {:?}", - member - .edit(&ctx.http, |m| { - let mut rng = rand::thread_rng(); - m.nickname(format!( - "{}, {}", - name, - [ - "The Big Cheese", - "The One and Only", - "The Exalted One", - "not to be trusted", - "The Scoundrel", - "A big fish in a small pond", - ][rng.gen_range(0, 5)] - )); - m - }) - .map(|()| { - e!( - "Unable to add role: {:?}", - member.add_role(&ctx.http, CONFIG.registered_member_role) - ); - }) - ); - }) - ); + match parse_token(&msg.author, content) { + Ok(name) => { + e!( + "Unable to get member: {:?}", + serenity::model::id::GuildId(CONFIG.server_id) + .member(ctx.http.clone(), msg.author.id) + .map(|mut member| { + e!( + "Unable to remove role: {:?}", + member.remove_role(&ctx.http, CONFIG.unregistered_member_role) + ); + e!( + "Unable to edit nickname: {:?}", + member.edit(&ctx.http, |m| { + let mut rng = rand::thread_rng(); + m.nickname(format!( + "{}, {}", + name, + [ + "The Big Cheese", + "The One and Only", + "The Exalted One", + "not to be trusted", + "The Scoundrel", + "A big fish in a small pond", + ][rng.gen_range(0, 5)] + )); + m + }) + ); + let new_msg = msg + .channel_id + .say(&ctx.http, "Verification succesful") + .expect("Error sending message"); + e!( + "Error deleting register message: {:?}", + new_msg.delete(&ctx) + ); + }) + ); + } + Err(reason) => e!( + "Error sending message: {:?}", + msg.channel_id + .say(&ctx.http, format!("Verification error: {:?}", reason)) + ), + } + e!("Error deleting register message: {:?}", msg.delete(&ctx)); } }