endpoint to update pronouns using authentication

main
aprzn 1 year ago
parent 41e80ca508
commit df3cf2e8a0

@ -1,14 +1,25 @@
mod tsar; mod tsar;
mod security;
use std::{env, net::{IpAddr, SocketAddr}}; use std::{env, net::{IpAddr, SocketAddr}};
use axum::{ use axum::{
routing::get, routing::{get, post},
Router, Router,
extract::{Path, State}, http::StatusCode, extract::{Path, State}, http::StatusCode, Json,
}; };
use rand::{ thread_rng, seq::IteratorRandom }; use rand::{thread_rng, seq::IteratorRandom};
use security::salt_and_hash;
use serde::Deserialize;
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions}; use sqlx::{SqlitePool, sqlite::SqlitePoolOptions};
use base64::prelude::*;
#[derive(Deserialize)]
struct SetPronounsRequest {
password: String,
new_pronouns: String,
}
#[tokio::main] #[tokio::main]
@ -31,6 +42,7 @@ async fn main() {
let app = Router::new() let app = Router::new()
.route("/api/thethirdcan/hello", get(|| async { "hello world!" })) .route("/api/thethirdcan/hello", get(|| async { "hello world!" }))
.route("/api/thethirdcan/pronouns/:user", get(user_pronouns)) .route("/api/thethirdcan/pronouns/:user", get(user_pronouns))
.route("/api/thethirdcan/pronouns/:user", post(set_pronouns))
.with_state(pool); .with_state(pool);
axum::Server::bind(&SocketAddr::new(IpAddr::from([0;4]), port)) axum::Server::bind(&SocketAddr::new(IpAddr::from([0;4]), port))
@ -46,7 +58,7 @@ async fn user_pronouns(
) -> Result<String, StatusCode> { ) -> Result<String, StatusCode> {
sqlx::query!("SELECT pronouns sqlx::query!("SELECT pronouns
FROM users FROM users
WHERE username == ?", WHERE username = ?",
user) user)
.fetch_one(pool) .fetch_one(pool)
.await .await
@ -61,3 +73,29 @@ async fn user_pronouns(
.map(ToOwned::to_owned) .map(ToOwned::to_owned)
.ok_or(StatusCode::INTERNAL_SERVER_ERROR) .ok_or(StatusCode::INTERNAL_SERVER_ERROR)
} }
async fn set_pronouns(
State(pool): State<&SqlitePool>,
Path(user): Path<String>,
Json(payload): Json<SetPronounsRequest>,
) -> Result<(), StatusCode> {
let password_data = BASE64_URL_SAFE.decode(payload.password).map_err(|_| StatusCode::BAD_REQUEST)?;
let salted_hashed_password = salt_and_hash(&password_data);
let auth_slice = salted_hashed_password.as_ref();
let rows_affected = sqlx::query!("UPDATE users
SET pronouns = ?
WHERE username = ? AND auth = ?
", payload.new_pronouns, user, auth_slice)
.execute(pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.rows_affected();
if rows_affected == 0 {
return Err(StatusCode::NOT_FOUND);
}
Ok(())
}

@ -0,0 +1,8 @@
use sha2::{Sha256, Digest};
pub fn salt_and_hash(input: &[u8]) -> Box<[u8]> {
let mut hasher = Sha256::new();
hasher.update(input);
hasher.update("get salted on lmao");
hasher.finalize().as_slice().into()
}

@ -5,9 +5,10 @@ use base64::prelude::*;
use reqwest::Client; use reqwest::Client;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use sha2::{Sha256, Digest};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::security::salt_and_hash;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct LoginInfo { struct LoginInfo {
#[serde(rename = "user")] #[serde(rename = "user")]
@ -160,7 +161,7 @@ async fn handle_event(pool: &SqlitePool, login: &LoginInfo, thread: &MessageThre
let sid = thread.sid; let sid = thread.sid;
let mid = event.mid; let mid = event.mid;
let Some(uid) = thread.members.iter().find(|member| member.mid == mid).and_then(|member| member.uid) else { return Err(()) }; let Some(uid) = thread.members.iter().find(|member| member.mid == mid).and_then(|member| member.uid) else { return Err(()) };
let Some(username) = res.users.iter().find(|user| user.id == uid).map(|user| &user.name) else { return Err(()) }; let Some(username) = res.users.iter().find(|user| user.id == uid).map(|user| &user.key) else { return Err(()) };
debug!("handling '{text}' from {username} in thread {sid}"); debug!("handling '{text}' from {username} in thread {sid}");
@ -172,16 +173,10 @@ Note: this will remove your previous authentication code, if you had one."#.into
} else if text.starts_with("get_authentication") { } else if text.starts_with("get_authentication") {
let key_data: [u8; 16] = thread_rng().gen(); let key_data: [u8; 16] = thread_rng().gen();
let b64_key = BASE64_URL_SAFE.encode(key_data); let b64_key = BASE64_URL_SAFE.encode(key_data);
let hashed_key = { let hashed_key = salt_and_hash(&key_data);
let mut hasher = Sha256::new(); let hashed_key_slice = hashed_key.as_ref();
hasher.update(key_data);
hasher.update("get salted on lmao");
hasher.finalize()
};
let hashed_key_slice = hashed_key.as_slice();
let user_in_db = sqlx::query!("SELECT username FROM users WHERE username == ?;", username) let user_in_db = sqlx::query!("SELECT username FROM users WHERE username = ?;", username)
.fetch_optional(pool) .fetch_optional(pool)
.await .await
.map_err(|e| error!("failed to search database for username: {e}"))? .map_err(|e| error!("failed to search database for username: {e}"))?
@ -190,7 +185,7 @@ Note: this will remove your previous authentication code, if you had one."#.into
if user_in_db { if user_in_db {
sqlx::query!("UPDATE users sqlx::query!("UPDATE users
SET auth = ? SET auth = ?
WHERE username == ?; WHERE username = ?;
", hashed_key_slice, username) ", hashed_key_slice, username)
.execute(pool) .execute(pool)
.await .await

Loading…
Cancel
Save