From 2c86325e3c23fdddfb0a84e479437c8bc057a7eb Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Fri, 31 Jan 2020 20:30:03 -0500 Subject: Add user request guard --- src/db.rs | 24 ++++++++++++++++++---- src/error.rs | 2 ++ src/main.rs | 9 +++++---- src/models.rs | 9 +++++---- src/schema.rs | 5 ++++- src/server.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++--------- templates/index.hbs | 3 +++ 7 files changed, 87 insertions(+), 22 deletions(-) diff --git a/src/db.rs b/src/db.rs index 6014db9..3f94ff2 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,15 +1,14 @@ use crate::error::Error; use crate::models; -use base64; +use diesel::dsl::select; +use diesel::dsl::exists; use diesel::connection::Connection; use diesel::pg::PgConnection; use diesel::ExpressionMethods; use diesel::QueryDsl; use diesel::RunQueryDsl; -use rand; -use rand::Rng; -pub const COST: u32 = 12; +pub const COST: u32 = 10; pub fn create_config(conn: &PgConnection, config: &models::Config) -> Result<(), Error> { use crate::schema::config; @@ -24,6 +23,12 @@ pub fn create_config(conn: &PgConnection, config: &models::Config) -> Result<(), }) } +pub fn get_config(conn: &PgConnection) -> Result { + use crate::schema::config; + config::table.get_result::(conn) + .map_err(From::from) +} + pub fn adduser(conn: &PgConnection, username: &str, password: &str) -> Result<(), Error> { use crate::schema::users; @@ -54,3 +59,14 @@ pub fn authenticate( Err(Error::NotFound) } } + +pub fn user_exists( + conn: &PgConnection, + username: &str) -> Result { + use crate::schema::users; + + let result = select(exists(users::table.filter(users::username.eq(username)))) + .get_result(conn) + .map_err(|err| err)?; + Ok(result) +} diff --git a/src/error.rs b/src/error.rs index 81647d5..523922a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,7 @@ pub enum Error { PasswordError(BcryptError), AlreadyExists, NotFound, + InternalError, } impl fmt::Display for Error { @@ -19,6 +20,7 @@ impl fmt::Display for Error { Error::PasswordError(ref e) => e.fmt(f), Error::AlreadyExists => f.write_str("AlreadyExists"), Error::NotFound => f.write_str("NotFound"), + Error::InternalError => f.write_str("InternalError"), } } } diff --git a/src/main.rs b/src/main.rs index 6d8693f..70cd9d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -66,9 +66,10 @@ fn main() { if let Some(matches) = matches.subcommand_matches("init") { let config = pjournal::models::Config { - strava_client_id: matches.value_of("strava_client_id").unwrap(), - strava_client_secret: matches.value_of("strava_client_secret").unwrap(), - rocket_secret_key: matches.value_of("rocket_secret_key").unwrap(), + strava_client_id: matches.value_of("strava_client_id").unwrap().to_string(), + strava_client_secret: matches.value_of("strava_client_secret").unwrap().to_string(), + rocket_secret_key: matches.value_of("rocket_secret_key").unwrap().to_string(), + singleton: true, }; pjournal::db::create_config(&conn, &config).unwrap(); @@ -80,6 +81,6 @@ fn main() { let config = pjournal::server::Params { base_url: base_url.to_string(), }; - pjournal::server::start(db_url, config); + pjournal::server::start(conn, db_url, config); } } diff --git a/src/models.rs b/src/models.rs index 28a8b65..10f7d68 100644 --- a/src/models.rs +++ b/src/models.rs @@ -5,10 +5,11 @@ use bcrypt; #[derive(Insertable, Queryable)] #[table_name = "config"] -pub struct Config<'a> { - pub strava_client_secret: &'a str, - pub strava_client_id: &'a str, - pub rocket_secret_key: &'a str, +pub struct Config { + pub strava_client_secret: String, + pub strava_client_id: String, + pub rocket_secret_key: String, + pub singleton: bool, } #[derive(Insertable, Queryable)] diff --git a/src/schema.rs b/src/schema.rs index 055d6d0..809706c 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -14,4 +14,7 @@ table! { } } -allow_tables_to_appear_in_same_query!(config, users,); +allow_tables_to_appear_in_same_query!( + config, + users, +); diff --git a/src/server.rs b/src/server.rs index 4f98337..40e6b87 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,13 +1,14 @@ -use diesel::pg::PgConnection; use rocket::http::Cookies; use rocket::http::Cookie; use rocket::config::Config; use rocket::config::Environment; use rocket::config::Value; use rocket::http::Status; +use rocket::request; use rocket::request::Form; use rocket::request::FromForm; -use rocket::response; +use rocket::request::FromRequest; +use rocket::request::Request; use rocket::response::Redirect; use rocket::State; use rocket_contrib::templates::Template; @@ -24,11 +25,48 @@ pub struct Params { #[database("db")] pub struct Db(diesel::PgConnection); +#[derive(Debug)] +pub struct LoggedInUser { + pub username: String, +} + +impl<'a, 'r> FromRequest<'a, 'r> for LoggedInUser { + type Error = Error; + + fn from_request(request: &'a Request<'r>) + -> request::Outcome { + let conn = request.guard::() + .map_failure(|(s, ())| (s, Error::InternalError))?; + + let user = (|| { + let username = request.cookies() + .get_private("user") + .map(|cookie| cookie.value().to_string()) + .ok_or(Error::NotFound)?; + if db::user_exists(&conn, &username)? { + Ok(LoggedInUser{username: username}) + } else { + Err(Error::NotFound) + } + })(); + + use request::Outcome; + match user { + Ok(user) => Outcome::Success(user), + Err(Error::NotFound) => Outcome::Forward(()), + Err(e) => Outcome::Failure((Status::InternalServerError, e)), + } + } +} + #[get("/")] -fn index() -> Template { +fn index(user: Option) -> Template { let mut context = HashMap::new(); - context.insert("parent", "layout"); - context.insert("message", "Hello, World"); + context.insert("parent", "layout".to_string()); + context.insert("message", "Hello, World".to_string()); + for user in user { + context.insert("user", user.username); + } Template::render("index", context) } @@ -48,12 +86,10 @@ struct LoginData { password: String, } -// Request guard for logged in user: https://api.rocket.rs/v0.4/rocket/request/trait.FromRequest.html - #[post("/login", data = "")] fn login_submit(conn: Db, data: Form, mut cookies: Cookies) -> Result { match db::authenticate(&*conn, &data.username, &data.password) { - Ok(user) => { + Ok(_user) => { cookies.add_private(Cookie::new("user", data.username.clone())); Ok(Redirect::to(uri!(index).to_string())) }, @@ -87,14 +123,17 @@ fn link_strava(config: State) -> Redirect { )) } -pub fn start(db_url: &str, params: Params) { +pub fn start(conn: diesel::PgConnection, db_url: &str, params: Params) { let mut database_config = HashMap::new(); let mut databases = HashMap::new(); database_config.insert("url", Value::from(db_url)); databases.insert("db", Value::from(database_config)); + let persistent_config = db::get_config(&conn).expect("loading config"); + let config = Config::build(Environment::Development) .extra("databases", databases) + .secret_key(persistent_config.rocket_secret_key) .finalize() .unwrap(); diff --git a/templates/index.hbs b/templates/index.hbs index f61c517..238e5e2 100644 --- a/templates/index.hbs +++ b/templates/index.hbs @@ -1,4 +1,7 @@ {{#*inline "page"}} +{{#if user}} +

Logged in as {{ user }}

+{{/if}}

Message: {{ message }}

Link strava account

{{/inline}} -- cgit v1.2.3