Restructure code, allow for order modification

This commit is contained in:
~erin 2021-11-21 21:47:19 -05:00
parent aa6dc76570
commit 9df07bf3db
No known key found for this signature in database
GPG Key ID: DA70E064A8C70F44
7 changed files with 345 additions and 148 deletions

235
Cargo.lock generated
View File

@ -14,6 +14,60 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aead"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
dependencies = [
"generic-array",
]
[[package]]
name = "aes"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
"aes-soft",
"aesni",
"cipher",
]
[[package]]
name = "aes-gcm"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "aes-soft"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
"cipher",
"opaque-debug",
]
[[package]]
name = "aesni"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
"cipher",
"opaque-debug",
]
[[package]]
name = "ahash"
version = "0.7.6"
@ -25,15 +79,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -157,11 +202,20 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174"
dependencies = [
"crypto-mac",
"crypto-mac 0.8.0",
"digest",
"opaque-debug",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.8.0"
@ -224,6 +278,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
"generic-array",
]
[[package]]
name = "color_quant"
version = "1.1.0"
@ -242,11 +305,32 @@ version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d"
dependencies = [
"aes-gcm",
"base64",
"hkdf",
"percent-encoding",
"rand 0.8.4",
"sha2",
"subtle",
"time",
"version_check",
]
[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
"libc",
]
[[package]]
name = "cpuid-bool"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
[[package]]
name = "crc32fast"
version = "1.2.1"
@ -310,6 +394,25 @@ dependencies = [
"subtle",
]
[[package]]
name = "crypto-mac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "ctr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
dependencies = [
"cipher",
]
[[package]]
name = "deflate"
version = "0.8.6"
@ -383,19 +486,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fallible_collections"
version = "0.4.3"
@ -595,6 +685,16 @@ dependencies = [
"wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
name = "ghash"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "gif"
version = "0.11.3"
@ -648,6 +748,26 @@ dependencies = [
"libc",
]
[[package]]
name = "hkdf"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
dependencies = [
"digest",
"hmac",
]
[[package]]
name = "hmac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
dependencies = [
"crypto-mac 0.10.1",
"digest",
]
[[package]]
name = "hound"
version = "3.4.0"
@ -688,12 +808,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.14"
@ -998,6 +1112,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "paris"
version = "1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4503608d3670c54b6dd45e47744545a6ee8d56226cfe5e0446c7ba4a08d8b9fc"
[[package]]
name = "parking_lot"
version = "0.11.2"
@ -1070,8 +1190,7 @@ dependencies = [
"argon2",
"bincode",
"captcha",
"env_logger",
"log",
"paris",
"rand 0.8.4",
"rand_core 0.6.3",
"rocket",
@ -1104,6 +1223,17 @@ dependencies = [
"miniz_oxide 0.3.7",
]
[[package]]
name = "polyval"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
dependencies = [
"cpuid-bool",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.15"
@ -1294,8 +1424,6 @@ version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
@ -1506,6 +1634,19 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
[[package]]
name = "sha2"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"opaque-debug",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -1675,15 +1816,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "thread_local"
version = "1.1.3"
@ -1940,6 +2072,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "uuid"
version = "0.8.2"
@ -2054,15 +2196,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = { version = "0.5.0-rc.1", features = ["json"] }
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
serde = "1.0.130"
bincode = "1.3.3"
sled = "0.34.7"
@ -15,5 +15,6 @@ rand_core = { version = "0.6", features = ["std"] }
uuid = { version = "0.8", features = ["serde", "v5"] }
rand = "0.8.4"
captcha = "0.0.8"
log = "0.4.0"
env_logger = "0.9.0"
#log = "0.4.0"
#env_logger = "0.9.0"
paris = { version = "1.5", features = ["macros"] }

View File

@ -1,18 +1,14 @@
use rocket::serde::json::Json;
use rocket::{form::Form, State};
use rocket::{form::Form, http::CookieJar, State};
use rand::Rng;
use uuid::Uuid;
use crate::authenticate::*;
use crate::database::*;
use crate::structures::*;
use argon2::{
password_hash::{PasswordHasher, SaltString},
Argon2,
};
use std::env;
use argon2::{password_hash::SaltString, Argon2};
// Endpoint to add a new product
#[post("/api/new", data = "<request>")]
@ -24,18 +20,7 @@ pub fn new_product(
) -> Json<Status> {
/* Need to rework the password mechanism so it's not stored in plaintext serverside */
let password = request.password.as_bytes(); // Turn the provided passord into a bytes array
let hashed_password = argon2
.hash_password(password, &salt.as_ref())
.unwrap()
.to_string(); // Hash the password
let mut admin_password = env::var("ADMIN_PASSWD").unwrap(); // Get the set admin password from environment variables
admin_password = argon2
.hash_password(admin_password.as_bytes(), &salt.as_ref())
.unwrap()
.to_string(); // Hash the admin password
if hashed_password == admin_password {
if check_password(&request.password, &salt, &argon2) {
// Check if the password is correct
let new_product: Product = Product {
// Create a new Product object from the supplied info
@ -61,7 +46,7 @@ pub fn new_product(
// Endpoint to place a new order
#[post("/api/order", data = "<order>")]
pub fn new_order(order: Form<OrderRequest<'_>>) -> String {
pub fn new_order(cookies: &CookieJar<'_>, order: Form<OrderRequest<'_>>) -> String {
let mut rng = rand::thread_rng(); // Get a new random number
let to_hash = format!(
"{}{}{}",
@ -69,63 +54,107 @@ pub fn new_order(order: Form<OrderRequest<'_>>) -> String {
order.encryption_key.to_string(),
rng.gen::<f64>().to_string()
); // Create a has from various components of the order + a random number
let db_order: Order = Order {
name: order.name.to_string(),
message: order.user_message.to_string(),
encryption_key: order.encryption_key.to_string(),
email: order.email.to_string(),
payment_type: order.payment_type.clone(),
uuid: Uuid::new_v5(&Uuid::NAMESPACE_X500, to_hash.as_bytes()),
status: OrderStatus::Submitted,
};
make_order(&db_order);
format!(
"Thank you for ordering from Catgirl Pharmacy!\nYour order Uuid is {}",
db_order.uuid.to_hyphenated().to_string()
)
let user_token = cookies.get_private("token").unwrap();
if user_token.value() == order.captcha {
let db_order: Order = Order {
name: order.name.to_string(),
message: order.user_message.to_string(),
encryption_key: order.encryption_key.to_string(),
email: order.email.to_string(),
payment_type: order.payment_type.clone(),
uuid: Uuid::new_v5(&Uuid::NAMESPACE_X500, to_hash.as_bytes()),
status: OrderStatus::Submitted,
};
make_order(&db_order);
return format!(
"Thank you for ordering from Catgirl Pharmacy!\nYour order Uuid is {}",
db_order.uuid.to_hyphenated().to_string()
);
} else {
return "Incorrect CAPTCHA answer.\nPlease try again!".to_string();
}
}
// Modify an order
#[post("/api/order/update", data = "<update>")]
pub fn update_order(
argon2: &State<Argon2>,
salt: &State<SaltString>,
update: Form<OrderUpdate<'_>>,
) -> Json<Status> {
if check_password(&update.password, &salt, &argon2) {
let mut new_order = read_order(&update.order_uuid).unwrap().unwrap();
new_order.status = update.status;
new_order.payment_type = update.payment_type;
make_order(&new_order);
return Json(Status {
status: "success".to_string(),
reason: "meowy".to_string(),
});
} else {
return Json(Status {
status: "fail".to_string(),
reason: "wrong password".to_string(),
});
}
}
// Get the details of a specific order from it's Uuid
#[get("/api/order/<uuid>")]
pub fn order_info(uuid: &str) -> Json<Order> {
let order = read_order(uuid).unwrap().unwrap();
return Json(order);
let order = read_order(uuid).unwrap().unwrap(); // Read the order from the database
return Json(order); // Serialize it into json & return
}
// Get all orders made
#[post("/api/orders", data = "<auth>")]
pub fn all_orders(
auth: Form<Authenticate>,
argon2: &State<Argon2>,
salt: &State<SaltString>,
) -> Result<Json<Vec<Order>>, Json<Status>> {
let entered_password = auth.password.as_bytes();
let hashed_password = argon2
.hash_password(entered_password, &salt.as_ref())
.unwrap()
.to_string();
let mut admin_password = env::var("ADMIN_PASSWD").unwrap();
admin_password = argon2
.hash_password(admin_password.as_bytes(), &salt.as_ref())
.unwrap()
.to_string();
if admin_password == hashed_password {
let all_orders = read_all_orders();
let mut return_string = String::new();
let mut orders_list: Vec<Order> = Vec::new();
if check_password(&auth.password, &salt, &argon2) {
let all_orders = read_all_orders(); // Read all orders from the database
let mut orders_list: Vec<Order> = Vec::new(); // Create a new vector of Order's
for x in all_orders {
return_string = format!("{}\n{:?}", return_string, x);
orders_list.push(x);
// Loop through all read orders
orders_list.push(x); // Push each entry to the vector
}
return Ok(Json(orders_list));
return Ok(Json(orders_list)); // Return the orders serialized as json
} else {
return Err(Json(Status {
// Fail on wrong password
status: "fail".to_string(),
reason: "wrong password".to_string(),
}));
}
}
// Delete an order
#[post("/api/delete/<order>", data = "<auth>")]
pub fn delete_order(
auth: Form<Authenticate>,
order: &str,
argon2: &State<Argon2>,
salt: &State<SaltString>,
) -> Json<Status> {
if check_password(&auth.password, &salt, &argon2) {
remove_order(&order).unwrap();
return Json(Status {
status: "success".to_string(),
reason: "deleted order".to_string(),
});
} else {
return Json(Status {
status: "fail".to_string(),
reason: "wrong password".to_string(),
});
}
}
// Update fields of a product
#[post("/api/update", data = "<update>")]
pub fn update_product(
db: &State<sled::Db>,
@ -133,29 +162,21 @@ pub fn update_product(
salt: &State<SaltString>,
update: Form<Update<'_>>,
) -> Json<Status> {
let password = update.password.as_bytes();
let hashed_password = argon2
.hash_password(password, &salt.as_ref())
.unwrap()
.to_string();
let mut admin_password = env::var("ADMIN_PASSWD").unwrap();
admin_password = argon2
.hash_password(admin_password.as_bytes(), &salt.as_ref())
.unwrap()
.to_string();
if admin_password == hashed_password {
if check_password(&update.password, &salt, &argon2) {
match update.field {
// Check what field is being updated
"price_usd" => {
let mut new_product = read_product(db, &update.product).unwrap().unwrap();
new_product.price_usd = update.value;
register_product(db, &update.product, &new_product);
let mut new_product = read_product(db, &update.product).unwrap().unwrap(); // Read the product from the database
new_product.price_usd = update.value; // Change the price_usd value to the provided value
register_product(db, &update.product, &new_product); // Update the product in the database
return Json(Status {
// Return a json success status
status: "success".to_string(),
reason: "price changed".to_string(),
});
}
"stock" => {
// Same as before, but with stock
let mut new_product = read_product(db, &update.product).unwrap().unwrap();
new_product.stock = update.value as u32;
register_product(db, &update.product, &new_product);
@ -165,33 +186,23 @@ pub fn update_product(
});
}
_ => {
// Fail because provided field wasn't found, return json
return Json(Status {
status: "fail".to_string(),
reason: "field not found".to_string(),
})
});
}
}
};
return Json(Status {
// Return JSON fail due to wrong password
status: "fail".to_string(),
reason: "wrong password".to_string(),
});
}
#[get("/api/stock/<product>")]
pub fn get_stock(db: &State<sled::Db>, product: &str) -> String {
read_product(db, &product)
.unwrap()
.unwrap()
.stock
.to_string()
}
#[get("/api/price/<product>")]
pub fn get_price(db: &State<sled::Db>, product: &str) -> String {
read_product(db, &product)
.unwrap()
.unwrap()
.price_usd
.to_string()
// Get all data about a product
#[get("/api/product/<product>")]
pub fn get_product_info(db: &State<sled::Db>, product: &str) -> Json<Product> {
return Json(read_product(db, &product).unwrap().unwrap());
}

27
src/authenticate.rs Normal file
View File

@ -0,0 +1,27 @@
use argon2::{
password_hash::{PasswordHasher, SaltString},
Argon2,
};
use rocket::State;
use std::env;
pub fn check_password(password: &str, salt: &State<SaltString>, argon2: &State<Argon2>) -> bool {
let entered_password = password.as_bytes(); // Get the user entered password
let hashed_password = argon2 // Hash the password
.hash_password(entered_password, &salt.as_ref())
.unwrap()
.to_string();
let mut admin_password = env::var("ADMIN_PASSWD").unwrap(); // Get the provided admin password from the environment variable
admin_password = argon2 // Hash the admin password
.hash_password(admin_password.as_bytes(), &salt.as_ref())
.unwrap()
.to_string();
if hashed_password == admin_password {
return true;
} else {
return false;
}
}

View File

@ -92,3 +92,11 @@ pub fn read_all_orders() -> Vec<Order> {
}
return all_orders; // Return the orders
}
// Remove an order from the database
pub fn remove_order(uuid: &str) -> std::result::Result<Option<sled::IVec>, sled::Error> {
info!("Removing order with Uuid {}", uuid);
let db = sled::open("order_db").unwrap();
db.remove(uuid)
}

View File

@ -4,9 +4,10 @@ use rocket::fairing::{Fairing, Info, Kind};
use rocket::{http::Header, Request, Response};
#[macro_use]
extern crate log;
extern crate paris;
mod api;
mod authenticate;
mod captcha;
mod database;
mod structures;
@ -45,9 +46,14 @@ impl Fairing for CORS {
// Launch rocket
#[launch]
fn rocket() -> _ {
env_logger::init();
let mut log = paris::Logger::new();
//env_logger::init();
info!("Starting up Rocket");
let _config = sled::Config::default()
.use_compression(true)
.compression_factor(15);
rocket::build()
.manage(database::open()) // Manage database state
.manage(Argon2::default()) // Manage Argon2 state
@ -55,15 +61,16 @@ fn rocket() -> _ {
.mount(
"/",
routes![
get_stock,
get_price,
new_order,
update_order,
order_info,
all_orders,
new_product,
update_product,
return_captcha,
check_captcha
check_captcha,
get_product_info,
delete_order
],
)
.attach(CORS)

View File

@ -1,12 +1,21 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
// Struct for the urers answer to a captcha
// Struct for the users answer to a captcha
#[derive(Clone, Debug, FromForm)]
pub struct CaptchaAnswer {
pub text: String,
}
// Struct for modifying an order
#[derive(FromForm, Copy, Clone)]
pub struct OrderUpdate<'r> {
pub r#order_uuid: &'r str,
pub status: OrderStatus,
pub payment_type: PaymentOption,
pub r#password: &'r str,
}
// Return status
#[derive(Serialize)]
pub struct Status {
@ -47,6 +56,7 @@ pub struct OrderRequest<'r> {
pub r#user_message: &'r str,
pub r#encryption_key: &'r str,
pub r#payment_type: PaymentOption,
pub r#captcha: &'r str,
}
// Struct for a stored order
@ -71,7 +81,7 @@ pub struct Update<'r> {
}
// Possible status states for an order
#[derive(Debug, PartialEq, FromFormField, Serialize, Clone, Deserialize)]
#[derive(Debug, PartialEq, FromFormField, Serialize, Clone, Copy, Deserialize)]
pub enum OrderStatus {
Submitted,
Processing,
@ -80,7 +90,7 @@ pub enum OrderStatus {
}
// Options for payment
#[derive(Debug, PartialEq, FromFormField, Serialize, Clone, Deserialize)]
#[derive(Debug, PartialEq, FromFormField, Serialize, Deserialize, Clone, Copy)]
pub enum PaymentOption {
XMR,
ETH,