feat: initial working pwd reset method

This commit is contained in:
Zhongheng Liu 2026-01-31 01:00:08 +01:00
commit c8856ddeb1
Signed by: steven
GPG key ID: 805A28B071DAD84B
8 changed files with 3246 additions and 0 deletions

0
src/auth.rs Normal file
View file

45
src/main.rs Normal file
View file

@ -0,0 +1,45 @@
mod auth;
mod provider;
mod request_types;
#[macro_use] extern crate rocket;
use std::env;
use rocket::serde::json::Json;
use rocket::{Error, State};
use rocket::http::Status;
use crate::provider::{PurelymailProvider, SMTPUserCredentials};
use crate::request_types::PasswordResetRequest;
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
#[get("/onboard/<token>")]
fn onboard(token: String) -> String {
todo!()
}
#[post("/doResetPassword", data = "<data>")]
async fn user(data: Json<PasswordResetRequest>, provider: &State<PurelymailProvider>) -> Result<String, Status> {
provider.reset_password(&data.email, &data.new_password).await
.map(|_| { String::from("Good") })
.or(Err(Status::BadRequest))
}
#[launch]
fn rocket() -> _ {
let token = env::var("API_TOKEN").expect("API_TOKEN env var not set");
let smtp_webmaster_username = env::var("WEBMASTER_USERNAME")
.expect("WEBMASTER_USERNAME env var not set");
let smtp_webmaster_password = env::var("WEBMASTER_PASSWORD")
.expect("WEBMASTER_PASSWORD env var not set");
let provider: PurelymailProvider = PurelymailProvider::new(
token,
SMTPUserCredentials::new(&smtp_webmaster_username, &smtp_webmaster_password)
);
rocket::build()
.manage(provider)
.mount("/", routes![index, onboard, user])
}

90
src/provider.rs Normal file
View file

@ -0,0 +1,90 @@
use reqwest::{Body, Client, RequestBuilder};
use serde::{Deserialize, Serialize};
pub struct SMTPUserCredentials {
username: String,
password: String,
}
impl SMTPUserCredentials {
pub fn new(username: &str, password: &str) -> SMTPUserCredentials {
SMTPUserCredentials {
username: username.to_string(),
password: password.to_string(),
}
}
}
pub struct PurelymailProvider {
token: String,
webmaster: SMTPUserCredentials
}
#[derive(Debug, Deserialize)]
struct UserListResponse {
r#type: String,
result: UserList
}
#[derive(Debug, Deserialize)]
struct UserList {
users: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserUpdateRequest {
pub user_name: String,
pub new_user_name: Option<String>,
pub new_password: String,
pub enable_search_indexing: bool,
pub enable_password_reset: bool,
pub require_two_factor_authentication: bool,
}
impl From<UserUpdateRequest> for Body {
fn from(user: UserUpdateRequest) -> Body {
let bytes = serde_json::to_vec(&user).expect("Failed to serialize UserUpdateRequest");
Body::from(bytes)
}
}
impl PurelymailProvider {
pub fn new(token: String, webmaster_cred: SMTPUserCredentials) -> PurelymailProvider {
PurelymailProvider { token, webmaster: webmaster_cred }
}
fn dispatch_onboarding_email() {
todo!()
}
fn with_authorize(&self, req: RequestBuilder) -> RequestBuilder {
req.header("Purelymail-Api-Token", &self.token)
}
pub async fn reset_password(&self, email_addr: &str, new_password: &str) -> Result<(), &str> {
println!("Resetting password");
let client = Client::new();
let r = self.with_authorize(
client.post("https://purelymail.com/api/v0/listUser")
).body("{}").send().await.expect("Failed to reset password");
let res = r.json::<UserListResponse>()
.await.expect("TODO: user list bad");
if res.r#type != "success" {
return Err("The API returned an error");
}
let users = res.result.users;
if !users.contains(&String::from(email_addr)) {
return Err("The user does not exist");
}
let r = self.with_authorize(
client.post("https://purelymail.com/api/v0/modifyUser")
).body(UserUpdateRequest {
user_name: email_addr.to_string(),
new_user_name: None,
new_password: new_password.to_string(),
enable_search_indexing: false,
enable_password_reset: false,
require_two_factor_authentication: false,
}).send().await;
if r.is_err() {
return Err("The API returned an error");
}
Ok(())
}
}

7
src/request_types.rs Normal file
View file

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct PasswordResetRequest {
pub email: String,
pub new_password: String,
}