fix2 tokens

This commit is contained in:
Mateusz Gruszczyński
2026-08-03 01:35:05 +02:00
parent 49dad1a5f4
commit e3ee6319b9
25 changed files with 1420 additions and 223 deletions
+49
View File
@@ -50,6 +50,15 @@ pub fn resource_token<'a>(headers: &'a HeaderMap, kind: &str, slug: &str) -> Opt
cookie_value(headers, &name)
}
pub fn share_session_token<'a>(
headers: &'a HeaderMap,
kind: &str,
slug: &str,
) -> Option<&'a str> {
let name = share_session_cookie_name(kind, slug);
cookie_value(headers, &name)
}
pub fn session_cookie(token: &str, ttl_days: i64) -> HeaderValue {
secure_cookie(SESSION_COOKIE, token, ttl_days.saturating_mul(86_400))
}
@@ -104,6 +113,23 @@ pub fn resource_cookie(kind: &str, slug: &str, token: &str, ttl_days: i64) -> He
)
}
pub fn share_session_cookie(
kind: &str,
slug: &str,
token: &str,
max_age_seconds: i64,
) -> HeaderValue {
secure_cookie(
&share_session_cookie_name(kind, slug),
token,
max_age_seconds,
)
}
pub fn clear_share_session_cookie(kind: &str, slug: &str) -> HeaderValue {
clear_cookie(&share_session_cookie_name(kind, slug))
}
pub fn client_key(headers: &HeaderMap) -> String {
let forwarded_ip = header_ip(headers, "cf-connecting-ip")
.or_else(|| header_ip(headers, "x-real-ip"))
@@ -161,6 +187,11 @@ fn resource_cookie_name(kind: &str, slug: &str) -> String {
format!("__Host-rustpad_access_{}", hex::encode(&digest[..12]))
}
fn share_session_cookie_name(kind: &str, slug: &str) -> String {
let digest = Sha256::digest(format!("{kind}:{slug}").as_bytes());
format!("__Host-rustpad_share_{}", hex::encode(&digest[..12]))
}
pub fn cookie_value<'a>(headers: &'a HeaderMap, name: &str) -> Option<&'a str> {
headers
.get(header::COOKIE)
@@ -318,6 +349,24 @@ mod tests {
assert!(value.starts_with("__Host-rustpad_session=abc123;"));
}
#[test]
fn share_sessions_use_separate_scoped_opaque_cookies() {
let value = share_session_cookie("workspace", "private-space", "opaque", 600)
.to_str()
.unwrap();
assert!(value.starts_with("__Host-rustpad_share_"));
assert!(value.contains("=opaque;"));
assert!(value.contains("Max-Age=600"));
assert!(value.contains("HttpOnly"));
assert!(value.contains("Secure"));
assert!(value.contains("SameSite=Lax"));
let cleared = clear_share_session_cookie("workspace", "private-space")
.to_str()
.unwrap();
assert!(cleared.contains("Max-Age=0"));
}
#[test]
fn csrf_requires_matching_cookie_and_header() {
let token = "a".repeat(CSRF_TOKEN_BYTES * 2);