-
Notifications
You must be signed in to change notification settings - Fork 254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Added RequestContextStore, RequestContext and SignedPrivateCookieJar #633
base: master
Are you sure you want to change the base?
Changes from 1 commit
e59c252
73b0738
c683b1c
cdd62d2
77d0f18
39e5ab7
f0b9207
815b562
3b07485
22122b1
47261bc
b4ead0c
b5d10b1
9b05a67
c326037
55d7ac8
e9e0355
aa08161
8ef8e39
96dfdad
41b3253
16368e2
b8684a1
c7778bc
adbf72e
95312b4
715a765
f71d820
8f53ee0
3799d35
b1470a0
c31b842
3fefbec
9aa4cbc
0104015
fad6290
9709afa
538a4e7
edd9fca
8575966
fad4bfb
1e4ff90
660cdfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
use crate::request_context::driver::PRIVATE_COOKIE_NAME; | ||
use axum::http::HeaderMap; | ||
use axum_extra::extract::cookie::Cookie; | ||
use axum_extra::extract::cookie::{Key, PrivateCookieJar, SignedCookieJar}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct SignedPrivateCookieJar { | ||
private_jar: PrivateCookieJar, | ||
signed_jar: SignedCookieJar, | ||
} | ||
|
||
impl SignedPrivateCookieJar { | ||
#[must_use] | ||
pub fn new(private_key: &Key, signed_key: &Key) -> Self { | ||
Self { | ||
private_jar: PrivateCookieJar::new(private_key.clone()), | ||
signed_jar: SignedCookieJar::new(signed_key.clone()), | ||
} | ||
} | ||
|
||
#[must_use] | ||
pub fn from_headers( | ||
Check failure on line 24 in src/request_context/driver/cookie.rs GitHub Actions / Run Clippy
|
||
private_key: &Key, | ||
signed_key: &Key, | ||
headers: &HeaderMap, | ||
) -> Result<Self, SignedPrivateCookieJarError> { | ||
// Create a new instance of the SignedPrivateCookieJar | ||
let signed_jar = SignedCookieJar::from_headers(headers, signed_key.clone()); | ||
// Create a new instance of the PrivateCookieJar | ||
let private_jar = PrivateCookieJar::from_headers(headers, private_key.clone()); | ||
let private_map = private_jar.get(PRIVATE_COOKIE_NAME); | ||
let signed_map = signed_jar.get(PRIVATE_COOKIE_NAME); | ||
match (private_map, signed_map) { | ||
(Some(_), None) => Err(SignedPrivateCookieJarError::FromHeaders( | ||
"Private cookie is present but signed cookie is missing".to_string(), | ||
)), | ||
(None, Some(_)) => Err(SignedPrivateCookieJarError::FromHeaders( | ||
"Signed cookie is present but private cookie is missing".to_string(), | ||
)), | ||
(None, None) => Ok(Self::new(private_key, signed_key)), | ||
(Some(private_cookie), Some(signed_cookie)) => { | ||
if private_cookie.value() == signed_cookie.value() { | ||
Ok(Self { | ||
private_jar, | ||
signed_jar, | ||
}) | ||
} else { | ||
Err(SignedPrivateCookieJarError::FromHeaders( | ||
"Private cookie and signed cookie do not match".to_string(), | ||
)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[must_use] | ||
pub fn add( | ||
Check failure on line 59 in src/request_context/driver/cookie.rs GitHub Actions / Run Clippy
|
||
&mut self, | ||
name: &str, | ||
value: impl Serialize + Send, | ||
) -> Result<(), SignedPrivateCookieJarError> { | ||
// Firstly, get the Hashmap from the private_jar | ||
let mut map: HashMap<String, serde_json::Value> = | ||
if let Some(cookie) = self.private_jar.get(PRIVATE_COOKIE_NAME) { | ||
let cookie_value = cookie.value().to_owned(); | ||
serde_json::from_str(&cookie_value)? | ||
} else { | ||
HashMap::new() | ||
}; | ||
// Insert the value into the Hashmap | ||
map.insert(name.to_owned(), serde_json::to_value(value)?); | ||
// Serialize the updated map back to a string | ||
let updated_cookie_value = serde_json::to_string(&map)?; | ||
// Create a new cookie with the updated value | ||
let new_cookie = Cookie::new(PRIVATE_COOKIE_NAME, updated_cookie_value); | ||
// Add the new cookie to the jar | ||
self.private_jar = self.private_jar.clone().add(new_cookie.clone()); | ||
// Then, sign the encrypted data | ||
if let Some(encrypted_cookie) = self.private_jar.get(PRIVATE_COOKIE_NAME) { | ||
self.signed_jar = self.signed_jar.clone().add(encrypted_cookie.clone()); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[must_use] | ||
pub fn get<T: for<'de> Deserialize<'de>>( | ||
Check failure on line 89 in src/request_context/driver/cookie.rs GitHub Actions / Run Clippy
|
||
&self, | ||
name: &str, | ||
) -> Result<Option<T>, SignedPrivateCookieJarError> { | ||
// Firstly, get the Hashmap from the private_jar | ||
if let Some(cookie) = self.private_jar.get(PRIVATE_COOKIE_NAME) { | ||
let cookie_value = cookie.value().to_owned(); | ||
let map: HashMap<String, serde_json::Value> = serde_json::from_str(&cookie_value)?; | ||
// Deserialize the value from the Hashmap | ||
return Ok(map | ||
.get(name) | ||
.and_then(|value| serde_json::from_value(value.clone()).ok())); | ||
} | ||
Ok(None) | ||
} | ||
|
||
#[must_use] | ||
pub fn remove(&mut self, name: &str) -> Result<(), SignedPrivateCookieJarError> { | ||
Check failure on line 106 in src/request_context/driver/cookie.rs GitHub Actions / Run Clippy
Check failure on line 106 in src/request_context/driver/cookie.rs GitHub Actions / Run Clippy
|
||
// Firstly, get the Hashmap from the private_jar | ||
let mut map: HashMap<String, serde_json::Value> = | ||
if let Some(cookie) = self.private_jar.get(PRIVATE_COOKIE_NAME) { | ||
let cookie_value = cookie.value().to_owned(); | ||
serde_json::from_str(&cookie_value)? | ||
} else { | ||
HashMap::new() | ||
}; | ||
|
||
// Remove the value from the Hashmap | ||
map.remove(name); | ||
// Serialize the updated map back to a string | ||
let updated_cookie_value = serde_json::to_string(&map).unwrap(); | ||
// Create a new cookie with the updated value | ||
let new_cookie = Cookie::new(PRIVATE_COOKIE_NAME, updated_cookie_value); | ||
// Add the new cookie to the jar | ||
self.private_jar = self.private_jar.clone().add(new_cookie.clone()); | ||
// Then, sign the encrypted data | ||
if let Some(encrypted_cookie) = self.private_jar.get(PRIVATE_COOKIE_NAME) { | ||
self.signed_jar = self.signed_jar.clone().add(encrypted_cookie.clone()); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
pub enum SignedPrivateCookieJarError { | ||
#[error("Unable to extract data from cookie")] | ||
ExtractData(#[from] serde_json::Error), | ||
#[error("From headers error")] | ||
FromHeaders(String), | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
use axum_extra::extract::cookie::Cookie; | ||
use axum_extra::extract::cookie::Key; | ||
use std::collections::HashMap; | ||
|
||
#[test] | ||
fn test_signed_private_cookie_jar() -> Result<(), SignedPrivateCookieJarError> { | ||
let (private_key, signed_key) = (Key::generate(), Key::generate()); | ||
let mut jar = SignedPrivateCookieJar::new(&private_key, &signed_key); | ||
let (name, value) = ("foo", "bar".to_string()); | ||
jar.add(name, value.clone())?; | ||
assert_eq!(jar.get::<String>(name)?, Some(value.clone())); | ||
jar.remove(name)?; | ||
assert_eq!(jar.get::<String>(name)?, None); | ||
Ok(()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
pub mod cookie; | ||
|
||
use crate::request_context::driver::cookie::SignedPrivateCookieJar; | ||
use tower_sessions::Session; | ||
|
||
pub const PRIVATE_COOKIE_NAME: &str = "__loco_app_session"; | ||
#[derive(Debug, Clone)] | ||
pub enum Driver { | ||
TowerSession(Session), | ||
SignedPrivateCookieJar(Box<SignedPrivateCookieJar>), | ||
} | ||
|
||
impl Driver {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens to request context when there is no request involved? for example tasks, workers ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, that is a good point