diff --git a/server/src/account.rs b/server/src/account.rs index 9b44c2c1..4d0197ee 100644 --- a/server/src/account.rs +++ b/server/src/account.rs @@ -136,10 +136,6 @@ pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result { let row = result.iter().next() .ok_or(format_err!("account not updated {:?}", id))?; - let name: String = row.get(1); - - info!("login account={:?}", name); - Ok(token) } diff --git a/server/src/net.rs b/server/src/net.rs index 3d01572a..1d49f7c4 100644 --- a/server/src/net.rs +++ b/server/src/net.rs @@ -1,16 +1,13 @@ - -use iron::status; -use iron::headers::{SetCookie}; -use iron::prelude::*; -use persistent::Read; -use iron::typemap::Key; -use router::Router; -use iron::{AfterMiddleware}; -use cookie::{Cookie, SameSite}; - use chrono::Duration; - +use cookie::{Cookie, SameSite}; use failure::Fail; +use iron::headers::{Cookie as CookieHdr, SetCookie}; +use iron::prelude::*; +use iron::status; +use iron::typemap::Key; +use iron::{typemap, BeforeMiddleware,AfterMiddleware}; +use persistent::Read; +use router::Router; // use warden::{warden}; // use pubsub::{pg_listen}; @@ -56,6 +53,38 @@ impl From for IronError { } } +impl typemap::Key for account::Account { type Value = account::Account; } +struct AuthMiddleware; +impl BeforeMiddleware for AuthMiddleware { + fn before(&self, req: &mut Request) -> IronResult<()> { + let state = req.get::>().unwrap(); + let db = state.pool.get().or(Err(MnmlHttpError::DbError))?; + + match req.headers.get::() { + Some(cookies) => { + for c in cookies.iter() { + let cookie = Cookie::parse(c) + .or(Err(MnmlHttpError::BadRequest))?; + + // got auth token + if cookie.name() == TOKEN_HEADER { + match account::from_token(&db, cookie.value().to_string()) { + Ok(a) => req.extensions.insert::(a), + Err(_) => return Err(IronError::from(MnmlHttpError::Unauthorized)), + }; + } + } + } + None => (), + }; + + Ok(()) + } +} + +const AUTH_CLEAR: &str = + "x-auth-token=; HttpOnly; SameSite=Strict; Max-Age=-1;"; + struct ErrorHandler; impl AfterMiddleware for ErrorHandler { fn catch(&self, _: &mut Request, mut err: IronError) -> IronResult { @@ -63,12 +92,7 @@ impl AfterMiddleware for ErrorHandler { // on unauthorized we clear the auth token match err.response.status { Some(status::Unauthorized) => { - let v = Cookie::build(TOKEN_HEADER, "") - .http_only(true) - .same_site(SameSite::Strict) - .max_age(Duration::seconds(-1)) // 1 week aligns with db set - .finish(); - err.response.headers.set(SetCookie(vec![v.to_string()])); + err.response.headers.set(SetCookie(vec![AUTH_CLEAR.to_string()])); }, _ => (), }; @@ -79,23 +103,18 @@ impl AfterMiddleware for ErrorHandler { } } -// fn logout(r: HttpRequest, state: web::Data) -> Result { -// match r.cookie("x-auth-token") { -// Some(t) => { -// let db = state.pool.get().or(Err(MnmlHttpError::ServerError))?; -// let mut tx = db.transaction().or(Err(MnmlHttpError::ServerError))?; -// match account::from_token(&mut tx, t.value().to_string()) { -// Ok(a) => { -// account::new_token(&mut tx, a.id).or(Err(MnmlHttpError::Unauthorized))?; -// tx.commit().or(Err(MnmlHttpError::ServerError))?; -// return Ok(logout_res()); -// }, -// Err(_) => Err(MnmlHttpError::Unauthorized), -// } -// }, -// None => Err(MnmlHttpError::Unauthorized), -// } -// } +fn token_res(token: String) -> Response { + let v = Cookie::build(TOKEN_HEADER, token) + .http_only(true) + .same_site(SameSite::Strict) + .max_age(Duration::weeks(1)) // 1 week aligns with db set + .finish(); + + let mut res = Response::with(status::Ok); + res.headers.set(SetCookie(vec![v.to_string()])); + + return res; +} #[derive(Debug,Clone,Deserialize)] @@ -118,7 +137,7 @@ fn register(req: &mut Request) -> IronResult { match account::create(¶ms.name, ¶ms.password, ¶ms.code, &mut tx) { Ok(token) => { tx.commit().or(Err(MnmlHttpError::ServerError))?; - Ok(login_res(token)) + Ok(token_res(token)) }, Err(e) => { warn!("{:?}", e); @@ -133,19 +152,6 @@ struct LoginBody { password: String, } -fn login_res(token: String) -> Response { - let v = Cookie::build(TOKEN_HEADER, token) - .http_only(true) - .same_site(SameSite::Strict) - .max_age(Duration::weeks(1)) // 1 week aligns with db set - .finish(); - - let mut res = Response::with(status::Ok); - res.headers.set(SetCookie(vec![v.to_string()])); - - return res; -} - fn login(req: &mut Request) -> IronResult { let state = req.get::>().unwrap(); let params = match req.get::>() { @@ -160,7 +166,7 @@ fn login(req: &mut Request) -> IronResult { Ok(a) => { let token = account::new_token(&mut tx, a.id).or(Err(MnmlHttpError::ServerError))?; tx.commit().or(Err(MnmlHttpError::ServerError))?; - Ok(login_res(token)) + Ok(token_res(token)) }, Err(e) => { warn!("{:?}", e); @@ -169,6 +175,27 @@ fn login(req: &mut Request) -> IronResult { } } +fn logout(req: &mut Request) -> IronResult { + let state = req.get::>().unwrap(); + match req.extensions.get::() { + Some(a) => { + let db = state.pool.get().or(Err(MnmlHttpError::DbError))?; + let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?; + + account::new_token(&mut tx, a.id).or(Err(MnmlHttpError::Unauthorized))?; + + tx.commit().or(Err(MnmlHttpError::ServerError))?; + + let mut res = Response::with(status::Ok); + res.headers.set(SetCookie(vec![AUTH_CLEAR.to_string()])); + Ok(res) + + }, + None => Err(IronError::from(MnmlHttpError::Unauthorized)), + } +} + + const MAX_BODY_LENGTH: usize = 1024 * 1024 * 10; pub struct State { @@ -181,10 +208,13 @@ impl Key for State { type Value = State; } pub fn start(pool: PgPool) { let mut router = Router::new(); router.post("/api/login", login, "login"); + router.post("/api/logout", logout, "logout"); + router.post("/api/register", register, "register"); let mut chain = Chain::new(router); chain.link(Read::::both(State { pool })); chain.link_before(Read::::one(MAX_BODY_LENGTH)); + chain.link_before(AuthMiddleware); chain.link_after(ErrorHandler); Iron::new(chain).http("127.0.0.1:40000").unwrap(); } diff --git a/server/src/ws.rs b/server/src/ws.rs index e63901b7..444cd744 100644 --- a/server/src/ws.rs +++ b/server/src/ws.rs @@ -35,6 +35,7 @@ pub fn start(pool: PgPool) { let (acc_s, acc_r) = unbounded(); + // search through the ws request for the auth cookie let cb = |req: &Request| { let err = || ErrorResponse { error_code: StatusCode::FORBIDDEN, @@ -50,7 +51,6 @@ pub fn start(pool: PgPool) { // got auth token if cookie.name() == TOKEN_HEADER { - info!("{:?}", cookie.value().to_string()); acc_s.send(Some(cookie.value().to_string())).or(Err(err()))?; } }; @@ -61,6 +61,7 @@ pub fn start(pool: PgPool) { let mut websocket = accept_hdr(stream.unwrap(), cb).unwrap(); + // get a copy of the account let account = match acc_r.recv().unwrap() { Some(t) => { let db = ws_pool.get()