Add login

This commit is contained in:
2020-11-13 00:30:39 +01:00
parent fdab612863
commit 9b9cc4e0bd
8 changed files with 333 additions and 24 deletions

62
src/auth.rs Normal file
View File

@ -0,0 +1,62 @@
use crate::routes::pages;
use rocket::{
catch,
http::{Cookie, CookieJar, Status},
post,
request::{Form, FromForm, FromRequest, Outcome, Request},
response::Redirect,
uri, State,
};
use rocket_contrib::templates::Template;
pub struct MasterPassword(String);
impl From<String> for MasterPassword {
fn from(pass: String) -> Self {
Self(pass)
}
}
const AUTH_COOKIE_KEY: &str = "authorized";
const AUTH_COOKIE_VAL: &str = "true";
#[derive(Debug)]
pub struct Authorized;
#[derive(Debug)]
pub struct Unauthorized;
#[rocket::async_trait]
impl<'a, 'r> FromRequest<'a, 'r> for Authorized {
type Error = Unauthorized;
async fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
let cookies = request.cookies();
match cookies.get_private(AUTH_COOKIE_KEY) {
Some(cookie) if cookie.value() == AUTH_COOKIE_VAL => Outcome::Success(Authorized),
_ => Outcome::Failure((Status::Unauthorized, Unauthorized)),
}
}
}
#[catch(401)]
pub fn login_page(_req: &Request) -> Template {
Template::render("login", &())
}
#[derive(FromForm)]
pub struct Login {
password: String,
}
#[post("/login", data = "<login>")]
pub fn login(
cookies: &CookieJar,
login: Form<Login>,
master_pass: State<MasterPassword>,
) -> Redirect {
if login.password == master_pass.0 {
cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, AUTH_COOKIE_VAL));
}
Redirect::to(uri!(pages::index))
}

View File

@ -1,8 +1,10 @@
mod auth;
mod database;
mod handlebars_util;
mod routes;
mod status_json;
use crate::auth::MasterPassword;
use crate::database::migrations::migrate;
use crate::database::unversioned::global::schema_version;
use crate::database::SCHEMA_VERSION;
@ -18,6 +20,13 @@ async fn main() -> io::Result<()> {
let db_path = env::var("DB_PATH").expect("DB_PATH not set");
let master_pass: MasterPassword = env::var("STL_PASSWORD")
.unwrap_or_else(|_| {
eprintln!(r#"STL_PASSWORD not set, defaulting to "password""#);
"password".into()
})
.into();
let mut sled = sled::open(db_path)?;
match sled.insert(
serialize(schema_version::K).unwrap(),
@ -42,6 +51,7 @@ async fn main() -> io::Result<()> {
handlebars_util::register_helpers(engines)
}))
.manage(sled)
.manage(master_pass)
.mount("/static", StaticFiles::from("static"))
.mount(
"/",
@ -56,8 +66,10 @@ async fn main() -> io::Result<()> {
routes::api::end_session,
routes::api::bump_session,
routes::api::delete_session,
auth::login,
],
);
)
.register(rocket::catchers![auth::login_page,]);
rocket.launch().await.expect("rocket failed to launch");

View File

@ -1,3 +1,4 @@
use crate::auth::Authorized;
use crate::database::latest::trees::{categories, sessions};
use crate::routes::pages;
use crate::status_json::StatusJson;
@ -18,6 +19,7 @@ pub struct NewCategory {
#[post("/create_category", data = "<category>")]
pub fn create_category(
_auth: Authorized,
category: Form<NewCategory>,
db: State<'_, sled::Db>,
) -> Result<StatusJson, StatusJson> {
@ -39,21 +41,9 @@ pub fn create_category(
Ok(Status::Ok.into())
}
#[post("/category/<category_uuid>/start_session")]
pub fn start_session(
category_uuid: Uuid,
db: State<'_, sled::Db>,
) -> Result<StatusJson, StatusJson> {
toggle_category_session(category_uuid, true, db)
}
#[post("/category/<category_uuid>/end_session")]
pub fn end_session(category_uuid: Uuid, db: State<'_, sled::Db>) -> Result<StatusJson, StatusJson> {
toggle_category_session(category_uuid, false, db)
}
#[post("/category/<category_uuid>/bump_session/minutes/<minutes>")]
pub fn bump_session(
_auth: Authorized,
category_uuid: Uuid,
minutes: i64,
db: State<'_, sled::Db>,
@ -95,6 +85,24 @@ pub fn bump_session(
})??)
}
#[post("/category/<category_uuid>/start_session")]
pub fn start_session(
_auth: Authorized,
category_uuid: Uuid,
db: State<'_, sled::Db>,
) -> Result<StatusJson, StatusJson> {
toggle_category_session(category_uuid, true, db)
}
#[post("/category/<category_uuid>/end_session")]
pub fn end_session(
_auth: Authorized,
category_uuid: Uuid,
db: State<'_, sled::Db>,
) -> Result<StatusJson, StatusJson> {
toggle_category_session(category_uuid, false, db)
}
pub fn toggle_category_session(
category_uuid: Uuid,
set_active: bool,
@ -156,6 +164,7 @@ pub struct EditSession {
#[post("/session/<session_uuid>/edit", data = "<session>")]
pub fn edit_session(
_auth: Authorized,
session_uuid: Uuid,
session: Form<EditSession>,
db: State<'_, sled::Db>,
@ -195,7 +204,11 @@ pub fn edit_session(
}
#[post("/session/<session_uuid>/delete")]
pub fn delete_session(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Redirect, StatusJson> {
pub fn delete_session(
_auth: Authorized,
session_uuid: Uuid,
db: State<'_, sled::Db>,
) -> Result<Redirect, StatusJson> {
let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?);
let sessions_tree = db.open_tree(sessions::NAME)?;

View File

@ -1,3 +1,4 @@
use crate::auth::Authorized;
use crate::database::latest::trees::{categories, sessions};
use crate::status_json::StatusJson;
use bincode::deserialize;
@ -12,7 +13,7 @@ use std::collections::{BTreeMap, HashMap};
use std::time::Duration;
#[get("/")]
pub fn index(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
pub fn index(_auth: Authorized, db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
#[derive(Debug, Serialize, Deserialize)]
struct TemplateContext {
categories: Vec<(uuid::Uuid, categories::V)>,
@ -33,7 +34,11 @@ pub fn index(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
}
#[get("/session/<session_uuid>/edit")]
pub fn session_edit(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
pub fn session_edit(
_auth: Authorized,
session_uuid: Uuid,
db: State<'_, sled::Db>,
) -> Result<Template, StatusJson> {
#[derive(Debug, Serialize, Deserialize)]
struct SessionPageContext {
session: sessions::V,
@ -57,7 +62,7 @@ pub fn session_edit(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result<Templ
}
#[get("/history")]
pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
pub fn history(_auth: Authorized, db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
#[derive(Debug, Serialize, Deserialize)]
struct HistoryEntryContext {
category: categories::V,
@ -108,7 +113,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
}
#[get("/stats")]
pub fn stats(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
pub fn stats(_auth: Authorized, db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
#[derive(Debug, Serialize, Deserialize)]
struct StatsContext {
categories_stats: Vec<CategoryStatsContext>,