1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +01:00

add support for session access from guards (#234)

This commit is contained in:
tglman 2022-03-25 18:25:38 +00:00 committed by GitHub
parent 8db1088345
commit 4d77e26e1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 3 deletions

View File

@ -1,11 +1,13 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Fixed - Implement `SessionExt` for `GuardContext`. [#234]
- Do not leak internal implementation details to callers when errors occur. [#236] - 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 [#236]: https://github.com/actix/actix-extras/pull/236
## 0.6.1 - 2022-03-21 ## 0.6.1 - 2022-03-21
- No significant changes since `0.6.0`. - No significant changes since `0.6.0`.

View File

@ -193,13 +193,14 @@ pub mod test_helpers {
*policy, *policy,
) )
.await; .await;
acceptance_tests::guard(store_builder.clone(), *policy).await;
} }
} }
mod acceptance_tests { mod acceptance_tests {
use actix_web::{ use actix_web::{
dev::Service, dev::Service,
middleware, test, guard, middleware, test,
web::{self, get, post, resource, Bytes}, web::{self, get, post, resource, Bytes},
App, HttpResponse, Result, App, HttpResponse, Result,
}; };
@ -209,7 +210,7 @@ pub mod test_helpers {
use crate::{ use crate::{
middleware::SessionLength, storage::SessionStore, test_helpers::key, middleware::SessionLength, storage::SessionStore, test_helpers::key,
CookieContentSecurity, Session, SessionMiddleware, CookieContentSecurity, Session, SessionExt, SessionMiddleware,
}; };
pub(super) async fn basic_workflow<F, Store>( pub(super) async fn basic_workflow<F, Store>(
@ -308,6 +309,91 @@ pub mod test_helpers {
assert_eq!(cookie_2.max_age(), Some(Duration::seconds(60))); 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>( pub(super) async fn complex_workflow<F, Store>(
store_builder: F, store_builder: F,
is_invalidation_supported: bool, is_invalidation_supported: bool,
@ -549,6 +635,13 @@ pub mod test_helpers {
Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter })) 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)] #[derive(Deserialize)]
struct Identity { struct Identity {
user_id: String, user_id: String,

View File

@ -1,5 +1,6 @@
use actix_web::{ use actix_web::{
dev::{ServiceRequest, ServiceResponse}, dev::{ServiceRequest, ServiceResponse},
guard::GuardContext,
HttpMessage, HttpRequest, HttpMessage, HttpRequest,
}; };
@ -29,3 +30,9 @@ impl SessionExt for ServiceResponse {
self.request().get_session() self.request().get_session()
} }
} }
impl<'a> SessionExt for GuardContext<'a> {
fn get_session(&self) -> Session {
Session::get_session(&mut *self.req_data_mut())
}
}