Import and update the file service application and orizentic #72

Merged
savanni merged 36 commits from file-service into main 2023-10-03 23:50:54 +00:00
6 changed files with 104 additions and 24 deletions
Showing only changes of commit b3bfa84691 - Show all commits

4
.gitignore vendored
View File

@ -4,4 +4,6 @@ node_modules
dist dist
result result
*.tgz *.tgz
file-service/auth.sqlite file-service/*.sqlite
file-service/*.sqlite-shm
file-service/*.sqlite-wal

51
Cargo.lock generated
View File

@ -416,7 +416,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.29", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -471,6 +471,16 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "cookie"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
dependencies = [
"time 0.3.29",
"version_check 0.9.4",
]
[[package]] [[package]]
name = "cool_asserts" name = "cool_asserts"
version = "2.0.3" version = "2.0.3"
@ -661,6 +671,12 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "deranged"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@ -842,6 +858,7 @@ dependencies = [
"bytes", "bytes",
"chrono", "chrono",
"clap 4.4.6", "clap 4.4.6",
"cookie",
"cool_asserts", "cool_asserts",
"futures-util", "futures-util",
"hex-string", "hex-string",
@ -1744,7 +1761,7 @@ dependencies = [
"log 0.3.9", "log 0.3.9",
"mime 0.2.6", "mime 0.2.6",
"num_cpus", "num_cpus",
"time", "time 0.1.45",
"traitobject", "traitobject",
"typeable", "typeable",
"unicase 1.4.2", "unicase 1.4.2",
@ -2178,7 +2195,7 @@ checksum = "6c9172cb4c2f6c52117e25570983edcbb322f130b1031ae5d5d6b1abe7eeb493"
dependencies = [ dependencies = [
"iron", "iron",
"log 0.3.9", "log 0.3.9",
"time", "time 0.1.45",
] ]
[[package]] [[package]]
@ -3942,6 +3959,34 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "time"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe"
dependencies = [
"deranged",
"itoa",
"serde 1.0.188",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
dependencies = [
"time-core",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.7.4" version = "0.7.4"

View File

@ -25,9 +25,12 @@ required-features = [ "auth-cli" ]
[target.auth-cli.dependencies] [target.auth-cli.dependencies]
[dependencies] [dependencies]
base64ct = { version = "1", features = [ "alloc" ] }
build_html = { version = "2" } build_html = { version = "2" }
bytes = { version = "1" } bytes = { version = "1" }
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4", features = [ "derive" ], optional = true }
cookie = { version = "0.17" }
futures-util = { version = "0.3" } futures-util = { version = "0.3" }
hex-string = "0.1.0" hex-string = "0.1.0"
http = { version = "0.2" } http = { version = "0.2" }
@ -45,8 +48,6 @@ thiserror = "1.0.20"
tokio = { version = "1", features = [ "full" ] } tokio = { version = "1", features = [ "full" ] }
uuid = { version = "0.4", features = [ "serde", "v4" ] } uuid = { version = "0.4", features = [ "serde", "v4" ] }
warp = { version = "0.3" } warp = { version = "0.3" }
base64ct = { version = "1", features = [ "alloc" ] }
clap = { version = "4", features = [ "derive" ], optional = true }
[dev-dependencies] [dev-dependencies]
cool_asserts = { version = "2" } cool_asserts = { version = "2" }

View File

@ -10,9 +10,9 @@ pub async fn handle_index(
token: Option<SessionToken>, token: Option<SessionToken>,
) -> Result<Response<String>, Error> { ) -> Result<Response<String>, Error> {
match token { match token {
Some(token) => match app.auth_session(token).await { Some(token) => match app.validate_session(token).await {
Ok(_) => render_gallery_page(app).await, Ok(_) => render_gallery_page(app).await,
Err(err) => render_auth_page(Some(format!("authentication refused: {:?}", err))), Err(err) => render_auth_page(Some(format!("session expired: {:?}", err))),
}, },
None => render_auth_page(None), None => render_auth_page(None),
} }
@ -76,10 +76,21 @@ pub async fn handle_auth(
) -> Result<http::Response<String>, Error> { ) -> Result<http::Response<String>, Error> {
match form.get("token") { match form.get("token") {
Some(token) => match app Some(token) => match app
.auth_token(AuthToken::from(AuthToken::from(token.clone()))) .authenticate(AuthToken::from(AuthToken::from(token.clone())))
.await .await
{ {
Ok(_session_token) => render_gallery_page(app).await, Ok(Some(session_token)) => Response::builder()
.header("location", "/")
.header(
"set-cookie",
format!(
"session={}; Secure; HttpOnly; SameSite=Strict",
session_token.to_string()
),
)
.status(StatusCode::SEE_OTHER)
.body("".to_owned()),
Ok(None) => render_auth_page(Some(format!("no user found"))),
Err(_) => render_auth_page(Some(format!("invalid auth token"))), Err(_) => render_auth_page(Some(format!("invalid auth token"))),
}, },
None => render_auth_page(Some(format!("no token available"))), None => render_auth_page(Some(format!("no token available"))),

View File

@ -6,9 +6,10 @@ use http::status::StatusCode;
// use mustache::{compile_path, Template}; // use mustache::{compile_path, Template};
// use orizentic::{Permissions, ResourceName, Secret}; // use orizentic::{Permissions, ResourceName, Secret};
use bytes::Buf; use bytes::Buf;
use cookie::Cookie;
use futures_util::StreamExt; use futures_util::StreamExt;
use std::{ use std::{
collections::HashSet, collections::{HashMap, HashSet},
convert::Infallible, convert::Infallible,
io::Read, io::Read,
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
@ -126,12 +127,15 @@ impl App {
} }
} }
pub async fn auth_token(&self, token: AuthToken) -> Result<Option<SessionToken>, AuthError> { pub async fn authenticate(&self, token: AuthToken) -> Result<Option<SessionToken>, AuthError> {
self.authdb.read().await.auth_token(token).await self.authdb.read().await.authenticate(token).await
} }
pub async fn auth_session(&self, token: SessionToken) -> Result<Option<Username>, AuthError> { pub async fn validate_session(
self.authdb.read().await.auth_session(token).await &self,
token: SessionToken,
) -> Result<Option<Username>, AuthError> {
self.authdb.read().await.validate_session(token).await
} }
pub async fn list_files(&self) -> Result<HashSet<FileId>, ReadFileError> { pub async fn list_files(&self) -> Result<HashSet<FileId>, ReadFileError> {
@ -159,9 +163,23 @@ fn maybe_with_session() -> impl Filter<Extract = (Option<SessionToken>,), Error
{ {
warp::any() warp::any()
.and(warp::header::optional::<String>("cookie")) .and(warp::header::optional::<String>("cookie"))
.map(|cookies| { .map(|cookies| match cookies {
println!("cookies retrieved: {:?}", cookies); Some(cookies) => {
let c = Cookie::split_parse(cookies)
.collect::<Result<Vec<Cookie>, cookie::ParseError>>();
match c {
Ok(cookies) => {
for c in cookies {
if c.name() == "session" {
return Some(SessionToken::from(c.value()));
}
}
None None
}
Err(_) => None,
}
}
None => None,
}) })
} }

View File

@ -278,7 +278,7 @@ impl AuthDB {
Ok(usernames) Ok(usernames)
} }
pub async fn auth_token(&self, token: AuthToken) -> Result<Option<SessionToken>, AuthError> { pub async fn authenticate(&self, token: AuthToken) -> Result<Option<SessionToken>, AuthError> {
let results = sqlx::query("SELECT * FROM users WHERE token = $1") let results = sqlx::query("SELECT * FROM users WHERE token = $1")
.bind(token.to_string()) .bind(token.to_string())
.fetch_all(&self.pool) .fetch_all(&self.pool)
@ -308,7 +308,10 @@ impl AuthDB {
Ok(Some(SessionToken::from(session_token))) Ok(Some(SessionToken::from(session_token)))
} }
pub async fn auth_session(&self, token: SessionToken) -> Result<Option<Username>, AuthError> { pub async fn validate_session(
&self,
token: SessionToken,
) -> Result<Option<Username>, AuthError> {
let rows = sqlx::query( let rows = sqlx::query(
"SELECT users.username FROM sessions INNER JOIN users ON sessions.user_id = users.id WHERE sessions.token = $1", "SELECT users.username FROM sessions INNER JOIN users ON sessions.user_id = users.id WHERE sessions.token = $1",
) )
@ -498,7 +501,7 @@ mod authdb_test {
let token = AuthToken::from("0000000000"); let token = AuthToken::from("0000000000");
assert_matches!(db.auth_token(token).await, Ok(None)); assert_matches!(db.authenticate(token).await, Ok(None));
} }
#[tokio::test] #[tokio::test]
@ -511,7 +514,7 @@ mod authdb_test {
.await .await
.expect("user to be created"); .expect("user to be created");
assert_matches!(db.auth_token(token).await, Ok(_)); assert_matches!(db.authenticate(token).await, Ok(_));
} }
#[tokio::test] #[tokio::test]
@ -524,13 +527,13 @@ mod authdb_test {
.await .await
.expect("user to be created"); .expect("user to be created");
let session = db let session = db
.auth_token(token) .authenticate(token)
.await .await
.expect("token authentication should succeed") .expect("token authentication should succeed")
.expect("session token should be found"); .expect("session token should be found");
assert_matches!( assert_matches!(
db.auth_session(session).await, db.validate_session(session).await,
Ok(Some(username)) => { Ok(Some(username)) => {
assert_eq!(username, Username::from("savanni")); assert_eq!(username, Username::from("savanni"));
}); });