This commit is contained in:
ntr 2019-06-16 15:45:03 +10:00
parent 9fcdbeb370
commit 102a8e9817
6 changed files with 72 additions and 30 deletions

View File

@ -3,38 +3,22 @@ const preact = require('preact');
const { Component } = require('preact') const { Component } = require('preact')
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const SERVER = process.env.NODE_ENV === 'production' ? '/' : 'http://localhost:40000'; const { postData } = require('../utils');
function postData(url = '/', data = {}) {
// Default options are marked with *
return fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
// mode: "no-cors", // no-cors, cors, *same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "include", // include, same-origin, *omit
headers: {
'Accept': 'application/json',
'content-type': 'application/json'
},
redirect: "error", // manual, *follow, error
// referrer: "", // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(response => response.json()); // parses response to JSON
}
const addState = connect( const addState = connect(
null, (state) => {
(dispatch) => { const {
ws
} = state;
function submitLogin(name, password) { function submitLogin(name, password) {
postData(`${SERVER}/login`, { name, password }) postData('/login', { name, password })
.then(data => console.log(JSON.stringify(data))) .then(data => ws.connect())
.catch(error => console.error(error)); .catch(error => console.error(error));
} }
function submitRegister(name, password, code) { function submitRegister(name, password, code) {
postData(`${SERVER}/register`, { name, password, code }) postData('/register', { name, password, code })
.then(data => console.log(JSON.stringify(data))) .then(data => ws.connect())
.catch(error => console.error(error)); .catch(error => console.error(error));
} }

View File

@ -1,8 +1,9 @@
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const preact = require('preact'); const preact = require('preact');
const { Fragment } = require('preact'); const { Fragment } = require('preact');
const actions = require('../actions');
const { postData } = require('../utils');
const actions = require('../actions');
const { saw } = require('./shapes'); const { saw } = require('./shapes');
const testGame = process.env.NODE_ENV === 'development' && require('./../test.game'); const testGame = process.env.NODE_ENV === 'development' && require('./../test.game');
@ -38,6 +39,10 @@ const addState = connect(
return ws.sendInstanceList(); return ws.sendInstanceList();
} }
function logout() {
postData('/logout').then(() => window.location.reload(true));
}
return { return {
account, account,
instances, instances,
@ -47,6 +52,7 @@ const addState = connect(
sendInstanceState, sendInstanceState,
sendAccountInstances, sendAccountInstances,
sendInstanceList, sendInstanceList,
logout,
}; };
}, },
function receiveDispatch(dispatch) { function receiveDispatch(dispatch) {
@ -97,6 +103,7 @@ function Nav(args) {
sendInstanceState, sendInstanceState,
sendAccountInstances, sendAccountInstances,
sendInstanceList, sendInstanceList,
logout,
setTestGame, setTestGame,
setTestInstance, setTestInstance,
@ -133,6 +140,7 @@ function Nav(args) {
<h2>Hax</h2> <h2>Hax</h2>
<button onClick={() => setTestGame(account.id)}>Test Game</button> <button onClick={() => setTestGame(account.id)}>Test Game</button>
<button onClick={() => setTestInstance(account.id)}>Test Instance</button> <button onClick={() => setTestInstance(account.id)}>Test Instance</button>
<button onClick={() => logout()}>Logout</button>
</Fragment>) </Fragment>)
: null; : null;

View File

@ -345,6 +345,24 @@ const TARGET_COLOURS = {
BROWN: '#583108', BROWN: '#583108',
}; };
const SERVER = process.env.NODE_ENV === 'production' ? '/' : 'http://localhost:40000';
function postData(url = '/', data = {}) {
// Default options are marked with *
return fetch(`${SERVER}${url}`, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
// mode: "no-cors", // no-cors, cors, *same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "include", // include, same-origin, *omit
headers: {
'Accept': 'application/json',
'content-type': 'application/json'
},
redirect: "error", // manual, *follow, error
// referrer: "", // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
}
module.exports = { module.exports = {
stringSort, stringSort,
convertItem, convertItem,
@ -352,6 +370,8 @@ module.exports = {
eventClasses, eventClasses,
getCombatSequence, getCombatSequence,
getCombatText, getCombatText,
postData,
SERVER,
NULL_UUID, NULL_UUID,
STATS, STATS,
COLOURS, COLOURS,

View File

@ -4,7 +4,7 @@ exports.up = async knex => {
table.timestamps(true, true); table.timestamps(true, true);
table.string('name', 42).notNullable().unique(); table.string('name', 42).notNullable().unique();
table.string('password').notNullable(); table.string('password').notNullable();
table.string('token', 64); table.string('token', 64).notNullable();
table.timestamp('token_expiry'); table.timestamp('token_expiry');
table.index('name'); table.index('name');

View File

@ -99,7 +99,6 @@ pub fn account_login(name: &String, password: &String, tx: &mut Transaction) ->
SELECT id, password, name SELECT id, password, name
FROM accounts FROM accounts
WHERE name = $1 WHERE name = $1
RETURNING id, name;
"; ";
let result = tx let result = tx
@ -134,7 +133,7 @@ pub fn account_login(name: &String, password: &String, tx: &mut Transaction) ->
account_set_token(tx, &account) account_set_token(tx, &account)
} }
fn account_set_token(tx: &mut Transaction, account: &Account) -> Result<String, Error> { pub fn account_set_token(tx: &mut Transaction, account: &Account) -> Result<String, Error> {
let mut rng = thread_rng(); let mut rng = thread_rng();
let token: String = iter::repeat(()) let token: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric)) .map(|()| rng.sample(Alphanumeric))

View File

@ -17,7 +17,7 @@ use r2d2_postgres::{TlsMode, PostgresConnectionManager};
use rpc::{receive, RpcResult, RpcErrorResponse, AccountLoginParams, AccountCreateParams}; use rpc::{receive, RpcResult, RpcErrorResponse, AccountLoginParams, AccountCreateParams};
use warden::{warden}; use warden::{warden};
use account::{Account, account_login, account_create, account_from_token}; use account::{Account, account_login, account_create, account_from_token, account_set_token};
pub type Db = PooledConnection<PostgresConnectionManager>; pub type Db = PooledConnection<PostgresConnectionManager>;
type PgPool = Pool<PostgresConnectionManager>; type PgPool = Pool<PostgresConnectionManager>;
@ -174,6 +174,17 @@ fn token_res(token: String, secure: bool) -> HttpResponse {
.finish() .finish()
} }
fn token_clear() -> HttpResponse {
HttpResponse::Ok()
.cookie(Cookie::build("x-auth-token", "")
// .secure(secure)
.http_only(true)
.same_site(SameSite::Strict)
.max_age(-1) // 1 week aligns with db set
.finish())
.finish()
}
fn login(state: web::Data<State>, params: web::Json::<AccountLoginParams>) -> Result<HttpResponse, MnmlError> { fn login(state: web::Data<State>, params: web::Json::<AccountLoginParams>) -> Result<HttpResponse, MnmlError> {
let db = state.pool.get().or(Err(MnmlError::ServerError))?; let db = state.pool.get().or(Err(MnmlError::ServerError))?;
let mut tx = db.transaction().or(Err(MnmlError::ServerError))?; let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
@ -190,6 +201,24 @@ fn login(state: web::Data<State>, params: web::Json::<AccountLoginParams>) -> Re
} }
} }
fn logout(r: HttpRequest, state: web::Data<State>) -> Result<HttpResponse, MnmlError> {
match r.cookie("x-auth-token") {
Some(t) => {
let db = state.pool.get().or(Err(MnmlError::ServerError))?;
let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
match account_from_token(t.value().to_string(), &mut tx) {
Ok(a) => {
account_set_token(&mut tx, &a).or(Err(MnmlError::Unauthorized))?;
tx.commit().or(Err(MnmlError::ServerError))?;
return Ok(token_clear());
},
Err(_) => Err(MnmlError::Unauthorized),
}
},
None => Err(MnmlError::Unauthorized),
}
}
fn register(state: web::Data<State>, params: web::Json::<AccountCreateParams>) -> Result<HttpResponse, MnmlError> { fn register(state: web::Data<State>, params: web::Json::<AccountCreateParams>) -> Result<HttpResponse, MnmlError> {
let db = state.pool.get().or(Err(MnmlError::ServerError))?; let db = state.pool.get().or(Err(MnmlError::ServerError))?;
let mut tx = db.transaction().or(Err(MnmlError::ServerError))?; let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
@ -235,6 +264,7 @@ pub fn start() {
.wrap(middleware::Logger::default()) .wrap(middleware::Logger::default())
.wrap(Cors::new().supports_credentials()) .wrap(Cors::new().supports_credentials())
.service(web::resource("/login").route(web::post().to(login))) .service(web::resource("/login").route(web::post().to(login)))
.service(web::resource("/logout").route(web::post().to(logout)))
.service(web::resource("/register").route(web::post().to(register))) .service(web::resource("/register").route(web::post().to(register)))
.service(web::resource("/ws/").route(web::get().to(connect)))) .service(web::resource("/ws/").route(web::get().to(connect))))
.bind("127.0.0.1:40000").expect("could not bind to port") .bind("127.0.0.1:40000").expect("could not bind to port")
@ -245,6 +275,7 @@ pub fn start() {
.data(State { pool: pool.clone(), secure: true }) .data(State { pool: pool.clone(), secure: true })
.wrap(middleware::Logger::default()) .wrap(middleware::Logger::default())
.service(web::resource("/login").route(web::post().to(login))) .service(web::resource("/login").route(web::post().to(login)))
.service(web::resource("/logout").route(web::post().to(logout)))
.service(web::resource("/register").route(web::post().to(register))) .service(web::resource("/register").route(web::post().to(register)))
.service(web::resource("/ws/").route(web::get().to(connect)))) .service(web::resource("/ws/").route(web::get().to(connect))))
.bind("127.0.0.1:40000").expect("could not bind to port") .bind("127.0.0.1:40000").expect("could not bind to port")