From 9be964ef8f01debedf192f22009c735496b6feaa Mon Sep 17 00:00:00 2001 From: phga Date: Fri, 25 Nov 2022 03:47:14 +0100 Subject: [PATCH] fix: SSL/CORS -> Vite Proxy + expected login behaviour + minor things --- rust_solid_cassandra/backend/Cargo.lock | 173 ++++++------------ rust_solid_cassandra/backend/Cargo.toml | 8 +- rust_solid_cassandra/backend/src/main.rs | 37 ++-- .../backend/src/model/user.rs | 4 +- .../backend/src/repo/user_repository.rs | 2 +- rust_solid_cassandra/backend/src/routes.rs | 50 +++-- .../backend/src/routes/user_routes.rs | 28 ++- .../frontend/src/api/RestClient.ts | 24 ++- rust_solid_cassandra/frontend/vite.config.ts | 9 + 9 files changed, 164 insertions(+), 171 deletions(-) diff --git a/rust_solid_cassandra/backend/Cargo.lock b/rust_solid_cassandra/backend/Cargo.lock index a8cb78d..25d125e 100644 --- a/rust_solid_cassandra/backend/Cargo.lock +++ b/rust_solid_cassandra/backend/Cargo.lock @@ -19,21 +19,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "actix-cors" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e" -dependencies = [ - "actix-utils", - "actix-web", - "derive_more", - "futures-util", - "log", - "once_cell", - "smallvec", -] - [[package]] name = "actix-http" version = "3.2.2" @@ -43,7 +28,6 @@ dependencies = [ "actix-codec", "actix-rt", "actix-service", - "actix-tls", "actix-utils", "ahash", "base64", @@ -167,24 +151,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "actix-tls" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "log", - "openssl", - "pin-project-lite", - "tokio-openssl", - "tokio-util", -] - [[package]] name = "actix-utils" version = "3.0.1" @@ -208,7 +174,6 @@ dependencies = [ "actix-rt", "actix-server", "actix-service", - "actix-tls", "actix-utils", "actix-web-codegen", "ahash", @@ -372,16 +337,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "backend" version = "0.1.0" dependencies = [ - "actix-cors", "actix-identity", "actix-session", "actix-web", "cassandra-cpp", "env_logger", "log", - "openssl", "serde", "serde_json", + "sha256", "uuid", ] @@ -412,6 +376,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[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 = "block-buffer" version = "0.10.3" @@ -521,7 +494,7 @@ dependencies = [ "hmac", "percent-encoding", "rand", - "sha2", + "sha2 0.10.6", "subtle", "time", "version_check", @@ -578,13 +551,22 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -637,21 +619,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.1.0" @@ -762,6 +729,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkdf" version = "0.12.3" @@ -777,7 +750,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.6", ] [[package]] @@ -966,45 +939,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-sys" -version = "0.9.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1052,12 +986,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - [[package]] name = "polyval" version = "0.6.0" @@ -1234,7 +1162,20 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.6", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -1245,7 +1186,17 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.6", +] + +[[package]] +name = "sha256" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" +dependencies = [ + "hex", + "sha2 0.9.9", ] [[package]] @@ -1374,18 +1325,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "tokio-openssl" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08f9ffb7809f1b20c1b398d92acf4cc719874b3b2b2d9ea2f09b4a80350878a" -dependencies = [ - "futures-util", - "openssl", - "openssl-sys", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.4" @@ -1492,12 +1431,6 @@ dependencies = [ "syn", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" diff --git a/rust_solid_cassandra/backend/Cargo.toml b/rust_solid_cassandra/backend/Cargo.toml index 47b6cf3..5308a61 100644 --- a/rust_solid_cassandra/backend/Cargo.toml +++ b/rust_solid_cassandra/backend/Cargo.toml @@ -6,14 +6,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-web = { version = "4", features = ["openssl"] } # Webserver itself +actix-web = "4" +# actix-web = { version = "4", features = ["openssl"] } # Webserver itself actix-session = { version = "0.7", features = ["cookie-session"] } # Session middleware actix-identity = "0.5.2" -actix-cors = "0.6.4" +# actix-cors = "0.6.4" +sha256 = "1.1.1" env_logger = "0.9" # Logger itself log = "0.4" # Lightweight logging facade (Logging API) uuid = { version = "1.2.2", features = ["v4", "fast-rng", "macro-diagnostics", "serde"]} cassandra-cpp = "1.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -openssl = { version = "0.10.42", features = ["v110"] } \ No newline at end of file +# openssl = { version = "0.10.42", features = ["v110"] } \ No newline at end of file diff --git a/rust_solid_cassandra/backend/src/main.rs b/rust_solid_cassandra/backend/src/main.rs index 2c381bb..dd76601 100644 --- a/rust_solid_cassandra/backend/src/main.rs +++ b/rust_solid_cassandra/backend/src/main.rs @@ -1,6 +1,6 @@ use std::{env, io, net::SocketAddrV4, sync::Arc}; -use actix_cors::Cors; +// use actix_cors::Cors; use actix_identity::IdentityMiddleware; use actix_session::{config::PersistentSession, storage::CookieSessionStore, SessionMiddleware}; use actix_web::{ @@ -15,7 +15,7 @@ mod model; use model::user::User; // Define our repo module mod repo; -use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; +// use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use repo::todo_repository::TodoRepository; use repo::user_repository::UserRepository; // Define our routes module @@ -42,7 +42,7 @@ async fn main() -> io::Result<()> { // TODO: web::Data is already an Arc //-> Investigate if we really need the session as an Arc as well - let user = User::new("admin", "init_pw_hash", "init_salt"); + let user = User::new("admin", "nimda", "salzig"); let user_repo = web::Data::new(UserRepository::new(Arc::clone(&cassandra_session))); if let Err(err) = user_repo.create(&user) { log::debug!("Default user already exists: {err}"); @@ -67,9 +67,9 @@ async fn main() -> io::Result<()> { // TODO: Remove after local dev // openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost' // Move to /cert/... in container - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; - builder.set_private_key_file("/cert/key.pem", SslFiletype::PEM)?; - builder.set_certificate_chain_file("/cert/cert.pem")?; + // let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; + // builder.set_private_key_file("/cert/key.pem", SslFiletype::PEM)?; + // builder.set_certificate_chain_file("/cert/cert.pem")?; log::info!( "Starting HTTP server: http://{}:{}", @@ -83,21 +83,22 @@ async fn main() -> io::Result<()> { .wrap(IdentityMiddleware::default()) .wrap( SessionMiddleware::builder(CookieSessionStore::default(), key.clone()) - .cookie_secure(true) + .cookie_secure(false) // TODO: Remove after development with local solidjs app - .cookie_same_site(actix_web::cookie::SameSite::None) + // .cookie_secure(true) + // .cookie_same_site(actix_web::cookie::SameSite::None) // Session lifetime .session_lifecycle(PersistentSession::default().session_ttl(Duration::days(7))) .build(), ) - .wrap( - // TODO: Remove after development with local solidjs app - Cors::default() - .allowed_origin("http://localhost:3000") - .supports_credentials() - .allow_any_method() - .allow_any_header(), - ) + // TODO: Remove after development with local solidjs app + // .wrap( + // Cors::default() + // .allowed_origin("http://localhost:3000") + // .supports_credentials() + // .allow_any_method() + // .allow_any_header(), + // ) .wrap(middleware::Logger::default()) .app_data(user_repo.clone()) .app_data(todo_repo.clone()) @@ -111,7 +112,9 @@ async fn main() -> io::Result<()> { .service(routes::delete_logout) .default_service(web::to(routes::index)) }) - .bind_openssl(socket_addr, builder)? + .bind(socket_addr)? + // TODO: Remove after local dev + // .bind_openssl(socket_addr, builder)? .workers(2) // number of workers per bind default ist #cpus .run() .await diff --git a/rust_solid_cassandra/backend/src/model/user.rs b/rust_solid_cassandra/backend/src/model/user.rs index 6ee6b13..e841a7a 100644 --- a/rust_solid_cassandra/backend/src/model/user.rs +++ b/rust_solid_cassandra/backend/src/model/user.rs @@ -10,11 +10,11 @@ pub struct User { } impl User { - pub fn new(login: &str, hash: &str, salt: &str) -> User { + pub fn new(login: &str, password: &str, salt: &str) -> User { User { id: Uuid::new_v4(), login: String::from(login), - hash: String::from(hash), + hash: sha256::digest(format!("{}{}", password, salt)), salt: String::from(salt), } } diff --git a/rust_solid_cassandra/backend/src/repo/user_repository.rs b/rust_solid_cassandra/backend/src/repo/user_repository.rs index 6ea37a4..1b394f3 100644 --- a/rust_solid_cassandra/backend/src/repo/user_repository.rs +++ b/rust_solid_cassandra/backend/src/repo/user_repository.rs @@ -102,7 +102,7 @@ impl UserRepository { }) } - fn read_by_login(&self, login: &str) -> Result> { + pub fn read_by_login(&self, login: &str) -> Result> { let mut query = stmt!(&format!( "SELECT JSON * FROM {} WHERE login = ?", self.table diff --git a/rust_solid_cassandra/backend/src/routes.rs b/rust_solid_cassandra/backend/src/routes.rs index 66ced43..0c0b289 100644 --- a/rust_solid_cassandra/backend/src/routes.rs +++ b/rust_solid_cassandra/backend/src/routes.rs @@ -3,14 +3,27 @@ use actix_session::Session; use actix_web::{ delete, http::{header::ContentType, Method}, - post, web, HttpRequest, HttpResponse, Responder, HttpMessage, + post, web, HttpMessage, HttpRequest, HttpResponse, Responder, }; +use serde::{Deserialize, Serialize}; use crate::{model::user::User, repo::user_repository::UserRepository}; pub mod todo_routes; pub mod user_routes; +#[derive(Debug, Deserialize)] +pub struct LoginData { + login: String, + password: String, +} + +#[derive(Debug, Serialize)] +pub struct ActiveUser { + id: uuid::Uuid, + login: String, +} + /// Helper function to get a valid User from session. fn get_user_from_session(session: Session) -> Option { match session.get::("user") { @@ -26,7 +39,6 @@ fn get_user_from_session(session: Session) -> Option { } } - /// Handles any route that is not defined explicitly. pub async fn index(id: Option, method: Method) -> impl Responder { match method { @@ -47,29 +59,43 @@ pub async fn index(id: Option, method: Method) -> impl Responder { #[post("/login")] pub async fn post_login( req: HttpRequest, - payload: web::Json, + payload: web::Json, repo: web::Data, session: Session, ) -> impl Responder { log::debug!("Received {payload:?}"); - match repo.read(&payload.id()) { + match repo.read_by_login(&payload.login) { Ok(Some(user)) => { - if payload.salt() == "" { - log::debug!("Initial login request with empty salt: {payload:?}"); - HttpResponse::Ok().json(format!("{{ 'salt': '{}' }}", user.salt())) - } else if payload.hash() == user.hash() { + let hash = sha256::digest(String::from(format!("{}{}", payload.password, user.salt()))); + if hash == user.hash() { log::debug!("User successfully logged in: {payload:?} == {user:?}"); // TODO: Mayb handle more gracefully - Identity::login(&req.extensions(), format!("{}", payload.id())) + Identity::login(&req.extensions(), format!("{}", user.id())) .expect("Log the user in"); + // Create answer for frontend + let res = ActiveUser { + id: user.id().clone(), + login: String::from(user.login()), + }; + // let res = format!( + // "{{'id': '{}', 'login': '{}'}}", + // user.id(), + // user.login() + // ); // TODO: Mayb handle more gracefully session - .insert("user", payload.0) + .insert("user", user) .expect("Insert user into session"); - HttpResponse::Ok().finish() + HttpResponse::Ok().json(res) } else { - log::debug!("Wrong password hash for user: {payload:?} != {user:?}"); + log::debug!( + "Wrong password hash for user: ({}: {}) != ({}: {})", + payload.login, + hash, + user.login(), + user.hash() + ); HttpResponse::Unauthorized().finish() } } diff --git a/rust_solid_cassandra/backend/src/routes/user_routes.rs b/rust_solid_cassandra/backend/src/routes/user_routes.rs index 6cd8375..bf141f4 100644 --- a/rust_solid_cassandra/backend/src/routes/user_routes.rs +++ b/rust_solid_cassandra/backend/src/routes/user_routes.rs @@ -1,17 +1,22 @@ use actix_identity::Identity; // TODO: Guard for login -use actix_web::{get, web, Responder, HttpResponse, put, post}; +use actix_web::{get, post, put, web, HttpResponse, Responder}; +use serde::Deserialize; -use crate::{repo::user_repository::UserRepository, model::user::User}; +use crate::{model::user::User, repo::user_repository::UserRepository}; + +#[derive(Debug, Deserialize)] +pub struct NewUser { + pub login: String, + pub password: String, +} // TODO: Only allow if role is admin (If I implement roles for this evaluation...) /// Returns a json list of all users. #[get("/user")] pub async fn get_user(_id: Identity, repo: web::Data) -> impl Responder { match repo.read_all() { - Ok(users) => { - HttpResponse::Ok().json(users) - } + Ok(users) => HttpResponse::Ok().json(users), Err(err) => { log::error!("Could not read from user repo: {err}"); HttpResponse::InternalServerError() @@ -46,9 +51,16 @@ pub async fn put_user( // TODO: For a real app, impl smth like a registration-secret or email verification /// Creates a new user. #[post("/user")] -pub async fn post_user(payload: web::Json, repo: web::Data) -> impl Responder { +pub async fn post_user( + payload: web::Json, + repo: web::Data, +) -> impl Responder { log::debug!("Received {payload:?}"); - let user = User::new(payload.login(), payload.hash(), payload.salt()); + let user = User::new( + &payload.login, + &payload.password, + &sha256::digest(format!("{}{}", payload.login, &payload.password)), + ); match repo.create(&user) { Ok(_) => { log::debug!("Successfully created {user:?}"); @@ -59,4 +71,4 @@ pub async fn post_user(payload: web::Json, repo: web::Data HttpResponse::BadRequest().body(format!("{user:?}")) } } -} \ No newline at end of file +} diff --git a/rust_solid_cassandra/frontend/src/api/RestClient.ts b/rust_solid_cassandra/frontend/src/api/RestClient.ts index 066c0e4..bd11962 100644 --- a/rust_solid_cassandra/frontend/src/api/RestClient.ts +++ b/rust_solid_cassandra/frontend/src/api/RestClient.ts @@ -5,7 +5,7 @@ * evaluating different technologies for GLOU */ -const BACKEND_SERVER_URL = 'localhost:6969'; +const BACKEND_SERVER_URL = 'localhost:3000'; // Helper function to do the actual fetching from our backend // For now we always send/receive json -> For a real world example make this more variable const do_fetch = async ( @@ -14,20 +14,28 @@ const do_fetch = async ( payload?: string ): Promise => { try { - const req = await fetch(`https://${BACKEND_SERVER_URL}${path}`, { + const req = await fetch(`http://${BACKEND_SERVER_URL}/api${path}`, { method, - credentials: 'include', + // credentials: 'include', headers: { - 'Access-Control-Allow-Origin': `${BACKEND_SERVER_URL}`, - 'Access-Control-Allow-Credentials': 'true', + // 'Access-Control-Allow-Origin': `${BACKEND_SERVER_URL}`, + // 'Access-Control-Allow-Credentials': 'true', 'Content-Type': 'application/json', Accept: 'application/json', }, body: payload, }); - - const res = await req.text(); - return JSON.parse(res); + if (req.ok) { + const res = await req.text(); + try { + return JSON.parse(res); + } catch (err) { + return res; + } + } else { + console.log('Response from server was not 200'); + return {}; + } } catch (err) { console.log(err); } diff --git a/rust_solid_cassandra/frontend/vite.config.ts b/rust_solid_cassandra/frontend/vite.config.ts index 9ff59a1..c96facf 100644 --- a/rust_solid_cassandra/frontend/vite.config.ts +++ b/rust_solid_cassandra/frontend/vite.config.ts @@ -1,10 +1,19 @@ import { defineConfig } from 'vite'; import solidPlugin from 'vite-plugin-solid'; +const backend = 'http://127.0.0.1:6969'; + export default defineConfig({ plugins: [solidPlugin()], server: { port: 3000, + proxy: { + '/api': { + target: backend, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + }, + }, }, build: { target: 'esnext',