diff --git a/CHANGES.md b/CHANGES.md index a2b880e23..a0702c2ef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## 0.5.0 (2018-07-21) +* Session cookie configuration + * Actix/Actix-web 0.7 compatibility diff --git a/Cargo.toml b/Cargo.toml index 5e3d3afd0..77f139b63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ tokio-io = "0.1" tokio-codec = "0.1" tokio-tcp = "0.1" redis-async = "0.3.2" +time = "0.1" # actix web session actix-web = { git = "https://github.com/actix/actix-web.git", optional=true } diff --git a/src/lib.rs b/src/lib.rs index 5a0a96547..d98c5ef2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ extern crate log; extern crate redis_async; #[macro_use] extern crate failure; +extern crate time; mod redis; pub use redis::{Command, RedisActor}; diff --git a/src/redis.rs b/src/redis.rs index 91fd4f5dc..f2f0aa6da 100644 --- a/src/redis.rs +++ b/src/redis.rs @@ -37,10 +37,13 @@ impl RedisActor { pub fn start>(addr: S) -> Addr { let addr = addr.into(); + let mut backoff = ExponentialBackoff::default(); + backoff.max_elapsed_time = None; + Supervisor::start(|_| RedisActor { addr, cell: None, - backoff: ExponentialBackoff::default(), + backoff: backoff, queue: VecDeque::new(), }) } @@ -74,8 +77,6 @@ impl Actor for RedisActor { // we stop current context, supervisor will restart it. if let Some(timeout) = act.backoff.next_backoff() { ctx.run_later(timeout, |_, ctx| ctx.stop()); - } else { - ctx.stop(); } } }) @@ -85,8 +86,6 @@ impl Actor for RedisActor { // we stop current context, supervisor will restart it. if let Some(timeout) = act.backoff.next_backoff() { ctx.run_later(timeout, |_, ctx| ctx.stop()); - } else { - ctx.stop(); } }) .wait(ctx); diff --git a/src/session.rs b/src/session.rs index 91d9936c5..22a97feb6 100644 --- a/src/session.rs +++ b/src/session.rs @@ -6,7 +6,7 @@ use actix::prelude::*; use actix_web::middleware::session::{SessionBackend, SessionImpl}; use actix_web::middleware::Response as MiddlewareResponse; use actix_web::{error, Error, HttpRequest, HttpResponse, Result}; -use cookie::{Cookie, CookieJar, Key}; +use cookie::{Cookie, CookieJar, Key, SameSite}; use futures::future::{err as FutErr, ok as FutOk, Either}; use futures::Future; use http::header::{self, HeaderValue}; @@ -14,6 +14,7 @@ use rand::distributions::Alphanumeric; use rand::{self, Rng}; use redis_async::resp::RespValue; use serde_json; +use time::Duration; use redis::{Command, RedisActor}; @@ -80,6 +81,11 @@ impl RedisSessionBackend { ttl: "7200".to_owned(), addr: RedisActor::start(addr), name: "actix-session".to_owned(), + path: "/".to_owned(), + domain: None, + secure: false, + max_age: Some(Duration::days(7)), + same_site: None, })) } @@ -94,6 +100,38 @@ impl RedisSessionBackend { Rc::get_mut(&mut self.0).unwrap().name = name.to_owned(); self } + + /// Set custom cookie path + pub fn cookie_path(mut self, path: &str) -> Self { + Rc::get_mut(&mut self.0).unwrap().path = path.to_owned(); + self + } + + /// Set custom cookie domain + pub fn cookie_domain(mut self, domain: &str) -> Self { + Rc::get_mut(&mut self.0).unwrap().domain = Some(domain.to_owned()); + self + } + + /// Set custom cookie secure + /// If the `secure` field is set, a cookie will only be transmitted when the + /// connection is secure - i.e. `https` + pub fn cookie_secure(mut self, secure: bool) -> Self { + Rc::get_mut(&mut self.0).unwrap().secure = secure; + self + } + + /// Set custom cookie max-age + pub fn cookie_max_age(mut self, max_age: Duration) -> Self { + Rc::get_mut(&mut self.0).unwrap().max_age = Some(max_age); + self + } + + /// Set custom cookie SameSite + pub fn cookie_same_site(mut self, same_site: SameSite) -> Self { + Rc::get_mut(&mut self.0).unwrap().same_site = Some(same_site); + self + } } impl SessionBackend for RedisSessionBackend { @@ -126,8 +164,13 @@ impl SessionBackend for RedisSessionBackend { struct Inner { key: Key, ttl: String, - name: String, addr: Addr, + name: String, + path: String, + domain: Option, + secure: bool, + max_age: Option, + same_site: Option, } impl Inner { @@ -200,9 +243,22 @@ impl Inner { .collect(); let mut cookie = Cookie::new(self.name.clone(), value.clone()); - cookie.set_path("/"); + cookie.set_path(self.path.clone()); + cookie.set_secure(self.secure); cookie.set_http_only(true); + if let Some(ref domain) = self.domain { + cookie.set_domain(domain.clone()); + } + + if let Some(max_age) = self.max_age { + cookie.set_max_age(max_age); + } + + if let Some(same_site) = self.same_site { + cookie.set_same_site(same_site); + } + // set cookie let mut jar = CookieJar::new(); jar.signed(&self.key).add(cookie);