Merge branch 'develop' of ssh://mnml.gg:40022/~/mnml into develop

This commit is contained in:
ntr 2019-07-21 20:11:54 +10:00
commit 5e5cb1a7b0
3 changed files with 92 additions and 33 deletions

View File

@ -12,11 +12,10 @@ const addState = connect(
} = state; } = state;
function submitLogin(name, password) { function submitLogin(name, password) {
postData('/login', { name, password }) postData('/login', { name, password })
.then(res => { .then(res => res.json())
if (!res.ok) return errorToast(res); .then(data => {
res.text() if (!data.success) return errorToast(data.error_message);
}) console.log(data.response);
.then(res => {
ws.connect(); ws.connect();
}) })
.catch(error => errorToast(error)); .catch(error => errorToast(error));
@ -24,11 +23,10 @@ const addState = connect(
function submitRegister(name, password, code) { function submitRegister(name, password, code) {
postData('/register', { name, password, code }) postData('/register', { name, password, code })
.then(res => { .then(res => res.json())
if (!res.ok) return errorToast(res); .then(data => {
res.text() if (!data.success) return errorToast(data.error_message);
}) console.log(data.response);
.then(res => {
ws.connect(); ws.connect();
}) })
.catch(error => errorToast(error)); .catch(error => errorToast(error));

View File

@ -8,6 +8,7 @@ use serde_cbor::{from_slice};
use postgres::transaction::Transaction; use postgres::transaction::Transaction;
use net::MnmlHttpError;
use names::{name as generate_name}; use names::{name as generate_name};
use construct::{Construct, construct_recover, construct_spawn}; use construct::{Construct, construct_recover, construct_spawn};
use instance::{Instance, instance_delete}; use instance::{Instance, instance_delete};
@ -75,7 +76,7 @@ pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
Ok(Account { id, name, balance, subscribed }) Ok(Account { id, name, balance, subscribed })
} }
pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, Error> { pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, MnmlHttpError> {
let query = " let query = "
SELECT id, password, name, balance, subscribed SELECT id, password, name, balance, subscribed
FROM accounts FROM accounts
@ -96,7 +97,7 @@ pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<A
// verify garbage to prevent timing attacks // verify garbage to prevent timing attacks
verify(garbage.clone(), &garbage).ok(); verify(garbage.clone(), &garbage).ok();
return Err(err_msg("account not found")); return Err(MnmlHttpError::AccountNotFound);
}, },
}; };
@ -107,7 +108,7 @@ pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<A
let subscribed: bool = row.get(4); let subscribed: bool = row.get(4);
if !verify(password, &hash)? { if !verify(password, &hash)? {
return Err(err_msg("password does not match")); return Err(MnmlHttpError::PasswordNotMatch);
} }
let balance = u32::try_from(db_balance) let balance = u32::try_from(db_balance)
@ -212,17 +213,17 @@ pub fn set_subscribed(tx: &mut Transaction, id: Uuid, subscribed: bool) -> Resul
Ok(name) Ok(name)
} }
pub fn create(name: &String, password: &String, code: &String, tx: &mut Transaction) -> Result<String, Error> { pub fn create(name: &String, password: &String, code: &String, tx: &mut Transaction) -> Result<String, MnmlHttpError> {
if password.len() < PASSWORD_MIN_LEN { if password.len() < PASSWORD_MIN_LEN {
return Err(err_msg("password must be at least 12 characters")); return Err(MnmlHttpError::PasswordUnacceptable);
} }
if code.to_lowercase() != "grep842" { if code.to_lowercase() != "grep842" {
return Err(err_msg("https://discord.gg/YJJgurM")); return Err(MnmlHttpError::InvalidCode);
} }
if name.len() == 0 { if name.len() == 0 {
return Err(err_msg("account name not supplied")); return Err(MnmlHttpError::AccountNameNotProvided);
} }
let id = Uuid::new_v4(); let id = Uuid::new_v4();
@ -246,7 +247,7 @@ pub fn create(name: &String, password: &String, code: &String, tx: &mut Transact
match result.iter().next() { match result.iter().next() {
Some(row) => row, Some(row) => row,
None => return Err(err_msg("account not created")), None => return Err(MnmlHttpError::DbError),
}; };
// 3 constructs for a team and 1 to swap // 3 constructs for a team and 1 to swap

View File

@ -5,9 +5,11 @@ use iron::headers::{Cookie as CookieHdr, SetCookie};
use iron::prelude::*; use iron::prelude::*;
use iron::status; use iron::status;
use iron::typemap::Key; use iron::typemap::Key;
use iron::mime::Mime;
use iron::{typemap, BeforeMiddleware,AfterMiddleware}; use iron::{typemap, BeforeMiddleware,AfterMiddleware};
use persistent::Read; use persistent::Read;
use router::Router; use router::Router;
use serde::{Serialize, Deserialize};
// use warden::{warden}; // use warden::{warden};
// use pubsub::{pg_listen}; // use pubsub::{pg_listen};
@ -18,7 +20,7 @@ use payments::{stripe};
pub const TOKEN_HEADER: &str = "x-auth-token"; pub const TOKEN_HEADER: &str = "x-auth-token";
#[derive(Fail, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, Fail, Debug, Serialize, Deserialize)]
pub enum MnmlHttpError { pub enum MnmlHttpError {
// User Facing Errors // User Facing Errors
#[fail(display="internal server error")] #[fail(display="internal server error")]
@ -30,26 +32,84 @@ pub enum MnmlHttpError {
#[fail(display="bad request")] #[fail(display="bad request")]
BadRequest, BadRequest,
#[fail(display="account name taken or invalid")] #[fail(display="account name taken or invalid")]
AccountNameNotProvided,
#[fail(display="account name not provided")]
AccountNotFound,
#[fail(display="account not found")]
AccountNameTaken, AccountNameTaken,
#[fail(display="password does not match")]
PasswordNotMatch,
#[fail(display="password unacceptable. must be > 11 characters")] #[fail(display="password unacceptable. must be > 11 characters")]
PasswordUnacceptable, PasswordUnacceptable,
#[fail(display="incorrect token. refresh or logout of existing sessions")]
TokenDoesNotMatch,
#[fail(display="invalid code. https://discord.gg/YJJgurM")] #[fail(display="invalid code. https://discord.gg/YJJgurM")]
InvalidCode, InvalidCode,
} }
impl From<bcrypt::BcryptError> for MnmlHttpError {
fn from(_err: bcrypt::BcryptError) -> Self {
MnmlHttpError::ServerError
}
}
impl From<postgres::Error> for MnmlHttpError {
fn from(_err: postgres::Error) -> Self {
MnmlHttpError::DbError
}
}
impl From<failure::Error> for MnmlHttpError {
fn from(_err: failure::Error) -> Self {
MnmlHttpError::ServerError
}
}
#[derive(Serialize, Deserialize)]
struct JsonResponse {
response: Option<String>,
success: bool,
error_message: Option<String>
}
impl JsonResponse {
fn success(response: String) -> Self {
JsonResponse { response: Some(response), success: true, error_message: None }
}
fn error(msg: String) -> Self {
JsonResponse { response: None, success: false, error_message: Some(msg) }
}
}
fn iron_response (status: status::Status, message: String) -> Response {
let content_type = "application/json".parse::<Mime>().unwrap();
let msg = match status {
status::Ok => JsonResponse::success(message),
_ => JsonResponse::error(message)
};
let msg_out = serde_json::to_string(&msg).unwrap();
return Response::with((content_type, status, msg_out));
}
impl From<MnmlHttpError> for IronError { impl From<MnmlHttpError> for IronError {
fn from(m_err: MnmlHttpError) -> Self { fn from(m_err: MnmlHttpError) -> Self {
let (err, res) = match m_err { let (err, res) = match m_err {
MnmlHttpError::ServerError => (m_err.compat(), status::InternalServerError), MnmlHttpError::ServerError |
MnmlHttpError::DbError => (m_err.compat(), status::InternalServerError), MnmlHttpError::DbError => (m_err.compat(), status::InternalServerError),
MnmlHttpError::Unauthorized => (m_err.compat(), status::Unauthorized),
MnmlHttpError::BadRequest => (m_err.compat(), status::BadRequest),
MnmlHttpError::AccountNameTaken => (m_err.compat(), status::BadRequest),
MnmlHttpError::PasswordUnacceptable => (m_err.compat(), status::BadRequest),
MnmlHttpError::InvalidCode => (m_err.compat(), status::Unauthorized),
};
IronError::new(err, res) MnmlHttpError::AccountNameNotProvided |
MnmlHttpError::AccountNameTaken |
MnmlHttpError::AccountNotFound |
MnmlHttpError::BadRequest |
MnmlHttpError::PasswordUnacceptable => (m_err.compat(), status::BadRequest),
MnmlHttpError::PasswordNotMatch |
MnmlHttpError::InvalidCode |
MnmlHttpError::TokenDoesNotMatch |
MnmlHttpError::Unauthorized => (m_err.compat(), status::Unauthorized),
};
IronError { error: Box::new(err), response: iron_response(res, m_err.to_string()) }
} }
} }
@ -70,7 +130,7 @@ impl BeforeMiddleware for AuthMiddleware {
if cookie.name() == TOKEN_HEADER { if cookie.name() == TOKEN_HEADER {
match account::from_token(&db, cookie.value().to_string()) { match account::from_token(&db, cookie.value().to_string()) {
Ok(a) => req.extensions.insert::<account::Account>(a), Ok(a) => req.extensions.insert::<account::Account>(a),
Err(_) => return Err(IronError::from(MnmlHttpError::Unauthorized)), Err(_) => return Err(IronError::from(MnmlHttpError::TokenDoesNotMatch)),
}; };
} }
} }
@ -110,7 +170,7 @@ fn token_res(token: String) -> Response {
.max_age(Duration::weeks(1)) // 1 week aligns with db set .max_age(Duration::weeks(1)) // 1 week aligns with db set
.finish(); .finish();
let mut res = Response::with(status::Ok); let mut res = iron_response(status::Ok, "token_res".to_string());
res.headers.set(SetCookie(vec![v.to_string()])); res.headers.set(SetCookie(vec![v.to_string()]));
return res; return res;
@ -141,7 +201,7 @@ fn register(req: &mut Request) -> IronResult<Response> {
}, },
Err(e) => { Err(e) => {
warn!("{:?}", e); warn!("{:?}", e);
Err(IronError::from(MnmlHttpError::BadRequest)) Err(IronError::from(e))
} }
} }
} }
@ -170,7 +230,7 @@ fn login(req: &mut Request) -> IronResult<Response> {
}, },
Err(e) => { Err(e) => {
warn!("{:?}", e); warn!("{:?}", e);
Err(IronError::from(MnmlHttpError::Unauthorized)) Err(IronError::from(e))
} }
} }
} }
@ -186,7 +246,7 @@ fn logout(req: &mut Request) -> IronResult<Response> {
tx.commit().or(Err(MnmlHttpError::ServerError))?; tx.commit().or(Err(MnmlHttpError::ServerError))?;
let mut res = Response::with(status::Ok); let mut res = iron_response(status::Ok, "token_res".to_string());
res.headers.set(SetCookie(vec![AUTH_CLEAR.to_string()])); res.headers.set(SetCookie(vec![AUTH_CLEAR.to_string()]));
Ok(res) Ok(res)