From 47ead0febfb6b28ef698ef91dbc72a0986a81f08 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 17 May 2021 02:00:31 -0400 Subject: [PATCH] random tokens --- migrations/2021-05-15-071534_users/up.sql | 2 +- src/endpoints/users/create_account.rs | 124 +++++++++++++--------- src/main.rs | 1 + src/util/mod.rs | 1 + src/util/random.rs | 30 ++++++ 5 files changed, 105 insertions(+), 53 deletions(-) create mode 100644 src/util/mod.rs create mode 100644 src/util/random.rs diff --git a/migrations/2021-05-15-071534_users/up.sql b/migrations/2021-05-15-071534_users/up.sql index 2f295ebd9d..bd9c6d9a07 100644 --- a/migrations/2021-05-15-071534_users/up.sql +++ b/migrations/2021-05-15-071534_users/up.sql @@ -56,7 +56,7 @@ CREATE TABLE users ( ) NOT NULL DEFAULT 'light-mode', -- An uploaded avatar. Public hash in our bucket. - avatar_public_bucket_hash CHAR(32) NOT NULL UNIQUE, + avatar_public_bucket_hash CHAR(32) NOT NULL, -- If the user doesn't want to use gravatar and doesn't have an uploaded avatar. disable_gravatar BOOLEAN NOT NULL DEFAULT false, diff --git a/src/endpoints/users/create_account.rs b/src/endpoints/users/create_account.rs index 3ffd9e16b2..d85f853bb7 100644 --- a/src/endpoints/users/create_account.rs +++ b/src/endpoints/users/create_account.rs @@ -1,23 +1,27 @@ -use actix_web::{Responder, web, HttpResponse, error}; -use derive_more::{Display, Error}; -use actix_web::dev::HttpResponseBuilder; -use actix_web::http::StatusCode; -use actix_http::http::header; use actix_http::Error; +use actix_http::http::header; +use actix_web::dev::HttpResponseBuilder; use actix_web::error::ResponseError; +use actix_web::http::StatusCode; +use actix_web::{Responder, web, HttpResponse, error}; use crate::endpoints::users::create_account::CreateAccountError::BadInput; +use derive_more::{Display, Error}; +use log::{info, warn, log}; +use crate::server_state::ServerState; + +const NEW_USER_ROLE: &'static str = "new-user"; #[derive(Deserialize)] pub struct CreateAccountRequest { pub username: String, pub password: String, pub password_confirmation: String, + pub email_address: String, } #[derive(Serialize)] -pub struct CreateAccountResponse { +pub struct CreateAccountSuccessResponse { pub success: bool, - } #[derive(Serialize)] @@ -26,35 +30,29 @@ pub struct ErrorResponse { pub error_reason: String, } -#[derive(Debug, Display, Error)] -pub enum CreateAccountResponseError { - #[display(fmt = "internal error")] - InternalError, - - #[display(fmt = "bad request")] - BadClientData, - - #[display(fmt = "timeout")] - Timeout, -} - - #[derive(Debug, Display)] pub enum CreateAccountError { BadInput(String), + ServerError, } impl ResponseError for CreateAccountError { fn status_code(&self) -> StatusCode { match *self { CreateAccountError::BadInput(_) => StatusCode::BAD_REQUEST, + CreateAccountError::ServerError => StatusCode::INTERNAL_SERVER_ERROR, } } fn error_response(&self) -> HttpResponse { + let error_reason = match self { + BadInput(reason) => reason.to_string(), + CreateAccountError::ServerError => "server error".to_string(), + }; + let response = ErrorResponse { success: false, - error_reason: "there was a problem".to_string() + error_reason, }; let body = match serde_json::to_string(&response) { @@ -68,47 +66,69 @@ impl ResponseError for CreateAccountError { } } -impl ResponseError for CreateAccountResponseError { - fn status_code(&self) -> StatusCode { - match *self { - CreateAccountResponseError::InternalError => StatusCode::INTERNAL_SERVER_ERROR, - CreateAccountResponseError::BadClientData => StatusCode::BAD_REQUEST, - CreateAccountResponseError::Timeout => StatusCode::GATEWAY_TIMEOUT, - } - } - - fn error_response(&self) -> HttpResponse { - HttpResponseBuilder::new(self.status_code()) - .set_header(header::CONTENT_TYPE, "text/html; charset=utf-8") - .body(self.to_string()) - } -} - - -pub async fn create_account_handler(request: web::Json) - -> Result +pub async fn create_account_handler( + request: web::Json, + server_state: web::Data) -> Result { if request.username.len() < 3 { return Err(CreateAccountError::BadInput("username is too short".to_string())); } - /*let record_id = sqlx::query!( + if request.password.len() < 6 { + return Err(CreateAccountError::BadInput("password is too short".to_string())); + } + + if request.password != request.password_confirmation { + return Err(CreateAccountError::BadInput("passwords do not match".to_string())); + } + + if !request.email_address.contains("@") { + return Err(CreateAccountError::BadInput("invalid email address".to_string())); + } + + let token = "token"; + let password_hash = "temp"; + let profile_markdown = ""; + let profile_rendered_html = ""; + let ip_address = "1.1.1.1"; + + let record_id = sqlx::query!( r#" -INSERT INTO badges ( slug, title, description, image_url ) -VALUES ( ?, ?, ?, ? ) +INSERT INTO users ( + token, + username, + display_name, + email_address, + profile_markdown, + profile_rendered_html, + user_role_slug, + password_hash, + ip_address_creation, + ip_address_last_login, + ip_address_last_update +) +VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) "#, - self.slug, - self.title, - self.description, - self.image_url, + token.to_string(), + request.username.to_string(), + request.username.to_string(), + request.email_address.to_string(), + profile_markdown, + profile_rendered_html, + NEW_USER_ROLE, + password_hash, + ip_address.to_string(), + ip_address.to_string(), + ip_address.to_string(), ) - .execute(pool) - .await? + .execute(&server_state.mysql_pool) + .await + .map_err(|_| CreateAccountError::ServerError)? .last_insert_id(); - Ok(record_id)*/ - + info!("new user id: {}", record_id); + //Ok(record_id) /*Ok(web::Json())*/ if true { @@ -118,7 +138,7 @@ VALUES ( ?, ?, ?, ? ) // .map_err(error::ErrorInternalServerError)?; // error::ErrorBadRequest(msg) - let response = CreateAccountResponse { + let response = CreateAccountSuccessResponse { success: true, }; diff --git a/src/main.rs b/src/main.rs index fb937d1ad2..a8e0862418 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ pub mod server_state; pub mod endpoints; pub mod queries; +pub mod util; use actix_cors::Cors; use actix_http::http; diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000000..b9f20e6fd8 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1 @@ +pub mod random; \ No newline at end of file diff --git a/src/util/random.rs b/src/util/random.rs new file mode 100644 index 0000000000..79d448a61e --- /dev/null +++ b/src/util/random.rs @@ -0,0 +1,30 @@ +use rand::Rng; + +// Crockford characters +const CROCKFORD_CHARSET: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ"; + +pub fn random_token(length: usize) -> String { + let mut rng = rand::thread_rng(); + + let token: String = (0..length) + .map(|_| { + let idx = rng.gen_range(0..CROCKFORD_CHARSET.len()); + CROCKFORD_CHARSET[idx] as char + }) + .collect(); + + token +} + +#[cfg(test)] +mod tests { + use crate::util::random::random_token; + + #[test] + fn random_token_length() { + assert_eq!(random_token(0), "".to_string()); + assert_eq!(random_token(1).len(), 1); + assert_eq!(random_token(10).len(), 10); + assert_eq!(random_token(32).len(), 32); + } +}