From 959eef05ae2bb1059162df728c9747cd7998fe4b Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 12 Jun 2019 08:03:27 -0400 Subject: [PATCH 01/12] updated actix-session to support login and logout functionality (renew and purge) --- actix-session/src/lib.rs | 62 ++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index fb316f394..30d71552f 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -98,10 +98,23 @@ impl UserSession for ServiceRequest { } } +#[derive(PartialEq, Clone, Debug)] +pub enum SessionStatus { + Changed, + Purged, + Renewed, + Unchanged +} +impl Default for SessionStatus { + fn default() -> SessionStatus { + SessionStatus::Unchanged + } +} + #[derive(Default)] struct SessionInner { state: HashMap, - changed: bool, + pub status: SessionStatus, } impl Session { @@ -117,7 +130,7 @@ impl Session { /// Set a `value` from the session. pub fn set(&self, key: &str, value: T) -> Result<(), Error> { let mut inner = self.0.borrow_mut(); - inner.changed = true; + inner.status = SessionStatus::Changed; inner .state .insert(key.to_owned(), serde_json::to_string(&value)?); @@ -127,17 +140,30 @@ impl Session { /// Remove value from the session. pub fn remove(&self, key: &str) { let mut inner = self.0.borrow_mut(); - inner.changed = true; + inner.status = SessionStatus::Changed; inner.state.remove(key); } /// Clear the session. pub fn clear(&self) { let mut inner = self.0.borrow_mut(); - inner.changed = true; + inner.status = SessionStatus::Changed; inner.state.clear() } + /// Removes session, both client and server side. + pub fn purge(&self) { + let mut inner = self.0.borrow_mut(); + inner.status = SessionStatus::Purged; + inner.state.clear(); + } + + /// Renews the session key, assigning existing session state to new key. + pub fn renew(&self) { + let mut inner = self.0.borrow_mut(); + inner.status = SessionStatus::Renewed; + } + pub fn set_session( data: impl Iterator, req: &mut ServiceRequest, @@ -149,7 +175,7 @@ impl Session { pub fn get_changes( res: &mut ServiceResponse, - ) -> Option> { + ) -> (SessionStatus, Option>) { if let Some(s_impl) = res .request() .extensions() @@ -157,9 +183,9 @@ impl Session { { let state = std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new()); - Some(state.into_iter()) + (s_impl.borrow().status.clone(), Some(state.into_iter())) } else { - None + (SessionStatus::Unchanged, None) } } @@ -224,7 +250,8 @@ mod tests { session.remove("key"); let mut res = req.into_response(HttpResponse::Ok().finish()); - let changes: Vec<_> = Session::get_changes(&mut res).unwrap().collect(); + let (_status, state) = Session::get_changes(&mut res); + let changes: Vec<_> = state.unwrap().collect(); assert_eq!(changes, [("key2".to_string(), "\"value2\"".to_string())]); } @@ -241,4 +268,23 @@ mod tests { let res = session.get::("key").unwrap(); assert_eq!(res, Some("value".to_string())); } + + #[test] + fn purge_session() { + let mut req = test::TestRequest::default().to_srv_request(); + let session = Session::get_session(&mut *req.extensions_mut()); + assert_eq!(session.0.borrow().status, SessionStatus::Unchanged); + session.purge(); + assert_eq!(session.0.borrow().status, SessionStatus::Purged); + } + + + #[test] + fn renew_session() { + let mut req = test::TestRequest::default().to_srv_request(); + let session = Session::get_session(&mut *req.extensions_mut()); + assert_eq!(session.0.borrow().status, SessionStatus::Unchanged); + session.renew(); + assert_eq!(session.0.borrow().status, SessionStatus::Renewed); + } } From 65732197b8f1e3abdf8847edcbe24ca70f8a21b5 Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 12 Jun 2019 10:11:38 -0400 Subject: [PATCH 02/12] modified so as to consider unanticipated state changes --- actix-session/src/lib.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index 30d71552f..aaf0ab02f 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -130,25 +130,31 @@ impl Session { /// Set a `value` from the session. pub fn set(&self, key: &str, value: T) -> Result<(), Error> { let mut inner = self.0.borrow_mut(); - inner.status = SessionStatus::Changed; - inner - .state - .insert(key.to_owned(), serde_json::to_string(&value)?); + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Changed; + inner + .state + .insert(key.to_owned(), serde_json::to_string(&value)?); + } Ok(()) } /// Remove value from the session. pub fn remove(&self, key: &str) { let mut inner = self.0.borrow_mut(); - inner.status = SessionStatus::Changed; - inner.state.remove(key); + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Changed; + inner.state.remove(key); + } } /// Clear the session. pub fn clear(&self) { let mut inner = self.0.borrow_mut(); - inner.status = SessionStatus::Changed; - inner.state.clear() + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Changed; + inner.state.clear() + } } /// Removes session, both client and server side. @@ -161,7 +167,9 @@ impl Session { /// Renews the session key, assigning existing session state to new key. pub fn renew(&self) { let mut inner = self.0.borrow_mut(); - inner.status = SessionStatus::Renewed; + if inner.status != SessionStatus::Purged { + inner.status = SessionStatus::Renewed; + } } pub fn set_session( From ca4ed0932e880bdd44dd971a5248a5867679305e Mon Sep 17 00:00:00 2001 From: dowwie Date: Thu, 13 Jun 2019 08:59:59 -0400 Subject: [PATCH 03/12] made Session::get_session public --- actix-session/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index aaf0ab02f..beee6b07e 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -197,7 +197,7 @@ impl Session { } } - fn get_session(extensions: &mut Extensions) -> Session { + pub fn get_session(extensions: &mut Extensions) -> Session { if let Some(s_impl) = extensions.get::>>() { return Session(Rc::clone(&s_impl)); } From 32a66a99bf71d621e55f4bc83100ba3d56f2912a Mon Sep 17 00:00:00 2001 From: dowwie Date: Thu, 13 Jun 2019 09:19:03 -0400 Subject: [PATCH 04/12] reverting change to get_session due to side effects --- actix-session/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index beee6b07e..aaf0ab02f 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -197,7 +197,7 @@ impl Session { } } - pub fn get_session(extensions: &mut Extensions) -> Session { + fn get_session(extensions: &mut Extensions) -> Session { if let Some(s_impl) = extensions.get::>>() { return Session(Rc::clone(&s_impl)); } From fa7e0fe6df27c98da0117384b1e9ac5864b736d1 Mon Sep 17 00:00:00 2001 From: dowwie Date: Mon, 24 Jun 2019 18:40:14 -0400 Subject: [PATCH 05/12] updated cookie.rs req to get_changes --- actix-session/src/cookie.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index ac08d1146..55904f5bd 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -308,7 +308,7 @@ where Session::set_session(state.into_iter(), &mut req); Box::new(self.service.call(req).map(move |mut res| { - if let Some(state) = Session::get_changes(&mut res) { + if let (_status, Some(state)) = Session::get_changes(&mut res) { res.checked_expr(|res| inner.set_cookie(res, state)) } else { res From 0e05b37082fa57444858b46e1a8b57b731777f1c Mon Sep 17 00:00:00 2001 From: dowwie Date: Sat, 29 Jun 2019 14:24:02 -0400 Subject: [PATCH 06/12] updated cookie session to update on change --- actix-session/src/cookie.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index 55904f5bd..de1faea96 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -28,7 +28,7 @@ use futures::future::{ok, Future, FutureResult}; use futures::Poll; use serde_json::error::Error as JsonError; -use crate::Session; +use crate::{Session, SessionStatus}; /// Errors that can occur during handling cookie session #[derive(Debug, From, Display)] @@ -308,10 +308,10 @@ where Session::set_session(state.into_iter(), &mut req); Box::new(self.service.call(req).map(move |mut res| { - if let (_status, Some(state)) = Session::get_changes(&mut res) { - res.checked_expr(|res| inner.set_cookie(res, state)) - } else { - res + match Session::get_changes(&mut res) { + (SessionStatus::Changed, Some(state)) => + res.checked_expr(|res| inner.set_cookie(res, state)), + _ => res } })) } From 099a8ff7d82184512ae5862ca0e6d088a8838367 Mon Sep 17 00:00:00 2001 From: dowwie Date: Mon, 1 Jul 2019 15:26:19 -0400 Subject: [PATCH 07/12] updated session cookie to support login, logout, changes --- actix-session/src/cookie.rs | 42 ++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index de1faea96..45f24817e 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -119,7 +119,21 @@ impl CookieSessionInner { Ok(()) } - fn load(&self, req: &ServiceRequest) -> HashMap { + /// invalidates session cookie + fn remove_cookie(&self, res: &mut ServiceResponse) + -> Result<(), Error> { + let mut cookie = Cookie::named(self.name.clone()); + cookie.set_value(""); + cookie.set_max_age(time::Duration::seconds(0)); + cookie.set_expires(time::now() - time::Duration::days(365)); + + let val = HeaderValue::from_str(&cookie.to_string())?; + res.headers_mut().append(SET_COOKIE, val); + + Ok(()) + } + + fn load(&self, req: &ServiceRequest) -> (bool, HashMap) { if let Ok(cookies) = req.cookies() { for cookie in cookies.iter() { if cookie.name() == self.name { @@ -134,13 +148,13 @@ impl CookieSessionInner { }; if let Some(cookie) = cookie_opt { if let Ok(val) = serde_json::from_str(cookie.value()) { - return val; + return (false, val); } } } } } - HashMap::new() + (true, HashMap::new()) } } @@ -302,15 +316,33 @@ where self.service.poll_ready() } + /// On first request, a new session cookie is returned in response, regardless + /// of whether any session state is set. With subsequent requests, if the + /// session state changes, then set-cookie is returned in response. As + /// a user logs out, call session.purge() to set SessionStatus accordingly + /// and this will trigger removal of the session cookie in the response. fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let inner = self.inner.clone(); - let state = self.inner.load(&req); + let (is_new, state) = self.inner.load(&req); Session::set_session(state.into_iter(), &mut req); Box::new(self.service.call(req).map(move |mut res| { match Session::get_changes(&mut res) { - (SessionStatus::Changed, Some(state)) => + (SessionStatus::Changed, Some(state)) + | (SessionStatus::Renewed, Some(state)) => res.checked_expr(|res| inner.set_cookie(res, state)), + (SessionStatus::Unchanged, _) => + // set a new session cookie upon first request (new client) + if is_new { + let state: HashMap = HashMap::new(); + res.checked_expr(|res| inner.set_cookie(res, state.into_iter())) + } else { + res + }, + (SessionStatus::Purged, _) => { + inner.remove_cookie(&mut res); + res + }, _ => res } })) From 5bf5b0acd2b60c5d56ab60b0d33523b9952916c1 Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 3 Jul 2019 07:46:46 -0400 Subject: [PATCH 08/12] updated CHANGES with info about actix-session update --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a20713107..243dc8278 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,9 @@ ### Changed * Use `encoding_rs` crate instead of unmaintained `encoding` crate +* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` + at successful login to cycle a session (new key, cookie). Use ``Session.purge()`` + at logout to invalid a session cookie (and remove from redis cache, if applicable). ## [1.0.2] - 2019-06-17 From dabc4fe00b1b2cf149793fae40440e36a6e5e95f Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 3 Jul 2019 07:50:11 -0400 Subject: [PATCH 09/12] updated actix-session/CHANGES with info --- actix-session/CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index 10aea8700..ec1606ded 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -1,5 +1,10 @@ # Changes +## [0.2.0] - 2019-07-03 +* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` + at successful login to cycle a session (new key, cookie). Use ``Session.purge()`` + at logout to invalid a session cookie (and remove from redis cache, if applicable). + ## [0.1.1] - 2019-06-03 * Fix optional cookie session support From 2d424957fb4d4feea0e3c104b84f60d4ac0af2cf Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 3 Jul 2019 07:50:45 -0400 Subject: [PATCH 10/12] updated version in Cargo to 0.2 --- actix-session/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index 4c1d66570..d973661ef 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.1.1" +version = "0.2.0" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" From 1fdd77bffac9f16d780eff9aaefc23889eeae513 Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 3 Jul 2019 07:56:50 -0400 Subject: [PATCH 11/12] reworded session info in CHANGES --- CHANGES.md | 5 +++-- actix-session/CHANGES.md | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 243dc8278..5937cf066 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,8 +6,9 @@ * Use `encoding_rs` crate instead of unmaintained `encoding` crate * Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` - at successful login to cycle a session (new key, cookie). Use ``Session.purge()`` - at logout to invalid a session cookie (and remove from redis cache, if applicable). + at successful login to cycle a session (new key/cookie but keeps state). + Use ``Session.purge()`` at logout to invalid a session cookie (and remove + from redis cache, if applicable). ## [1.0.2] - 2019-06-17 diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index ec1606ded..927485051 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -2,8 +2,10 @@ ## [0.2.0] - 2019-07-03 * Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` - at successful login to cycle a session (new key, cookie). Use ``Session.purge()`` - at logout to invalid a session cookie (and remove from redis cache, if applicable). + at successful login to cycle a session (new key/cookie but keeps state). + Use ``Session.purge()`` at logout to invalid a session cookie (and remove + from redis cache, if applicable). + ## [0.1.1] - 2019-06-03 From 7596ab69e0b0a40a0c07627a55a2f2a47c8ecc6a Mon Sep 17 00:00:00 2001 From: dowwie Date: Wed, 3 Jul 2019 08:55:29 -0400 Subject: [PATCH 12/12] reverted actix-web/CHANGES.md --- CHANGES.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5937cf066..a20713107 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,10 +5,6 @@ ### Changed * Use `encoding_rs` crate instead of unmaintained `encoding` crate -* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` - at successful login to cycle a session (new key/cookie but keeps state). - Use ``Session.purge()`` at logout to invalid a session cookie (and remove - from redis cache, if applicable). ## [1.0.2] - 2019-06-17