1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +01:00

Merge pull request #170 from adwhit/private-cookies

Public, signed and private cookies
This commit is contained in:
Nikolay Kim 2018-04-09 12:54:15 -07:00 committed by GitHub
commit 458e6bdcc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 21 deletions

View File

@ -23,6 +23,8 @@
* Fix logger request duration calculation #152 * Fix logger request duration calculation #152
* Add `signed` and `private` `CookieSessionBackend`s
## 0.4.10 (2018-03-20) ## 0.4.10 (2018-03-20)

View File

@ -105,7 +105,7 @@ fn main() {
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
// cookie session middleware // cookie session middleware
.middleware(middleware::SessionStorage::new( .middleware(middleware::SessionStorage::new(
middleware::CookieSessionBackend::new(&[0; 32]).secure(false) middleware::CookieSessionBackend::signed(&[0; 32]).secure(false)
)) ))
// register favicon // register favicon
.resource("/favicon.ico", |r| r.f(favicon)) .resource("/favicon.ico", |r| r.f(favicon))

View File

@ -163,14 +163,17 @@ used with different backend types to store session data in different backends.
> can be added. > can be added.
[**CookieSessionBackend**](../actix_web/middleware/struct.CookieSessionBackend.html) [**CookieSessionBackend**](../actix_web/middleware/struct.CookieSessionBackend.html)
uses signed cookies as session storage. `CookieSessionBackend` creates sessions which uses cookies as session storage. `CookieSessionBackend` creates sessions which
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
single cookie. An internal server error is generated if a session contains more than 4000 bytes. single cookie. An internal server error is generated if a session contains more than 4000 bytes.
You need to pass a random value to the constructor of `CookieSessionBackend`. A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
This is a private key for cookie session. When this value is changed, all session data is lost.
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client.
The constructors take a key as an argument. This is the private key for cookie session - when this value is changed, all session data is lost.
> **Note**: anything you write into the session is visible by the user, but it is not modifiable.
In general, you create a In general, you create a
`SessionStorage` middleware and initialize it with specific backend implementation, `SessionStorage` middleware and initialize it with specific backend implementation,
@ -203,7 +206,7 @@ fn main() {
server::new( server::new(
|| App::new() || App::new()
.middleware(SessionStorage::new( // <- create session middleware .middleware(SessionStorage::new( // <- create session middleware
CookieSessionBackend::new(&[0; 32]) // <- create cookie session backend CookieSessionBackend::signed(&[0; 32]) // <- create signed cookie session backend
.secure(false) .secure(false)
))) )))
.bind("127.0.0.1:59880").unwrap() .bind("127.0.0.1:59880").unwrap()

View File

@ -121,7 +121,7 @@ unsafe impl Sync for SessionImplBox {}
/// fn main() { /// fn main() {
/// let app = App::new().middleware( /// let app = App::new().middleware(
/// SessionStorage::new( // <- create session middleware /// SessionStorage::new( // <- create session middleware
/// CookieSessionBackend::new(&[0; 32]) // <- create cookie session backend /// CookieSessionBackend::signed(&[0; 32]) // <- create cookie session backend
/// .secure(false)) /// .secure(false))
/// ); /// );
/// } /// }
@ -257,8 +257,14 @@ impl SessionImpl for CookieSession {
} }
} }
enum CookieSecurity {
Signed,
Private
}
struct CookieSessionInner { struct CookieSessionInner {
key: Key, key: Key,
security: CookieSecurity,
name: String, name: String,
path: String, path: String,
domain: Option<String>, domain: Option<String>,
@ -268,14 +274,16 @@ struct CookieSessionInner {
impl CookieSessionInner { impl CookieSessionInner {
fn new(key: &[u8]) -> CookieSessionInner { fn new(key: &[u8], security: CookieSecurity) -> CookieSessionInner {
CookieSessionInner { CookieSessionInner {
key: Key::from_master(key), key: Key::from_master(key),
security: security,
name: "actix-session".to_owned(), name: "actix-session".to_owned(),
path: "/".to_owned(), path: "/".to_owned(),
domain: None, domain: None,
secure: true, secure: true,
max_age: None } max_age: None,
}
} }
fn set_cookie(&self, resp: &mut HttpResponse, state: &HashMap<String, String>) -> Result<()> { fn set_cookie(&self, resp: &mut HttpResponse, state: &HashMap<String, String>) -> Result<()> {
@ -299,7 +307,11 @@ impl CookieSessionInner {
} }
let mut jar = CookieJar::new(); let mut jar = CookieJar::new();
jar.signed(&self.key).add(cookie);
match self.security {
CookieSecurity::Signed => jar.signed(&self.key).add(cookie),
CookieSecurity::Private => jar.private(&self.key).add(cookie),
}
for cookie in jar.delta() { for cookie in jar.delta() {
let val = HeaderValue::from_str(&cookie.to_string())?; let val = HeaderValue::from_str(&cookie.to_string())?;
@ -315,7 +327,12 @@ impl CookieSessionInner {
if cookie.name() == self.name { if cookie.name() == self.name {
let mut jar = CookieJar::new(); let mut jar = CookieJar::new();
jar.add_original(cookie.clone()); jar.add_original(cookie.clone());
if let Some(cookie) = jar.signed(&self.key).get(&self.name) {
let cookie_opt = match self.security {
CookieSecurity::Signed => jar.signed(&self.key).get(&self.name),
CookieSecurity::Private => jar.private(&self.key).get(&self.name),
};
if let Some(cookie) = cookie_opt {
if let Ok(val) = serde_json::from_str(cookie.value()) { if let Ok(val) = serde_json::from_str(cookie.value()) {
return val; return val;
} }
@ -327,18 +344,24 @@ impl CookieSessionInner {
} }
} }
/// Use signed cookies as session storage. /// Use cookies for session storage.
/// ///
/// `CookieSessionBackend` creates sessions which are limited to storing /// `CookieSessionBackend` creates sessions which are limited to storing
/// fewer than 4000 bytes of data (as the payload must fit into a single cookie). /// fewer than 4000 bytes of data (as the payload must fit into a single cookie).
/// Internal server error get generated if session contains more than 4000 bytes. /// An Internal Server Error is generated if the session contains more than 4000 bytes.
/// ///
/// You need to pass a random value to the constructor of `CookieSessionBackend`. /// A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
/// This is private key for cookie session, When this value is changed, all session data is lost.
/// ///
/// Note that whatever you write into your session is visible by the user (but not modifiable). /// A *signed* cookie is stored on the client as plaintext alongside
/// a signature such that the cookie may be viewed but not modified by the client.
///
/// A *private* cookie is stored on the client as encrypted text
/// such that it may neither be viewed nor modified by the client.
///
/// The constructors take a key as an argument.
/// This is the private key for cookie session - when this value is changed, all session data is lost.
/// The constructors will panic if the key is less than 32 bytes in length.
/// ///
/// Constructor panics if key length is less than 32 bytes.
/// ///
/// # Example /// # Example
/// ///
@ -347,7 +370,7 @@ impl CookieSessionInner {
/// use actix_web::middleware::CookieSessionBackend; /// use actix_web::middleware::CookieSessionBackend;
/// ///
/// # fn main() { /// # fn main() {
/// let backend: CookieSessionBackend = CookieSessionBackend::new(&[0; 32]) /// let backend: CookieSessionBackend = CookieSessionBackend::signed(&[0; 32])
/// .domain("www.rust-lang.org") /// .domain("www.rust-lang.org")
/// .name("actix_session") /// .name("actix_session")
/// .path("/") /// .path("/")
@ -358,12 +381,20 @@ pub struct CookieSessionBackend(Rc<CookieSessionInner>);
impl CookieSessionBackend { impl CookieSessionBackend {
/// Construct new `CookieSessionBackend` instance. /// Construct new *signed* `CookieSessionBackend` instance.
/// ///
/// Panics if key length is less than 32 bytes. /// Panics if key length is less than 32 bytes.
pub fn new(key: &[u8]) -> CookieSessionBackend { pub fn signed(key: &[u8]) -> CookieSessionBackend {
CookieSessionBackend( CookieSessionBackend(
Rc::new(CookieSessionInner::new(key))) Rc::new(CookieSessionInner::new(key, CookieSecurity::Signed)))
}
/// Construct new *private* `CookieSessionBackend` instance.
///
/// Panics if key length is less than 32 bytes.
pub fn private(key: &[u8]) -> CookieSessionBackend {
CookieSessionBackend(
Rc::new(CookieSessionInner::new(key, CookieSecurity::Private)))
} }
/// Sets the `path` field in the session cookie being built. /// Sets the `path` field in the session cookie being built.
@ -385,6 +416,9 @@ impl CookieSessionBackend {
} }
/// Sets the `secure` field in the session cookie being built. /// Sets the `secure` field in the session cookie being built.
///
/// If the `secure` field is set, a cookie will only be transmitted when the
/// connection is secure - i.e. `https`
pub fn secure(mut self, value: bool) -> CookieSessionBackend { pub fn secure(mut self, value: bool) -> CookieSessionBackend {
Rc::get_mut(&mut self.0).unwrap().secure = value; Rc::get_mut(&mut self.0).unwrap().secure = value;
self self