mirror of
https://github.com/actix/actix-extras.git
synced 2025-02-02 10:59:03 +01:00
Merge pull request #23 from Dowwie/master
updated session logic to support login and logout functionality
This commit is contained in:
commit
60f28d65f1
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ Cargo.lock
|
||||
target/
|
||||
guide/build/
|
||||
/gh-pages
|
||||
/.history
|
||||
|
||||
*.so
|
||||
*.out
|
||||
|
11
CHANGES.md
11
CHANGES.md
@ -1,9 +1,18 @@
|
||||
# Changes
|
||||
|
||||
## 0.6.0 (2019-05-18)
|
||||
|
||||
## 0.6.0 (2019-07-08)
|
||||
|
||||
* actix-web 1.0.0 compatibility
|
||||
|
||||
* Upgraded logic that evaluates session state, including new SessionStatus field,
|
||||
and introduced ``session.renew()`` and ``session.purge()`` functionality.
|
||||
Use ``renew()`` to cycle the session key at successful login. ``renew()`` keeps a
|
||||
session's state while replacing the old cookie and session key with new ones.
|
||||
Use ``purge()`` at logout to invalidate the session cookie and remove the
|
||||
session's redis cache entry.
|
||||
|
||||
|
||||
|
||||
## 0.5.1 (2018-08-02)
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
use std::{collections::HashMap, iter, rc::Rc};
|
||||
use actix::prelude::*;
|
||||
use actix_service::{Service, Transform};
|
||||
use actix_session::Session;
|
||||
use actix_session::{Session, SessionStatus};
|
||||
use actix_utils::cloneable::CloneableService;
|
||||
use actix_web::cookie::{Cookie, CookieJar, Key, SameSite};
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
@ -14,7 +11,7 @@ use futures::future::{err, ok, Either, Future, FutureResult};
|
||||
use futures::Poll;
|
||||
use rand::{distributions::Alphanumeric, rngs::OsRng, Rng};
|
||||
use redis_async::resp::RespValue;
|
||||
use time::Duration;
|
||||
use time::{self, Duration};
|
||||
|
||||
use crate::redis::{Command, RedisActor};
|
||||
|
||||
@ -147,10 +144,54 @@ where
|
||||
};
|
||||
|
||||
srv.call(req).and_then(move |mut res| {
|
||||
if let Some(state) = Session::get_changes(&mut res) {
|
||||
Either::A(inner.update(res, state, value))
|
||||
match Session::get_changes(&mut res) {
|
||||
(SessionStatus::Unchanged, None) =>
|
||||
Either::A(Either::A(Either::A(ok(res)))),
|
||||
(SessionStatus::Unchanged, Some(state)) =>
|
||||
Either::A(Either::A(Either::B(
|
||||
if value.is_none(){ // implies the session is new
|
||||
Either::A(
|
||||
inner.update(res, state, value)
|
||||
)
|
||||
} else {
|
||||
Either::B(ok(res))
|
||||
Either::B(
|
||||
ok(res)
|
||||
)
|
||||
}
|
||||
))),
|
||||
(SessionStatus::Changed, Some(state)) =>
|
||||
Either::A(Either::B(Either::A(inner.update(res, state, value)))),
|
||||
(SessionStatus::Purged, Some(_)) => {
|
||||
if let Some(val) = value {
|
||||
Either::A(Either::B(Either::B(Either::A(
|
||||
inner.clear_cache(val)
|
||||
.and_then(move |_|
|
||||
match inner.remove_cookie(&mut res){
|
||||
Ok(_) => Either::A(ok(res)),
|
||||
Err(_err) => Either::B(err(
|
||||
error::ErrorInternalServerError(_err)))
|
||||
})
|
||||
))))
|
||||
} else {
|
||||
Either::A(Either::B(Either::B(Either::B(
|
||||
err(error::ErrorInternalServerError("unexpected"))
|
||||
))))
|
||||
}
|
||||
},
|
||||
(SessionStatus::Renewed, Some(state)) => {
|
||||
if let Some(val) = value {
|
||||
Either::B(Either::A(
|
||||
inner.clear_cache(val)
|
||||
.and_then(move |_|
|
||||
inner.update(res, state, None))
|
||||
))
|
||||
} else {
|
||||
Either::B(Either::B(
|
||||
inner.update(res, state, None)
|
||||
))
|
||||
}
|
||||
},
|
||||
(_, None) => unreachable!()
|
||||
}
|
||||
})
|
||||
}))
|
||||
@ -266,7 +307,6 @@ impl Inner {
|
||||
};
|
||||
|
||||
let state: HashMap<_, _> = state.collect();
|
||||
|
||||
match serde_json::to_string(&state) {
|
||||
Err(e) => Either::A(err(e.into())),
|
||||
Ok(body) => Either::B(
|
||||
@ -289,4 +329,35 @@ impl Inner {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// removes cache entry
|
||||
fn clear_cache(&self, key: String)
|
||||
-> impl Future<Item=(), Error=Error> {
|
||||
self.addr
|
||||
.send(Command(resp_array!["DEL", key]))
|
||||
.map_err(Error::from)
|
||||
.and_then(|res| {
|
||||
match res {
|
||||
// redis responds with number of deleted records
|
||||
Ok(RespValue::Integer(x)) if x > 0 => Ok(()),
|
||||
_ => Err(error::ErrorInternalServerError(
|
||||
"failed to remove session from cache"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// invalidates session cookie
|
||||
fn remove_cookie<B>(&self, res: &mut ServiceResponse<B>)
|
||||
-> Result<(), Error> {
|
||||
let mut cookie = Cookie::named(self.name.clone());
|
||||
cookie.set_value("");
|
||||
cookie.set_max_age(Duration::seconds(0));
|
||||
cookie.set_expires(time::now() - Duration::days(365));
|
||||
|
||||
let val = HeaderValue::from_str(&cookie.to_string())
|
||||
.map_err(|err| error::ErrorInternalServerError(err))?;
|
||||
res.headers_mut().append(header::SET_COOKIE, val);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user