diff options
author | Kjetil Orbekk <kjetil.orbekk@gmail.com> | 2020-01-29 20:45:21 -0500 |
---|---|---|
committer | Kjetil Orbekk <kjetil.orbekk@gmail.com> | 2020-01-29 20:45:21 -0500 |
commit | cbf64a8a5c7d748722369a2ec47c1230650d7b88 (patch) | |
tree | ddb11ce3d1035aeb69fbe47dcda35e8a3876c1f3 /src | |
parent | c8db39dea2cf50dd1fa6c499600e09818b8db44a (diff) |
authentication
Diffstat (limited to 'src')
-rw-r--r-- | src/db.rs | 30 | ||||
-rw-r--r-- | src/error.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 5 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/models.rs | 20 | ||||
-rw-r--r-- | src/schema.rs | 5 | ||||
-rw-r--r-- | src/server.rs | 71 |
7 files changed, 112 insertions, 27 deletions
@@ -1,12 +1,13 @@ -use crate::models; use crate::error::Error; +use crate::models; +use base64; use diesel::connection::Connection; use diesel::pg::PgConnection; +use diesel::ExpressionMethods; +use diesel::QueryDsl; use diesel::RunQueryDsl; -use rand::Rng; use rand; -use base64; -use bcrypt; +use rand::Rng; pub const COST: u32 = 12; @@ -28,9 +29,28 @@ pub fn adduser(conn: &PgConnection, username: &str, password: &str) -> Result<() let hashed = bcrypt::hash(password, COST)?; let rows = diesel::insert_into(users::table) - .values(models::User::new(username, &hashed)).execute(conn)?; + .values(models::User::new(username, &hashed)) + .execute(conn)?; if rows != 1 { Err(Error::AlreadyExists)?; } Ok(()) } + +pub fn authenticate( + conn: &PgConnection, + username: &str, + password: &str, +) -> Result<models::User, Error> { + use crate::schema::users; + + let user: models::User = users::table + .filter(users::username.eq(username)) + .get_result(conn)?; + + if user.verify(password)? { + Ok(user) + } else { + Err(Error::NotFound) + } +} diff --git a/src/error.rs b/src/error.rs index 6ab0741..81647d5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,7 @@ -use std::convert::From; -use std::error::Error as StdError; use bcrypt::BcryptError; use diesel::result::Error as DieselErr; +use std::convert::From; +use std::error::Error as StdError; use std::fmt; #[derive(Debug)] @@ -9,6 +9,7 @@ pub enum Error { DieselError(DieselErr), PasswordError(BcryptError), AlreadyExists, + NotFound, } impl fmt::Display for Error { @@ -17,6 +18,7 @@ impl fmt::Display for Error { Error::DieselError(ref e) => e.fmt(f), Error::PasswordError(ref e) => e.fmt(f), Error::AlreadyExists => f.write_str("AlreadyExists"), + Error::NotFound => f.write_str("NotFound"), } } } @@ -4,6 +4,9 @@ extern crate rocket; #[macro_use] +extern crate rocket_contrib; + +#[macro_use] extern crate diesel; #[derive(Debug)] @@ -13,8 +16,8 @@ pub struct Config { pub base_url: String, } -pub mod error; pub mod db; +pub mod error; pub mod models; mod schema; pub mod server; diff --git a/src/main.rs b/src/main.rs index f88b7e2..6d8693f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,7 +71,7 @@ fn main() { rocket_secret_key: matches.value_of("rocket_secret_key").unwrap(), }; - pjournal::db::create_config(&conn, &config); + pjournal::db::create_config(&conn, &config).unwrap(); } else if let Some(matches) = matches.subcommand_matches("adduser") { let user = matches.value_of("USERNAME").unwrap(); let password = matches.value_of("PASSWORD").unwrap(); diff --git a/src/models.rs b/src/models.rs index 8bee887..28a8b65 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,5 +1,7 @@ +use crate::error::Error; use crate::schema::config; use crate::schema::users; +use bcrypt; #[derive(Insertable, Queryable)] #[table_name = "config"] @@ -11,16 +13,20 @@ pub struct Config<'a> { #[derive(Insertable, Queryable)] #[table_name = "users"] -pub struct User<'a> { - pub username: &'a str, - password: &'a str, +pub struct User { + pub username: String, + password: String, } -impl<'a> User<'a> { - pub fn new(username: &'a str, password: &'a str) -> User<'a> { +impl User { + pub fn new(username: &str, password: &str) -> User { User { - username: username, - password: password, + username: username.to_string(), + password: password.to_string(), } } + + pub fn verify(&self, password: &str) -> Result<bool, Error> { + bcrypt::verify(password, &self.password).map_err(|e| From::from(e)) + } } diff --git a/src/schema.rs b/src/schema.rs index 809706c..055d6d0 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -14,7 +14,4 @@ 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 2c7baad..4f98337 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,17 +1,29 @@ -use rocket::config; +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::Form; +use rocket::request::FromForm; use rocket::response; +use rocket::response::Redirect; use rocket::State; use rocket_contrib::templates::Template; use std::collections::HashMap; +use crate::db; +use crate::error::Error; use crate::strava; pub struct Params { pub base_url: String, } +#[database("db")] +pub struct Db(diesel::PgConnection); + #[get("/")] fn index() -> Template { let mut context = HashMap::new(); @@ -20,6 +32,36 @@ fn index() -> Template { Template::render("index", context) } +#[get("/login?<failed>")] +fn login(failed: bool) -> Template { + let mut context = HashMap::new(); + context.insert("parent", "layout"); + if failed { + context.insert("message", "Incorrect username or password"); + } + Template::render("login", context) +} + +#[derive(FromForm)] +struct LoginData { + username: String, + password: String, +} + +// Request guard for logged in user: https://api.rocket.rs/v0.4/rocket/request/trait.FromRequest.html + +#[post("/login", data = "<data>")] +fn login_submit(conn: Db, data: Form<LoginData>, mut cookies: Cookies) -> Result<Redirect, Error> { + match db::authenticate(&*conn, &data.username, &data.password) { + Ok(user) => { + cookies.add_private(Cookie::new("user", data.username.clone())); + Ok(Redirect::to(uri!(index).to_string())) + }, + Err(Error::NotFound) => Ok(Redirect::to(uri!(login: failed = true).to_string())), + Err(e) => Err(e), + } +} + #[get("/link_strava_callback?<code>")] fn link_strava_callback( config: State<Params>, @@ -30,8 +72,8 @@ fn link_strava_callback( } #[get("/link_strava")] -fn link_strava(config: State<Params>) -> response::Redirect { - response::Redirect::to(format!( +fn link_strava(config: State<Params>) -> Redirect { + Redirect::to(format!( concat!( "https://www.strava.com/oauth/authorize?", "client_id={}&", @@ -45,15 +87,30 @@ fn link_strava(config: State<Params>) -> response::Redirect { )) } -pub fn start(db_url: &str, config: Params) { +pub fn start(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)); - rocket::ignite() - .manage(config) - .mount("/", routes![index, link_strava, link_strava_callback]) + let config = Config::build(Environment::Development) + .extra("databases", databases) + .finalize() + .unwrap(); + + rocket::custom(config) + .manage(params) + .mount( + "/", + routes![ + index, + login, + login_submit, + link_strava, + link_strava_callback + ], + ) .attach(Template::fairing()) + .attach(Db::fairing()) .launch(); } |