summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKjetil Orbekk <kjetil.orbekk@gmail.com>2020-01-29 20:45:21 -0500
committerKjetil Orbekk <kjetil.orbekk@gmail.com>2020-01-29 20:45:21 -0500
commitcbf64a8a5c7d748722369a2ec47c1230650d7b88 (patch)
treeddb11ce3d1035aeb69fbe47dcda35e8a3876c1f3 /src
parentc8db39dea2cf50dd1fa6c499600e09818b8db44a (diff)
authentication
Diffstat (limited to 'src')
-rw-r--r--src/db.rs30
-rw-r--r--src/error.rs6
-rw-r--r--src/lib.rs5
-rw-r--r--src/main.rs2
-rw-r--r--src/models.rs20
-rw-r--r--src/schema.rs5
-rw-r--r--src/server.rs71
7 files changed, 112 insertions, 27 deletions
diff --git a/src/db.rs b/src/db.rs
index 71490d8..6014db9 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -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"),
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 5f8118b..60d89cd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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();
}