From 057382f8d138c63cdc48b0facd9bf4a4a229057a Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 4 Oct 2020 13:23:46 +0530 Subject: init --- src/bin/server.rs | 44 +++++++++++++++ src/handlers/mod.rs | 2 + src/handlers/smoke.rs | 15 +++++ src/handlers/users.rs | 106 +++++++++++++++++++++++++++++++++++ src/handlers/users.rs.html | 137 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 10 ++++ src/models.rs | 21 +++++++ src/schema.rs | 9 +++ 8 files changed, 344 insertions(+) create mode 100644 src/bin/server.rs create mode 100644 src/handlers/mod.rs create mode 100644 src/handlers/smoke.rs create mode 100644 src/handlers/users.rs create mode 100644 src/handlers/users.rs.html create mode 100644 src/lib.rs create mode 100644 src/models.rs create mode 100644 src/schema.rs (limited to 'src') diff --git a/src/bin/server.rs b/src/bin/server.rs new file mode 100644 index 0000000..eb290db --- /dev/null +++ b/src/bin/server.rs @@ -0,0 +1,44 @@ +use actix_cors::Cors; +use actix_identity::{CookieIdentityPolicy, IdentityService}; +use actix_web::middleware; +use actix_web::{web, App, HttpServer}; +use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::MysqlConnection; +use furby::handlers::smoke::manual_hello; +use furby::handlers::users; +use rand::Rng; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + pretty_env_logger::init(); + + let db_url = env!("DATABASE_URL"); + let manager = ConnectionManager::::new(db_url); + let pool = Pool::builder() + .build(manager) + .expect("Failed to create pool."); + + let private_key = rand::thread_rng().gen::<[u8; 32]>(); + HttpServer::new(move || { + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&private_key) + .name("user-login") + .secure(false), + )) + .wrap(Cors::new().supports_credentials().finish()) + .wrap(middleware::Logger::default()) + .data(pool.clone()) + .service( + web::scope("/user") + .route("/existing", web::post().to(users::name_exists)) + .route("/login", web::post().to(users::login)) + .route("/{uname}", web::get().to(users::user_details)) + .route("/new", web::post().to(users::new_user)), + ) + .route("/hey", web::get().to(manual_hello)) + }) + .bind("127.0.0.1:7878")? + .run() + .await +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs new file mode 100644 index 0000000..65d3519 --- /dev/null +++ b/src/handlers/mod.rs @@ -0,0 +1,2 @@ +pub mod smoke; +pub mod users; diff --git a/src/handlers/smoke.rs b/src/handlers/smoke.rs new file mode 100644 index 0000000..d0a1038 --- /dev/null +++ b/src/handlers/smoke.rs @@ -0,0 +1,15 @@ +use actix_web::{get, post, HttpResponse, Responder}; + +#[get("/")] +async fn hello() -> impl Responder { + HttpResponse::Ok().body("Hello world!") +} + +#[post("/echo")] +async fn echo(req_body: String) -> impl Responder { + HttpResponse::Ok().body(req_body) +} + +pub async fn manual_hello() -> impl Responder { + HttpResponse::Ok().body("Hey there!") +} diff --git a/src/handlers/users.rs b/src/handlers/users.rs new file mode 100644 index 0000000..e6b0415 --- /dev/null +++ b/src/handlers/users.rs @@ -0,0 +1,106 @@ +use crate::models::{Member, NewMember}; +use crate::schema::members::dsl::*; +use crate::TPool; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use bcrypt::{hash, verify, DEFAULT_COST}; +use diesel::prelude::*; +use log::{error, info}; +use serde::Deserialize; + +pub async fn new_user( + pool: web::Data, + item: web::Json, +) -> impl Responder { + let conn = pool.get().unwrap(); + let hashed_item = NewMember { + password: hash(&item.password, DEFAULT_COST).unwrap(), + ..(item.into_inner()) + }; + diesel::insert_into(members) + .values(hashed_item) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Inserted successfully!") +} + +pub async fn name_exists( + pool: web::Data, + item: String, +) -> impl Responder { + let conn = pool.get().unwrap(); + info!("target: {:?}", item); + if (members + .filter(username.eq(&item)) + .limit(1) + .load::(&conn) + .expect("Coundn't connect to DB")) + .len() + > 0 + { + HttpResponse::Ok().body("true") + } else { + HttpResponse::Ok().body("false") + } +} + +#[derive(Deserialize)] +pub struct Login { + username: String, + password: String, +} + +pub async fn login( + pool: web::Data, + cookie: Identity, + login_details: web::Json, +) -> impl Responder { + info!("Login hit"); + let conn = pool.get().unwrap(); + let entered_pass = &login_details.password; + let selected_user = members + .filter(username.eq(&login_details.username)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let hashed_pass = selected_user.password; + if verify(entered_pass, &hashed_pass).unwrap() { + cookie.remember(login_details.username.clone()); + info!( + "Successful login: {} {}", + selected_user.username, selected_user.email_id + ); + HttpResponse::Ok().finish() + } else { + HttpResponse::Unauthorized().finish() + } +} + +pub async fn logout(cookie: Identity) -> impl Responder { + cookie.forget(); + HttpResponse::Found().header("location", "/").finish() +} + +pub async fn user_details( + uname: web::Path, + pool: web::Data, +) -> impl Responder { + let conn = pool.get().unwrap(); + let uname = uname.into_inner(); + info!("Fetching info for: \"{}\"", uname); + let selected_user = members + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn); + match selected_user { + Ok(m) => { + info!("Found user: {}", uname); + HttpResponse::Ok().json(m) + } + Err(_) => { + error!("User not found: {}", uname); + HttpResponse::NotFound().finish() + } + } +} diff --git a/src/handlers/users.rs.html b/src/handlers/users.rs.html new file mode 100644 index 0000000..a233b04 --- /dev/null +++ b/src/handlers/users.rs.html @@ -0,0 +1,137 @@ + + + + +~/code/rust/actix-tests/src/handlers/users.rs.html + + + + + + + + +
+use crate::models::{Member, NewMember};
+use crate::schema::members::dsl::*;
+use crate::TPool;
+
+use actix_identity::Identity;
+use actix_web::{web, HttpResponse, Responder};
+use bcrypt::{hash, verify, DEFAULT_COST};
+use diesel::prelude::*;
+use log::{error, info};
+use serde::Deserialize;
+
+pub async fn new_user(
+    pool: web::Data<TPool>,
+    item: web::Json<NewMember>,
+) -> impl Responder {
+    let conn = pool.get().unwrap();
+    let hashed_item = NewMember {
+        password: hash(&item.password, DEFAULT_COST).unwrap(),
+        ..(item.into_inner())
+    };
+    diesel::insert_into(members)
+        .values(hashed_item)
+        .execute(&conn)
+        .expect("Coundn't connect to DB");
+    HttpResponse::Ok().body("Inserted successfully!")
+}
+
+pub async fn name_exists(
+    pool: web::Data<TPool>,
+    item: String,
+) -> impl Responder {
+    let conn = pool.get().unwrap();
+    info!("target: {:?}", item);
+    if (members
+        .filter(username.eq(&item))
+        .limit(1)
+        .load::<Member>(&conn)
+        .expect("Coundn't connect to DB"))
+    .len()
+        > 0
+    {
+        HttpResponse::Ok().body("true")
+    } else {
+        HttpResponse::Ok().body("false")
+    }
+}
+
+#[derive(Deserialize)]
+pub struct Login {
+    username: String,
+    password: String,
+}
+
+pub async fn login(
+    pool: web::Data<TPool>,
+    cookie: Identity,
+    login_details: web::Form<Login>,
+) -> impl Responder {
+    let conn = pool.get().unwrap();
+    let entered_pass = &login_details.password;
+    let selected_user = members
+        .filter(username.eq(&login_details.username))
+        .limit(1)
+        .first::<Member>(&conn)
+        .expect("Couldn't connect to DB");
+    let hashed_pass = selected_user.password;
+    if verify(entered_pass, &hashed_pass).unwrap() {
+        cookie.remember(login_details.username.clone());
+        info!(
+            "Successful login: {} {}",
+            selected_user.username, selected_user.email_id
+        );
+        HttpResponse::Found().header("location", "/").finish()
+    } else {
+        HttpResponse::Unauthorized().finish()
+    }
+}
+
+pub async fn logout(cookie: Identity) -> impl Responder {
+    cookie.forget();
+    HttpResponse::Found().header("location", "/").finish()
+}
+
+pub async fn user_details(
+    uname: web::Path<String>,
+    pool: web::Data<TPool>,
+) -> impl Responder {
+    let conn = pool.get().unwrap();
+    let uname = uname.into_inner();
+    info!("Fetching info for: \"{}\"", uname);
+    let selected_user = members
+        .filter(username.eq(&uname))
+        .limit(1)
+        .first::<Member>(&conn);
+    match selected_user {
+        Ok(m) => {
+            info!("Found user: {}", uname);
+            HttpResponse::Ok().json(m)
+        }
+        Err(_) => {
+            error!("User not found: {}", uname);
+            HttpResponse::NotFound().finish()
+        }
+    }
+}
+
+ + + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d956a3f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate diesel; + +pub mod handlers; +pub mod models; +pub mod schema; + +use diesel::r2d2::{self, ConnectionManager}; +use diesel::MysqlConnection; +pub type TPool = r2d2::Pool>; diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..ce28ac8 --- /dev/null +++ b/src/models.rs @@ -0,0 +1,21 @@ +use super::schema::members; +use diesel::{Insertable, Queryable}; +use serde::{Deserialize, Serialize}; + +#[derive(Queryable, Serialize)] +pub struct Member { + pub id: i32, + pub username: String, + pub password: String, + pub phone_number: String, + pub email_id: String, +} + +#[derive(Insertable, Deserialize)] +#[table_name = "members"] +pub struct NewMember { + pub username: String, + pub password: String, + pub phone_number: String, + pub email_id: String, +} diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..8ed6e58 --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,9 @@ +table! { + members (id) { + id -> Integer, + username -> Varchar, + password -> Varchar, + phone_number -> Varchar, + email_id -> Varchar, + } +} -- cgit v1.2.3