Skip to content

Commit

Permalink
add support for session access from guards
Browse files Browse the repository at this point in the history
  • Loading branch information
tglman committed Mar 23, 2022
1 parent bf41b4c commit 62cb7ae
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 2 deletions.
1 change: 1 addition & 0 deletions actix-session/CHANGES.md
Expand Up @@ -2,6 +2,7 @@

## Unreleased - 2021-xx-xx

- Implement `SessionExt` for `GuardContext`.

## 0.6.1 - 2022-03-21
- No significant changes since `0.6.0`.
Expand Down
97 changes: 95 additions & 2 deletions actix-session/src/lib.rs
Expand Up @@ -193,13 +193,14 @@ pub mod test_helpers {
*policy,
)
.await;
acceptance_tests::guard(store_builder.clone(), *policy).await;
}
}

mod acceptance_tests {
use actix_web::{
dev::Service,
middleware, test,
guard, middleware, test,
web::{self, get, post, resource, Bytes},
App, HttpResponse, Result,
};
Expand All @@ -209,7 +210,7 @@ pub mod test_helpers {

use crate::{
middleware::SessionLength, storage::SessionStore, test_helpers::key,
CookieContentSecurity, Session, SessionMiddleware,
CookieContentSecurity, Session, SessionExt, SessionMiddleware,
};

pub(super) async fn basic_workflow<F, Store>(
Expand Down Expand Up @@ -308,6 +309,91 @@ pub mod test_helpers {
assert_eq!(cookie_2.max_age(), Some(Duration::seconds(60)));
}

pub(super) async fn guard<F, Store>(store_builder: F, policy: CookieContentSecurity)
where
Store: SessionStore + 'static,
F: Fn() -> Store + Clone + Send + 'static,
{
let srv = actix_test::start(move || {
App::new()
.wrap(
SessionMiddleware::builder(store_builder(), key())
.cookie_name("test-session".into())
.cookie_content_security(policy)
.session_length(SessionLength::Predetermined {
max_session_length: Some(time::Duration::days(7)),
})
.build(),
)
.wrap(middleware::Logger::default())
.service(resource("/").route(get().to(index)))
.service(resource("/do_something").route(post().to(do_something)))
.service(resource("/login").route(post().to(login)))
.service(resource("/logout").route(post().to(logout)))
.service(
web::scope("/protected")
.guard(guard::fn_guard(|g| {
g.get_session().get::<String>("user_id").unwrap().is_some()
}))
.service(resource("/count").route(get().to(count))),
)
});

// Step 1: GET without session info
// - response should be a unsuccessful status
let req_1 = srv.get("/protected/count").send();
let resp_1 = req_1.await.unwrap();
assert!(!resp_1.status().is_success());

// Step 2: POST to login
// - set-cookie actix-session will be in response (session cookie #1)
// - updates session state: {"counter": 0, "user_id": "ferris"}
let req_2 = srv.post("/login").send_json(&json!({"user_id": "ferris"}));
let resp_2 = req_2.await.unwrap();
let cookie_1 = resp_2
.cookies()
.unwrap()
.clone()
.into_iter()
.find(|c| c.name() == "test-session")
.unwrap();

// Step 3: POST to do_something
// - adds new session state: {"counter": 1, "user_id": "ferris" }
// - set-cookie actix-session should be in response (session cookie #2)
// - response should be: {"counter": 1, "user_id": None}
let req_3 = srv.post("/do_something").cookie(cookie_1.clone()).send();
let mut resp_3 = req_3.await.unwrap();
let result_3 = resp_3.json::<IndexResponse>().await.unwrap();
assert_eq!(
result_3,
IndexResponse {
user_id: Some("ferris".into()),
counter: 1
}
);
let cookie_2 = resp_3
.cookies()
.unwrap()
.clone()
.into_iter()
.find(|c| c.name() == "test-session")
.unwrap();

// Step 4: GET using a existing user id
// - response should be: {"counter": 3, "user_id": "ferris"}
let req_4 = srv.get("/protected/count").cookie(cookie_2.clone()).send();
let mut resp_4 = req_4.await.unwrap();
let result_4 = resp_4.json::<IndexResponse>().await.unwrap();
assert_eq!(
result_4,
IndexResponse {
user_id: Some("ferris".into()),
counter: 1
}
);
}

pub(super) async fn complex_workflow<F, Store>(
store_builder: F,
is_invalidation_supported: bool,
Expand Down Expand Up @@ -549,6 +635,13 @@ pub mod test_helpers {
Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter }))
}

async fn count(session: Session) -> Result<HttpResponse> {
let user_id: Option<String> = session.get::<String>("user_id").unwrap();
let counter: i32 = session.get::<i32>("counter").unwrap().unwrap();

Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter }))
}

#[derive(Deserialize)]
struct Identity {
user_id: String,
Expand Down
7 changes: 7 additions & 0 deletions actix-session/src/session_ext.rs
@@ -1,5 +1,6 @@
use actix_web::{
dev::{ServiceRequest, ServiceResponse},
guard::GuardContext,
HttpMessage, HttpRequest,
};

Expand Down Expand Up @@ -29,3 +30,9 @@ impl SessionExt for ServiceResponse {
self.request().get_session()
}
}

impl<'a> SessionExt for GuardContext<'a> {
fn get_session(&self) -> Session {
Session::get_session(&mut *self.req_data_mut())
}
}

0 comments on commit 62cb7ae

Please sign in to comment.