Validate the session token
A previous commit added authentication token checks. Auth tokens are replaced with session tokens, which can (and should) expire. This commit validates sessions, which now allows access to gated operations.
This commit is contained in:
parent
529f22f49e
commit
09df915296
|
@ -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
|
||||||
|
|
|
@ -349,7 +349,7 @@ dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde 1.0.188",
|
"serde 1.0.188",
|
||||||
"time",
|
"time 0.1.45",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
@ -478,6 +478,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"
|
||||||
|
@ -672,6 +682,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"
|
||||||
|
@ -853,6 +869,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",
|
||||||
|
@ -1770,7 +1787,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",
|
||||||
|
@ -2204,7 +2221,7 @@ checksum = "6c9172cb4c2f6c52117e25570983edcbb322f130b1031ae5d5d6b1abe7eeb493"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iron",
|
"iron",
|
||||||
"log 0.3.9",
|
"log 0.3.9",
|
||||||
"time",
|
"time 0.1.45",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3955,6 +3972,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.1"
|
version = "0.7.1"
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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"))),
|
||||||
|
|
|
@ -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) => {
|
||||||
None
|
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
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue