mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-24 07:53:00 +01:00
Store visit and login timestamp in the identity cookie (#502)
This allows to verify time of login or last visit and therfore limiting the danger of leaked cookies.
This commit is contained in:
parent
f429d3319f
commit
60fa0d5427
@ -49,10 +49,12 @@
|
|||||||
//! ```
|
//! ```
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use actix_service::{Service, Transform};
|
use actix_service::{Service, Transform};
|
||||||
use futures::future::{ok, Either, FutureResult};
|
use futures::future::{ok, Either, FutureResult};
|
||||||
use futures::{Future, IntoFuture, Poll};
|
use futures::{Future, IntoFuture, Poll};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
|
|
||||||
use crate::cookie::{Cookie, CookieJar, Key, SameSite};
|
use crate::cookie::{Cookie, CookieJar, Key, SameSite};
|
||||||
@ -284,84 +286,133 @@ where
|
|||||||
|
|
||||||
struct CookieIdentityInner {
|
struct CookieIdentityInner {
|
||||||
key: Key,
|
key: Key,
|
||||||
|
key_v2: Key,
|
||||||
name: String,
|
name: String,
|
||||||
path: String,
|
path: String,
|
||||||
domain: Option<String>,
|
domain: Option<String>,
|
||||||
secure: bool,
|
secure: bool,
|
||||||
max_age: Option<Duration>,
|
max_age: Option<Duration>,
|
||||||
same_site: Option<SameSite>,
|
same_site: Option<SameSite>,
|
||||||
|
visit_deadline: Option<Duration>,
|
||||||
|
login_deadline: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
struct CookieValue {
|
||||||
|
identity: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
login_timestamp: Option<SystemTime>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
visit_timestamp: Option<SystemTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CookieIdentityExtention {
|
||||||
|
login_timestamp: Option<SystemTime>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CookieIdentityInner {
|
impl CookieIdentityInner {
|
||||||
fn new(key: &[u8]) -> CookieIdentityInner {
|
fn new(key: &[u8]) -> CookieIdentityInner {
|
||||||
|
let key_v2: Vec<u8> = key.iter().chain([1, 0, 0, 0].iter()).map(|e| *e).collect();
|
||||||
CookieIdentityInner {
|
CookieIdentityInner {
|
||||||
key: Key::from_master(key),
|
key: Key::from_master(key),
|
||||||
|
key_v2: Key::from_master(&key_v2),
|
||||||
name: "actix-identity".to_owned(),
|
name: "actix-identity".to_owned(),
|
||||||
path: "/".to_owned(),
|
path: "/".to_owned(),
|
||||||
domain: None,
|
domain: None,
|
||||||
secure: true,
|
secure: true,
|
||||||
max_age: None,
|
max_age: None,
|
||||||
same_site: None,
|
same_site: None,
|
||||||
|
visit_deadline: None,
|
||||||
|
login_deadline: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cookie<B>(
|
fn set_cookie<B>(
|
||||||
&self,
|
&self,
|
||||||
resp: &mut ServiceResponse<B>,
|
resp: &mut ServiceResponse<B>,
|
||||||
id: Option<String>,
|
value: Option<CookieValue>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let some = id.is_some();
|
let add_cookie = value.is_some();
|
||||||
{
|
let val = value.map(|val| if !self.legacy_supported() {
|
||||||
let id = id.unwrap_or_else(String::new);
|
serde_json::to_string(&val)
|
||||||
let mut cookie = Cookie::new(self.name.clone(), id);
|
} else {
|
||||||
cookie.set_path(self.path.clone());
|
Ok(val.identity)
|
||||||
cookie.set_secure(self.secure);
|
});
|
||||||
cookie.set_http_only(true);
|
let mut cookie = Cookie::new(self.name.clone(), val.unwrap_or_else(|| Ok(String::new()))?);
|
||||||
|
cookie.set_path(self.path.clone());
|
||||||
|
cookie.set_secure(self.secure);
|
||||||
|
cookie.set_http_only(true);
|
||||||
|
|
||||||
if let Some(ref domain) = self.domain {
|
if let Some(ref domain) = self.domain {
|
||||||
cookie.set_domain(domain.clone());
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut jar = CookieJar::new();
|
|
||||||
if some {
|
|
||||||
jar.private(&self.key).add(cookie);
|
|
||||||
} else {
|
|
||||||
jar.add_original(cookie.clone());
|
|
||||||
jar.private(&self.key).remove(cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
for cookie in jar.delta() {
|
|
||||||
let val = HeaderValue::from_str(&cookie.to_string())?;
|
|
||||||
resp.headers_mut().append(header::SET_COOKIE, val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut jar = CookieJar::new();
|
||||||
|
let key = if self.legacy_supported() {&self.key} else {&self.key_v2};
|
||||||
|
if add_cookie {
|
||||||
|
jar.private(&key).add(cookie);
|
||||||
|
} else {
|
||||||
|
jar.add_original(cookie.clone());
|
||||||
|
jar.private(&key).remove(cookie);
|
||||||
|
}
|
||||||
|
for cookie in jar.delta() {
|
||||||
|
let val = HeaderValue::from_str(&cookie.to_string())?;
|
||||||
|
resp.headers_mut().append(header::SET_COOKIE, val);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, req: &ServiceRequest) -> Option<String> {
|
fn load(&self, req: &ServiceRequest) -> Option<CookieValue> {
|
||||||
if let Ok(cookies) = req.cookies() {
|
let cookie = req.cookie(&self.name)?;
|
||||||
for cookie in cookies.iter() {
|
let mut jar = CookieJar::new();
|
||||||
if cookie.name() == self.name {
|
jar.add_original(cookie.clone());
|
||||||
let mut jar = CookieJar::new();
|
let res = if self.legacy_supported() {
|
||||||
jar.add_original(cookie.clone());
|
jar.private(&self.key).get(&self.name).map(|n| CookieValue {
|
||||||
|
identity: n.value().to_string(),
|
||||||
|
login_timestamp: None,
|
||||||
|
visit_timestamp: None
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
res.or_else(|| jar.private(&self.key_v2).get(&self.name).and_then(|c| self.parse(c)))
|
||||||
|
}
|
||||||
|
|
||||||
let cookie_opt = jar.private(&self.key).get(&self.name);
|
fn parse(&self, cookie: Cookie) -> Option<CookieValue> {
|
||||||
if let Some(cookie) = cookie_opt {
|
let value: CookieValue = serde_json::from_str(cookie.value()).ok()?;
|
||||||
return Some(cookie.value().into());
|
let now = SystemTime::now();
|
||||||
}
|
if let Some(visit_deadline) = self.visit_deadline {
|
||||||
}
|
if now.duration_since(value.visit_timestamp?).ok()? > visit_deadline.to_std().ok()? {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
if let Some(login_deadline) = self.login_deadline {
|
||||||
|
if now.duration_since(value.login_timestamp?).ok()? > login_deadline.to_std().ok()? {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn legacy_supported(&self) -> bool {
|
||||||
|
self.visit_deadline.is_none() && self.login_deadline.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn always_update_cookie(&self) -> bool {
|
||||||
|
self.visit_deadline.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requires_oob_data(&self) -> bool {
|
||||||
|
self.login_deadline.is_some()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,6 +494,18 @@ impl CookieIdentityPolicy {
|
|||||||
Rc::get_mut(&mut self.0).unwrap().same_site = Some(same_site);
|
Rc::get_mut(&mut self.0).unwrap().same_site = Some(same_site);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Accepts only users whose cookie has been seen before the given deadline
|
||||||
|
pub fn visit_deadline(mut self, value: Duration) -> CookieIdentityPolicy {
|
||||||
|
Rc::get_mut(&mut self.0).unwrap().visit_deadline = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accepts only users which has been authenticated before the given deadline
|
||||||
|
pub fn login_deadline(mut self, value: Duration) -> CookieIdentityPolicy {
|
||||||
|
Rc::get_mut(&mut self.0).unwrap().login_deadline = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdentityPolicy for CookieIdentityPolicy {
|
impl IdentityPolicy for CookieIdentityPolicy {
|
||||||
@ -450,7 +513,12 @@ impl IdentityPolicy for CookieIdentityPolicy {
|
|||||||
type ResponseFuture = Result<(), Error>;
|
type ResponseFuture = Result<(), Error>;
|
||||||
|
|
||||||
fn from_request(&self, req: &mut ServiceRequest) -> Self::Future {
|
fn from_request(&self, req: &mut ServiceRequest) -> Self::Future {
|
||||||
Ok(self.0.load(req))
|
Ok(self.0.load(req).map(|CookieValue {identity, login_timestamp, ..}| {
|
||||||
|
if self.0.requires_oob_data() {
|
||||||
|
req.extensions_mut().insert(CookieIdentityExtention { login_timestamp });
|
||||||
|
}
|
||||||
|
identity
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_response<B>(
|
fn to_response<B>(
|
||||||
@ -459,9 +527,28 @@ impl IdentityPolicy for CookieIdentityPolicy {
|
|||||||
changed: bool,
|
changed: bool,
|
||||||
res: &mut ServiceResponse<B>,
|
res: &mut ServiceResponse<B>,
|
||||||
) -> Self::ResponseFuture {
|
) -> Self::ResponseFuture {
|
||||||
if changed {
|
let _ = if changed {
|
||||||
let _ = self.0.set_cookie(res, id);
|
let login_timestamp = SystemTime::now();
|
||||||
}
|
self.0.set_cookie(res, id.map(|identity| CookieValue {
|
||||||
|
identity,
|
||||||
|
login_timestamp: self.0.login_deadline.map(|_| login_timestamp),
|
||||||
|
visit_timestamp: self.0.visit_deadline.map(|_| login_timestamp)
|
||||||
|
}))
|
||||||
|
} else if self.0.always_update_cookie() && id.is_some() {
|
||||||
|
let visit_timestamp = SystemTime::now();
|
||||||
|
let mut login_timestamp = None;
|
||||||
|
if self.0.requires_oob_data() {
|
||||||
|
let CookieIdentityExtention { login_timestamp: lt } = res.request().extensions_mut().remove().unwrap();
|
||||||
|
login_timestamp = lt;
|
||||||
|
}
|
||||||
|
self.0.set_cookie(res, Some(CookieValue {
|
||||||
|
identity: id.unwrap(),
|
||||||
|
login_timestamp,
|
||||||
|
visit_timestamp: self.0.visit_deadline.map(|_| visit_timestamp)
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,14 +560,20 @@ mod tests {
|
|||||||
use crate::test::{self, TestRequest};
|
use crate::test::{self, TestRequest};
|
||||||
use crate::{web, App, HttpResponse};
|
use crate::{web, App, HttpResponse};
|
||||||
|
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
const COOKIE_KEY_MASTER: [u8; 32] = [0; 32];
|
||||||
|
const COOKIE_NAME: &'static str = "actix_auth";
|
||||||
|
const COOKIE_LOGIN: &'static str = "test";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_identity() {
|
fn test_identity() {
|
||||||
let mut srv = test::init_service(
|
let mut srv = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(IdentityService::new(
|
.wrap(IdentityService::new(
|
||||||
CookieIdentityPolicy::new(&[0; 32])
|
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||||
.domain("www.rust-lang.org")
|
.domain("www.rust-lang.org")
|
||||||
.name("actix_auth")
|
.name(COOKIE_NAME)
|
||||||
.path("/")
|
.path("/")
|
||||||
.secure(true),
|
.secure(true),
|
||||||
))
|
))
|
||||||
@ -492,7 +585,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.service(web::resource("/login").to(|id: Identity| {
|
.service(web::resource("/login").to(|id: Identity| {
|
||||||
id.remember("test".to_string());
|
id.remember(COOKIE_LOGIN.to_string());
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
}))
|
}))
|
||||||
.service(web::resource("/logout").to(|id: Identity| {
|
.service(web::resource("/logout").to(|id: Identity| {
|
||||||
@ -537,9 +630,9 @@ mod tests {
|
|||||||
let mut srv = test::init_service(
|
let mut srv = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(IdentityService::new(
|
.wrap(IdentityService::new(
|
||||||
CookieIdentityPolicy::new(&[0; 32])
|
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||||
.domain("www.rust-lang.org")
|
.domain("www.rust-lang.org")
|
||||||
.name("actix_auth")
|
.name(COOKIE_NAME)
|
||||||
.path("/")
|
.path("/")
|
||||||
.max_age_time(duration)
|
.max_age_time(duration)
|
||||||
.secure(true),
|
.secure(true),
|
||||||
@ -563,9 +656,9 @@ mod tests {
|
|||||||
let mut srv = test::init_service(
|
let mut srv = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(IdentityService::new(
|
.wrap(IdentityService::new(
|
||||||
CookieIdentityPolicy::new(&[0; 32])
|
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||||
.domain("www.rust-lang.org")
|
.domain("www.rust-lang.org")
|
||||||
.name("actix_auth")
|
.name(COOKIE_NAME)
|
||||||
.path("/")
|
.path("/")
|
||||||
.max_age(seconds)
|
.max_age(seconds)
|
||||||
.secure(true),
|
.secure(true),
|
||||||
@ -582,4 +675,237 @@ mod tests {
|
|||||||
let c = resp.response().cookies().next().unwrap().to_owned();
|
let c = resp.response().cookies().next().unwrap().to_owned();
|
||||||
assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap());
|
assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_identity_server<F: Fn(CookieIdentityPolicy) -> CookieIdentityPolicy + Sync + Send + Clone + 'static>(f: F) -> impl actix_service::Service<Request = actix_http::Request, Response = ServiceResponse<actix_http::body::Body>, Error = actix_http::Error> {
|
||||||
|
test::init_service(
|
||||||
|
App::new()
|
||||||
|
.wrap(IdentityService::new(f(CookieIdentityPolicy::new(&COOKIE_KEY_MASTER).secure(false).name(COOKIE_NAME))))
|
||||||
|
.service(web::resource("/").to(|id: Identity| {
|
||||||
|
let identity = id.identity();
|
||||||
|
if identity.is_none() {
|
||||||
|
id.remember(COOKIE_LOGIN.to_string())
|
||||||
|
}
|
||||||
|
web::Json(identity)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn legacy_login_cookie(identity: &'static str) -> Cookie<'static> {
|
||||||
|
let mut jar = CookieJar::new();
|
||||||
|
jar.private(&Key::from_master(&COOKIE_KEY_MASTER)).add(Cookie::new(COOKIE_NAME, identity));
|
||||||
|
jar.get(COOKIE_NAME).unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn login_cookie(identity: &'static str, login_timestamp: Option<SystemTime>, visit_timestamp: Option<SystemTime>) -> Cookie<'static> {
|
||||||
|
let mut jar = CookieJar::new();
|
||||||
|
let key: Vec<u8> = COOKIE_KEY_MASTER.iter().chain([1, 0, 0, 0].iter()).map(|e| *e).collect();
|
||||||
|
jar.private(&Key::from_master(&key)).add(Cookie::new(COOKIE_NAME, serde_json::to_string(&CookieValue {
|
||||||
|
identity: identity.to_string(),
|
||||||
|
login_timestamp,
|
||||||
|
visit_timestamp
|
||||||
|
}).unwrap()));
|
||||||
|
jar.get(COOKIE_NAME).unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_logged_in(response: &mut ServiceResponse, identity: Option<&str>) {
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use futures::Stream;
|
||||||
|
let bytes =
|
||||||
|
test::block_on(response.take_body().fold(BytesMut::new(), |mut b, c| {
|
||||||
|
b.extend(c);
|
||||||
|
Ok::<_, Error>(b)
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
let resp: Option<String> = serde_json::from_slice(&bytes[..]).unwrap();
|
||||||
|
assert_eq!(resp.as_ref().map(|s| s.borrow()), identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_legacy_login_cookie(response: &mut ServiceResponse, identity: &str) {
|
||||||
|
let mut cookies = CookieJar::new();
|
||||||
|
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
||||||
|
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
||||||
|
}
|
||||||
|
let cookie = cookies.private(&Key::from_master(&COOKIE_KEY_MASTER)).get(COOKIE_NAME).unwrap();
|
||||||
|
assert_eq!(cookie.value(), identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LoginTimestampCheck {
|
||||||
|
NoTimestamp,
|
||||||
|
NewTimestamp,
|
||||||
|
OldTimestamp(SystemTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VisitTimeStampCheck {
|
||||||
|
NoTimestamp,
|
||||||
|
NewTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_login_cookie(response: &mut ServiceResponse, identity: &str, login_timestamp: LoginTimestampCheck, visit_timestamp: VisitTimeStampCheck) {
|
||||||
|
let mut cookies = CookieJar::new();
|
||||||
|
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
||||||
|
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
||||||
|
}
|
||||||
|
let key: Vec<u8> = COOKIE_KEY_MASTER.iter().chain([1, 0, 0, 0].iter()).map(|e| *e).collect();
|
||||||
|
let cookie = cookies.private(&Key::from_master(&key)).get(COOKIE_NAME).unwrap();
|
||||||
|
let cv: CookieValue = serde_json::from_str(cookie.value()).unwrap();
|
||||||
|
assert_eq!(cv.identity, identity);
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let t30sec_ago = now - Duration::seconds(30).to_std().unwrap();
|
||||||
|
match login_timestamp {
|
||||||
|
LoginTimestampCheck::NoTimestamp => assert_eq!(cv.login_timestamp, None),
|
||||||
|
LoginTimestampCheck::NewTimestamp => assert!(t30sec_ago <= cv.login_timestamp.unwrap() && cv.login_timestamp.unwrap() <= now),
|
||||||
|
LoginTimestampCheck::OldTimestamp(old_timestamp) => assert_eq!(cv.login_timestamp, Some(old_timestamp))
|
||||||
|
}
|
||||||
|
match visit_timestamp {
|
||||||
|
VisitTimeStampCheck::NoTimestamp => assert_eq!(cv.visit_timestamp, None),
|
||||||
|
VisitTimeStampCheck::NewTimestamp => assert!(t30sec_ago <= cv.visit_timestamp.unwrap() && cv.visit_timestamp.unwrap() <= now)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_no_login_cookie(response: &mut ServiceResponse) {
|
||||||
|
let mut cookies = CookieJar::new();
|
||||||
|
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
||||||
|
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
||||||
|
}
|
||||||
|
assert!(cookies.get(COOKIE_NAME).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_legacy_cookie_is_set() {
|
||||||
|
let mut srv = create_identity_server(|c| c);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_legacy_cookie_works() {
|
||||||
|
let mut srv = create_identity_server(|c| c);
|
||||||
|
let cookie = legacy_login_cookie(COOKIE_LOGIN);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, Some(COOKIE_LOGIN));
|
||||||
|
assert_no_login_cookie(&mut resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() {
|
||||||
|
let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90)));
|
||||||
|
let cookie = legacy_login_cookie(COOKIE_LOGIN);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::NoTimestamp, VisitTimeStampCheck::NewTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() {
|
||||||
|
let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90)));
|
||||||
|
let cookie = legacy_login_cookie(COOKIE_LOGIN);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::NewTimestamp, VisitTimeStampCheck::NoTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_cookie_rejected_if_login_timestamp_needed() {
|
||||||
|
let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90)));
|
||||||
|
let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now()));
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::NewTimestamp, VisitTimeStampCheck::NoTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_cookie_rejected_if_visit_timestamp_needed() {
|
||||||
|
let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90)));
|
||||||
|
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::NoTimestamp, VisitTimeStampCheck::NewTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_cookie_rejected_if_login_timestamp_too_old() {
|
||||||
|
let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90)));
|
||||||
|
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), None);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::NewTimestamp, VisitTimeStampCheck::NoTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_cookie_rejected_if_visit_timestamp_too_old() {
|
||||||
|
let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90)));
|
||||||
|
let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now() - Duration::days(180).to_std().unwrap()));
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, None);
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::NoTimestamp, VisitTimeStampCheck::NewTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_cookie_not_updated_on_login_deadline() {
|
||||||
|
let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90)));
|
||||||
|
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, Some(COOKIE_LOGIN));
|
||||||
|
assert_no_login_cookie(&mut resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity_cookie_updated_on_visit_deadline() {
|
||||||
|
let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90)).login_deadline(Duration::days(90)));
|
||||||
|
let timestamp = SystemTime::now() - Duration::days(1).to_std().unwrap();
|
||||||
|
let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp));
|
||||||
|
let mut resp = test::call_service(
|
||||||
|
&mut srv,
|
||||||
|
TestRequest::with_uri("/")
|
||||||
|
.cookie(cookie.clone())
|
||||||
|
.to_request()
|
||||||
|
);
|
||||||
|
assert_logged_in(&mut resp, Some(COOKIE_LOGIN));
|
||||||
|
assert_login_cookie(&mut resp, COOKIE_LOGIN, LoginTimestampCheck::OldTimestamp(timestamp), VisitTimeStampCheck::NewTimestamp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user