implement cookie based polymorphic session

session-crate
Ondřej Hruška 4 years ago
parent 52676cf227
commit d24af144fa
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 74
      Cargo.lock
  2. 1
      Cargo.toml
  3. 19
      src/main.rs
  4. 168
      src/session.rs
  5. 2
      templates/index.html.tera

74
Cargo.lock generated

@ -101,6 +101,14 @@ name = "byteorder"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c2-chacha"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.48"
@ -249,6 +257,16 @@ dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getrandom"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glob"
version = "0.2.11"
@ -653,6 +671,11 @@ dependencies = [
"sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ppv-lite86"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro-hack"
version = "0.5.11"
@ -695,6 +718,43 @@ dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
@ -766,6 +826,7 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_contrib 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1140,6 +1201,11 @@ dependencies = [
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasi"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
@ -1218,6 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
@ -1237,6 +1304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7"
@ -1285,11 +1353,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
"checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0"
"checksum pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
@ -1342,6 +1415,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

@ -14,6 +14,7 @@ serde_json = { version="1.0", features= ["preserve_order"] }
serde_yaml = "0.8.11"
json_dotpath = "0.1.2"
titlecase = "0.10.0"
rand = "0.7.2"
# special data structure that preserves order
indexmap = { version="1.3.0", features=["serde-1"] }

@ -13,6 +13,7 @@ use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template;
mod store;
mod session;
use crate::store::form::{render_card_fields, render_empty_fields, RenderedCard, RenderedField, MapFromForm, collect_card_form};
use crate::store::Store;
@ -20,8 +21,9 @@ use parking_lot::RwLock;
use rocket::request::Form;
use rocket::response::Redirect;
use rocket::State;
use rocket::{State, Request};
use std::env;
use crate::session::{SessionID, SessionStore, Session};
#[derive(Serialize, Debug)]
pub struct ListContext<'a> {
@ -29,6 +31,7 @@ pub struct ListContext<'a> {
pub cards: Vec<RenderedCard<'a>>,
pub page: usize,
pub pages: usize,
pub count : usize,
}
const PER_PAGE: usize = 20; // TODO configurable
@ -48,9 +51,19 @@ fn find_page_with_card(store: &Store, card_id: usize) -> Option<usize> {
}
#[get("/?<page>")]
fn route_index(store: State<RwLock<Store>>, page: Option<usize>) -> Template {
fn route_index(
store: State<RwLock<Store>>,
session : Session,
page: Option<usize>
) -> Template {
let rg = store.read();
let mut count : usize = session.get_or_default("foo.bar.count");
count += 1;
session.set("foo.bar.count", count);
println!("{:?}", session);
let mut page = page.unwrap_or_default();
let n_pages = (rg.data.cards.len() as f64 / PER_PAGE as f64).ceil() as usize;
@ -62,6 +75,7 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>) -> Template {
fields: render_empty_fields(&rg),
pages: n_pages,
page,
count,
cards: rg
.data
.cards
@ -174,6 +188,7 @@ fn main() {
rocket::ignite()
.attach(Template::fairing())
.attach(Session::fairing())
.manage(RwLock::new(store))
.mount("/", StaticFiles::from(cwd.join("templates/static/")))
.mount(

@ -0,0 +1,168 @@
use rocket::request::FromRequest;
use rocket::{Outcome, Request, State, http::{Status, Cookies, Cookie}, Response, Data, Rocket};
use std::ops::{Deref, DerefMut};
use std::collections::HashMap;
use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde_json::{Value, Map};
use rocket::fairing::{self, Fairing, Info};
use rand::Rng;
use std::borrow::Cow;
use serde::{Deserialize, Serialize};
use serde::de::DeserializeOwned;
use json_dotpath::DotPaths;
use rocket::response::ResponseBuilder;
const SESSION_ID : &'static str = "SESSID";
type SessionsMap = HashMap<String, SessionInstance>;
#[derive(Debug)]
struct SessionInstance {
data: serde_json::Map<String, Value>,
// TODO expiration
}
#[derive(Default, Debug)]
pub struct SessionStore {
inner: RwLock<SessionsMap>,
}
#[derive(PartialEq, Hash, Clone, Debug)]
pub struct SessionID(String);
impl<'a, 'r> FromRequest<'a, 'r> for &'a SessionID {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, (Status, Self::Error), ()> {
Outcome::Success(request.local_cache(|| {
println!("get id");
if let Some(cookie) = request.cookies().get(SESSION_ID) {
println!("from cookie");
SessionID(cookie.value().to_string()) // FIXME avoid cloning
} else {
println!("new id");
SessionID(rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect())
}
}))
}
}
#[derive(Debug)]
pub struct Session<'a> {
store: State<'a, SessionStore>,
id : &'a SessionID,
}
impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, (Status, Self::Error), ()> {
Outcome::Success(Session {
id: request.local_cache(|| {
if let Some(cookie) = request.cookies().get(SESSION_ID) {
SessionID(cookie.value().to_string())
} else {
SessionID(rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect())
}
}),
store: request.guard().unwrap()
})
}
}
impl<'a> Session<'a> {
pub fn fairing() -> impl Fairing {
SessionFairing
}
pub fn get<T : DeserializeOwned>(&self, path : &str) -> Option<T> {
let rg = self.store.inner.read();
if let Some(ses) = rg.get(&self.id.0) {
ses.data.dot_get(path)
} else {
None
}
}
pub fn get_or<T : DeserializeOwned>(&self, path : &str, def : T) -> T {
self.get(path).unwrap_or(def)
}
pub fn get_or_else<T : DeserializeOwned, F : FnOnce() -> T>(&self, path : &str, def : F) -> T {
self.get(path).unwrap_or_else(def)
}
pub fn get_or_default<T : DeserializeOwned + Default>(&self, path : &str) -> T {
self.get(path).unwrap_or_default()
}
pub fn take<T : DeserializeOwned>(&self, path : &str) -> Option<T> {
let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_take(path)
} else {
None
}
}
pub fn replace<O: DeserializeOwned, N: Serialize>(&self, path : &str, new : N) -> Option<O> {
let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_replace(path, new)
} else {
None
}
}
pub fn set<T : Serialize>(&self, path : &str, value : T) {
let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_set(path, value);
} else {
let mut map = Map::new();
map.dot_set(path, value);
wg.insert(self.id.0.clone(), SessionInstance {
data : map,
});
}
}
pub fn remove(&self, path : &str) {
let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_remove(path);
}
}
}
/// Fairing struct
struct SessionFairing;
impl Fairing for SessionFairing {
fn info(&self) -> Info {
Info {
name: "Session Fairing",
kind: fairing::Kind::Attach | fairing::Kind::Response
}
}
fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
Ok(rocket.manage(SessionStore::default()))
}
fn on_response<'r>(&self, request: &'r Request, response: &mut Response) {
let session = request.local_cache(|| {
SessionID("".to_string())
});
if !session.0.is_empty() {
response.adjoin_header(Cookie::build(SESSION_ID, session.0.clone()).finish());
}
}
}

@ -11,6 +11,8 @@
{%- endblock %}
{% block content -%}
COUNT {{count}}
<table class="cards-table">
<thead>
<tr>

Loading…
Cancel
Save