summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/db.rs52
-rw-r--r--server/src/main.rs48
2 files changed, 83 insertions, 17 deletions
diff --git a/server/src/db.rs b/server/src/db.rs
index 2bc3f9b..bad748d 100644
--- a/server/src/db.rs
+++ b/server/src/db.rs
@@ -101,16 +101,64 @@ fn generate_login_token() -> String {
base64_encode(&bytes)
}
+fn current_time() -> i64 {
+ SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as i64
+}
+
pub async fn create_login_token(db: DB, username: &str) -> Result<String, ()> {
let mut conn = db.lock().await;
- let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
+ let now = current_time();
let token = generate_login_token();
match sqlx::query("insert into Logins (user, token, expire) values ($1, $2, $3)")
.bind(username)
.bind(&token)
- .bind(now as i64 + LOGIN_TOKEN_EXPIRE_SECS)
+ .bind(now + LOGIN_TOKEN_EXPIRE_SECS)
.execute(conn.deref_mut()).await {
Ok(_) => Ok(token),
Err(_) => Err(()),
}
}
+
+pub async fn drop_token(db: DB, token: &str) {
+ let mut conn = db.lock().await;
+ // ignore errors
+ let _ = sqlx::query("delete from Logins where token = $1")
+ .bind(token)
+ .execute(conn.deref_mut()).await;
+}
+
+async fn set_token_expire(conn: &mut SqliteConnection, token: &str, expire: i64) -> Result<(), String> {
+ match sqlx::query("update Logins set expire = $1 where token = $2")
+ .bind(expire)
+ .bind(token)
+ .execute(conn).await {
+ Ok(_) => Ok(()),
+ Err(err) => {
+ eprintln!("set_token_expire: err = {err}");
+ Err("Server error".to_string())
+ }
+ }
+}
+
+pub async fn maybe_refresh_token(db: DB, token: &str) -> Result<(), String> {
+ let mut conn = db.lock().await;
+ let now = current_time();
+ match sqlx::query("select expire from Logins where token = $1")
+ .bind(token)
+ .fetch_optional(conn.deref_mut()).await {
+ Ok(Some(row)) => {
+ if now >= row.get::<i64, _>(0) - LOGIN_TOKEN_REFRESH_MARGIN {
+ set_token_expire(conn.deref_mut(), token, now + LOGIN_TOKEN_EXPIRE_SECS).await
+ } else {
+ Ok(())
+ }
+ },
+ Ok(None) => {
+ Err("Not logged in".to_string())
+ },
+ Err(err) => {
+ eprintln!("maybe_refresh_token: err = {err}");
+ Err("Server error".to_string())
+ }
+ }
+}
diff --git a/server/src/main.rs b/server/src/main.rs
index 11fddb4..547086b 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -17,18 +17,6 @@ mod constants; use constants::*;
mod util;
mod db; use db::DB;
-async fn handle_ping() -> Result<String, Rejection> {
- return Ok("pong".to_string())
-}
-
-#[derive(Deserialize)]
-struct RegisterReq {
- username: String,
- password: String,
-}
-
-type Response = Result<Box<dyn Reply>, Rejection>;
-
macro_rules! mk_bad_request {
($res:expr) => { Ok(Box::new(warp::reply::with_status($res, warp::http::StatusCode::BAD_REQUEST))) }
}
@@ -41,6 +29,24 @@ macro_rules! mk_server_err {
() => { Ok(Box::new(warp::reply::with_status("Internal server error", warp::http::StatusCode::INTERNAL_SERVER_ERROR))) }
}
+type Response = Result<Box<dyn Reply>, Rejection>;
+
+async fn handle_ping(db: DB, mtoken: Option<String>) -> Response {
+ if let Some(token) = mtoken {
+ match db::maybe_refresh_token(db, &token).await {
+ Ok(()) => {},
+ Err(err) => { return mk_bad_request!(err); },
+ }
+ }
+ return Ok(Box::new("pong"))
+}
+
+#[derive(Deserialize)]
+struct RegisterReq {
+ username: String,
+ password: String,
+}
+
async fn handle_register(db: DB, req: RegisterReq) -> Response {
let salt = SaltString::generate(&mut OsRng);
let hash = match Argon2::default().hash_password(req.password.as_bytes(), &salt) {
@@ -53,7 +59,7 @@ async fn handle_register(db: DB, req: RegisterReq) -> Response {
}
}
-async fn handle_login(db: DB, req: RegisterReq) -> Result<Box<dyn Reply>, Rejection> {
+async fn handle_login(db: DB, req: RegisterReq) -> Response {
let passhash = match db::get_passhash(db.clone(), &req.username).await {
Ok(passhash) => passhash,
Err(()) => return mk_not_found!("User not found"),
@@ -77,6 +83,11 @@ async fn handle_login(db: DB, req: RegisterReq) -> Result<Box<dyn Reply>, Reject
}
}
+async fn handle_logout(db: DB, token: String) -> Response {
+ db::drop_token(db, &token).await;
+ Ok(Box::new("Logged out"))
+}
+
macro_rules! db_handler1 {
($db:expr, $handler:ident) => { { let db2 = $db.clone(); move |a| $handler(db2.clone(), a) } }
}
@@ -86,15 +97,22 @@ async fn main() {
let db: DB = Arc::new(Mutex::new(db::open().await));
println!("Opened database at {DB_FILE_NAME}.");
+ let use_login_token = warp::header::<String>("x-kaasnoot-token");
+ let use_optional_login_token = warp::header::optional::<String>("x-kaasnoot-token");
+
let router =
(warp::get().and(warp::path!("ping"))
- .and_then(handle_ping))
+ .and(use_optional_login_token)
+ .and_then(db_handler1!(db, handle_ping)))
.or(warp::post().and(warp::path!("register"))
.and(warp::body::json())
.and_then(db_handler1!(db, handle_register)))
.or(warp::post().and(warp::path!("login"))
.and(warp::body::json())
- .and_then(db_handler1!(db, handle_login)));
+ .and_then(db_handler1!(db, handle_login)))
+ .or(warp::post().and(warp::path!("logout"))
+ .and(use_login_token)
+ .and_then(db_handler1!(db, handle_logout)));
warp::serve(router)
.run(([0, 0, 0, 0], 8775))