Skip to content
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

add support for session access from guards #234

Merged
merged 2 commits into from Mar 25, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion actix-session/CHANGES.md
@@ -1,11 +1,13 @@
# Changes

## Unreleased - 2021-xx-xx
### Fixed
- Implement `SessionExt` for `GuardContext`. [#234]
- Do not leak internal implementation details to callers when errors occur. [#236]

[#234]: https://github.com/actix/actix-extras/pull/234
[#236]: https://github.com/actix/actix-extras/pull/236


## 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")
robjtede marked this conversation as resolved.
Show resolved Hide resolved
.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())
}
}