Skip to content
This repository was archived by the owner on Jan 31, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ build_img:
docker build --platform linux/arm64 -t storiny_api .

fmt:
cargo fmt
cargo +nightly fmt

test:
cargo nextest run --workspace
Expand All @@ -29,4 +29,4 @@ update_geo:
docker compose -f geo/docker-compose.yaml up

migrate:
sqlx migrate run
sqlx migrate run
2 changes: 1 addition & 1 deletion macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use quote::{
quote,
};
use syn::{
FnArg,
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
FnArg,
};

/// Macro to use on tests to add the setup/teardown functionality.
Expand Down
1 change: 1 addition & 0 deletions redis_namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
| `s` | `session` | `s:user_id:session_key` | Persistent cookie session |
| `rl` | `resource_limit` | `rl:resource_type:user_id` | Daily resource limit for a user |
| `r` | `reading_session` | `r:story_id:session_uuid` | Story reading session for a user. These sessions use a session UUID, sent when the user starts reading a public story. It is then returned to the API server upon leaving the page. This key is used to determine the total reading duration by computing the difference between its creation time and the current time. |
| `bl` | `blog_login` | `bl:hashed_token` | User login verification token for blogs hosted on external domain. |
| `l:lgn` | `lock:login` | `l:lgn:email` | Login resource lock, enforced when the login process encounters too many failed attempts for a user. |
| `l:sgn` | `lock:signup` | `l:sgn:client_up` | Signup resource lock, enforced when too many users register from the same IP address in a short duration of time. |
| `l:rcv` | `lock:recovery` | `l:rcv:email` | Account recovery resource lock, enforced when there are too many recovery requests for an account associated with a specific email address. |
Expand Down
8 changes: 3 additions & 5 deletions session/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
//! Configuration options to tune the behaviour of [`SessionMiddleware`].

use actix_web::cookie::{
time::Duration,
Key,
SameSite,
time::Duration,
};
use serde::{
Deserialize,
Serialize,
};

use crate::{
storage::SessionStore,
SessionMiddleware,
storage::SessionStore,
};

/// Determines what type of session cookie should be used and how its lifecycle should be managed.
Expand Down Expand Up @@ -108,8 +108,6 @@ impl<Store: SessionStore> SessionMiddlewareBuilder<Store> {
///
/// Use `None` to leave the attribute unspecified. If unspecified, the attribute defaults
/// to the same host that set the cookie, excluding subdomains.
///
/// By default, the attribute is left unspecified.
pub fn cookie_domain(mut self, domain: Option<String>) -> Self {
self.configuration.cookie.domain = domain;
self
Expand Down Expand Up @@ -162,7 +160,7 @@ pub(crate) fn default_configuration(key: Key) -> Configuration {
secure: true,
http_only: true,
name: "id".into(),
same_site: SameSite::Lax,
same_site: SameSite::Strict,
path: "/".into(),
domain: None,
max_age: None,
Expand Down
59 changes: 50 additions & 9 deletions session/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ mod session_ext;
pub mod storage;

pub use self::{
middleware::SessionMiddleware,
middleware::{
EXTERNAL_BLOG_KEY,
LIFECYCLE_KEY,
SessionMiddleware,
},
session::{
Session,
SessionGetError,
Expand Down Expand Up @@ -53,6 +57,7 @@ pub mod test_helpers {
F: Fn() -> Store + Clone + Send + 'static,
{
acceptance_tests::basic_workflow(store_builder.clone()).await;
acceptance_tests::authorization_header(store_builder.clone()).await;
acceptance_tests::expiration_is_refreshed_on_changes(store_builder.clone()).await;
acceptance_tests::complex_workflow(store_builder.clone(), is_invalidation_supported).await;
acceptance_tests::lifecycle(store_builder.clone()).await;
Expand All @@ -61,41 +66,42 @@ pub mod test_helpers {

mod acceptance_tests {
use actix_web::{
App,
HttpResponse,
Result,
cookie::time,
dev::{
Service,
ServiceResponse,
},
guard,
http::header::AUTHORIZATION,
middleware,
test,
web::{
self,
Bytes,
get,
post,
resource,
Bytes,
},
App,
HttpResponse,
Result,
};
use serde::{
Deserialize,
Serialize,
};
use serde_json::{
json,
Value,
json,
};

use crate::{
config::SessionLifecycle,
storage::SessionStore,
test_helpers::key,
Session,
SessionExt,
SessionMiddleware,
config::SessionLifecycle,
storage::SessionStore,
test_helpers::key,
};

pub(super) async fn basic_workflow<F, Store>(store_builder: F)
Expand Down Expand Up @@ -137,6 +143,41 @@ pub mod test_helpers {
assert_eq!(body, Bytes::from_static(b"counter: 100"));
}

pub(super) async fn authorization_header<F, Store>(store_builder: F)
where
Store: SessionStore + 'static,
F: Fn() -> Store + Clone + Send + 'static,
{
let app = test::init_service(
App::new()
.wrap(
SessionMiddleware::builder(store_builder(), key())
.session_ttl(time::Duration::seconds(100))
.build(),
)
.service(web::resource("/").to(|ses: Session| async move {
ses.insert("counter", Value::from(10));
"test"
}))
.service(web::resource("/test/").to(|ses: Session| async move {
let val: usize = ses.get("counter").unwrap().unwrap();
format!("counter: {val}")
})),
)
.await;

let request = test::TestRequest::get().to_request();
let response = app.call(request).await.unwrap();
let cookie = response.get_cookie("id").expect("Cookie is set");
let token = cookie.value();

let request = test::TestRequest::with_uri("/test/")
.insert_header((AUTHORIZATION, format!("Bearer {token}")))
.to_request();
let body = test::call_and_read_body(&app, request).await;
assert_eq!(body, Bytes::from_static(b"counter: 10"));
}

pub(super) async fn expiration_is_refreshed_on_changes<F, Store>(store_builder: F)
where
Store: SessionStore + 'static,
Expand Down
Loading
Loading