users: Add routes and methods to activate user

Signed-off-by: ATechnoHazard <amolele@gmail.com>
This commit is contained in:
Amogh Lele 2020-07-01 23:25:27 +05:30
parent 328b14f986
commit d1625ce721
No known key found for this signature in database
GPG Key ID: F475143EDEDEBA3C
8 changed files with 106 additions and 47 deletions

View File

@ -4,8 +4,19 @@ use rocket::Rocket;
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use serde_json::Value; use serde_json::Value;
#[catch(400)]
pub fn bad_request() -> status::Custom<Json<Value>> {
status::Custom(
Status::BadRequest,
Json(json!({
"err": "bad request",
"msg": "Error parsing JSON body"
})),
)
}
#[catch(404)] #[catch(404)]
fn not_found() -> status::Custom<Json<Value>> { pub fn not_found() -> status::Custom<Json<Value>> {
status::Custom( status::Custom(
Status::NotFound, Status::NotFound,
Json(json!({ Json(json!({
@ -16,7 +27,7 @@ fn not_found() -> status::Custom<Json<Value>> {
} }
#[catch(422)] #[catch(422)]
fn unprocessable_entity() -> status::Custom<Json<Value>> { pub fn unprocessable_entity() -> status::Custom<Json<Value>> {
status::Custom( status::Custom(
Status::UnprocessableEntity, Status::UnprocessableEntity,
Json(json!({ Json(json!({
@ -28,7 +39,7 @@ fn unprocessable_entity() -> status::Custom<Json<Value>> {
} }
#[catch(500)] #[catch(500)]
fn internal_server_error() -> status::Custom<Json<Value>> { pub fn internal_server_error() -> status::Custom<Json<Value>> {
status::Custom( status::Custom(
Status::NotFound, Status::NotFound,
Json(json!({ Json(json!({
@ -40,6 +51,7 @@ fn internal_server_error() -> status::Custom<Json<Value>> {
pub fn fuel(rocket: Rocket) -> Rocket { pub fn fuel(rocket: Rocket) -> Rocket {
rocket.register(catchers![ rocket.register(catchers![
bad_request,
not_found, not_found,
unprocessable_entity, unprocessable_entity,
internal_server_error internal_server_error

View File

@ -4,10 +4,12 @@ use rocket::Rocket;
pub mod health; pub mod health;
pub mod paste; pub mod paste;
pub mod responder; pub mod responder;
pub mod user;
pub fn fuel(rocket: Rocket) -> Rocket { pub fn fuel(rocket: Rocket) -> Rocket {
let mut rocket = rocket; let mut rocket = rocket;
rocket = health::fuel(rocket); rocket = health::fuel(rocket);
rocket = paste::fuel(rocket); rocket = paste::fuel(rocket);
rocket = user::fuel(rocket);
rocket.attach(CORS()) rocket.attach(CORS())
} }

View File

@ -3,41 +3,30 @@ use std::ops::DerefMut;
use diesel::result::Error; use diesel::result::Error;
use rocket::response::status::Custom; use rocket::response::status::Custom;
use rocket::{ use rocket::{
http::{Cookie, Cookies, Status}, http::{Cookies, Status},
response::status, response::status,
Rocket, Rocket,
}; };
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use serde_json::Value; use serde_json::Value;
use crate::api::guards::db; use crate::api::catchers::{internal_server_error, not_found};
use crate::api::guards::db::DbConn;
use crate::core::paste::{entity::Paste, service::create_paste, service::fetch_paste}; use crate::core::paste::{entity::Paste, service::create_paste, service::fetch_paste};
use crate::core::users::service::create_or_fetch_user; use crate::core::users::service::create_or_fetch_user;
use crate::utils::phonetic_key; use crate::utils::phonetic_key;
use crate::utils::users::get_session_id;
#[post("/", data = "<paste>")] #[post("/", data = "<paste>")]
fn create(mut paste: Json<Paste>, conn: db::DbConn, mut ck: Cookies) -> Custom<Json<Value>> { fn create(mut paste: Json<Paste>, conn: DbConn, mut ck: Cookies) -> Custom<Json<Value>> {
// Check if frontend sent a session cookie // Check if frontend sent a session cookie
let user_id = match ck.get_private("session") { let user_id = get_session_id(&mut ck);
Some(c) => c.value().to_string(),
None => {
let user_id = phonetic_key::get_random_id();
ck.add_private(Cookie::new("session", user_id.clone()));
user_id
}
};
// Create or fetch already existing user // Create or fetch already existing user
let user = match create_or_fetch_user(user_id, &conn) { let user = match create_or_fetch_user(user_id, &conn) {
Ok(user) => user, Ok(user) => user,
Err(e) => { Err(_) => {
return status::Custom( return internal_server_error();
Status::InternalServerError,
Json(json!({
"err": e.to_string(),
"msg": "Failed to create or fetch user"
})),
)
} }
}; };
@ -56,37 +45,19 @@ fn create(mut paste: Json<Paste>, conn: db::DbConn, mut ck: Cookies) -> Custom<J
"paste_id": new_paste.id "paste_id": new_paste.id
})), })),
), ),
Err(e) => status::Custom( Err(_) => internal_server_error(),
Status::InternalServerError,
Json(json!({
"err": e.to_string(),
"msg": "Failed to create paste"
})),
),
} }
} }
#[get("/<id>")] #[get("/<id>")]
fn fetch(id: String, conn: db::DbConn) -> Custom<Json<Value>> { fn fetch(id: String, conn: DbConn) -> Custom<Json<Value>> {
let paste = match fetch_paste(id, &conn) { let paste = match fetch_paste(id, &conn) {
Ok(paste) => paste, Ok(paste) => paste,
Err(err) => { Err(e) => {
return match err.downcast_ref::<Error>() { return match e.downcast_ref::<Error>() {
Some(Error::NotFound) => Custom( Some(Error::NotFound) => not_found(),
Status::NotFound, _ => internal_server_error(),
Json(json!({ };
"err": err.to_string(),
"msg": "Unable to find a paste with that ID"
})),
),
_ => Custom(
Status::InternalServerError,
Json(json!({
"err": err.to_string(),
"msg": "Something went wrong, try again"
})),
),
}
} }
}; };

44
src/api/routes/user.rs Normal file
View File

@ -0,0 +1,44 @@
use rocket::response::status::Custom;
use rocket::{
http::{Status},
response::status,
Rocket,
};
use rocket_contrib::json::Json;
use serde_json::Value;
use crate::api::catchers::{internal_server_error, unprocessable_entity};
use crate::api::guards::db::DbConn;
use crate::core::users::entity::User;
use crate::core::users::service::{activate_user, create_or_fetch_user};
#[post("/", data = "<user>")]
fn activate(mut user: Json<User>, conn: DbConn) -> Custom<Json<Value>> {
if user.username.is_none() || user.password.is_none() {
return unprocessable_entity();
}
// Check if frontend sent a session cookie
let user_id = user.id.clone();
// Create or fetch already existing user
let mut found_user = match create_or_fetch_user(user_id, &conn) {
Ok(user) => user,
Err(_) => {
return internal_server_error();
}
};
// Activate user
found_user.activated = Some(true);
let activated_user = activate_user(&mut user, &conn);
match activated_user {
Ok(u) => status::Custom(Status::Ok, Json(json!(u))),
Err(_) => internal_server_error(),
}
}
pub fn fuel(rocket: Rocket) -> Rocket {
rocket.mount("/api/user", routes![activate])
}

View File

@ -17,3 +17,10 @@ pub fn find_user(id: String, conn: &PgConnection) -> Result<User> {
let user = users::table.find(id).get_result::<User>(conn)?; let user = users::table.find(id).get_result::<User>(conn)?;
Ok(user) Ok(user)
} }
pub fn update_user(user: &User, conn: &PgConnection) -> Result<User> {
let user = diesel::update(users::table.find(user.id.clone()))
.set(user)
.get_result::<User>(conn)?;
Ok(user)
}

View File

@ -32,3 +32,11 @@ pub fn create_or_fetch_user(id: String, conn: &PgConnection) -> Result<User> {
}; };
Ok(user) Ok(user)
} }
pub fn activate_user(user: &mut User, conn: &PgConnection) -> Result<User> {
let hashed_pass = hash(user.password.as_ref().unwrap().as_bytes(), DEFAULT_COST)?;
user.password = Some(hashed_pass);
let mut activated_user = postgres::update_user(user, conn)?;
activated_user.password = None;
Ok(activated_user)
}

View File

@ -1,3 +1,4 @@
pub mod db; pub mod db;
pub mod errors; pub mod errors;
pub mod phonetic_key; pub mod phonetic_key;
pub mod users;

14
src/utils/users.rs Normal file
View File

@ -0,0 +1,14 @@
use rocket::http::{Cookie, Cookies};
use crate::utils::phonetic_key;
pub fn get_session_id(ck: &mut Cookies) -> String {
match ck.get_private("session") {
Some(c) => c.value().to_string(),
None => {
let user_id = phonetic_key::get_random_id();
ck.add_private(Cookie::new("session", user_id.clone()));
user_id
}
}
}