From eb9c42ca567ed47a215802169a51df80529f3fc1 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Tue, 4 Jun 2019 13:03:25 +1000 Subject: [PATCH] Began work on spying code --- public/news/2019-06-04-01.md | 3 ++ server/combat.js | 29 ++++++---- server/index.js | 6 +++ server/spying.js | 62 ++++++++++++++++++++++ server/utilities.js | 32 ++++++++++- sql/create_database_structure.sql | 52 +++++++++++++++++- src/components/pages/profile.jsx | 22 ++++++-- src/components/panels/attack_button.jsx | 24 +++++---- src/components/panels/login.jsx | 4 +- src/components/panels/password_change.jsx | 4 +- src/components/panels/password_recover.jsx | 2 +- src/components/panels/password_reset.jsx | 4 +- src/components/panels/signup.jsx | 8 +-- src/components/panels/spy_button.jsx | 0 14 files changed, 215 insertions(+), 37 deletions(-) create mode 100644 server/spying.js delete mode 100644 src/components/panels/spy_button.jsx diff --git a/public/news/2019-06-04-01.md b/public/news/2019-06-04-01.md index 0a5e8d0..7bd1d33 100644 --- a/public/news/2019-06-04-01.md +++ b/public/news/2019-06-04-01.md @@ -4,4 +4,7 @@ _4 June 2019_ * Improved the instructions. * Disable attack button with no soldiers. +* Added credentials to attack status requests (others can't spoof to see who you're attacking anymore - my bad!) +* Began work on spying infrastructure. +* Made the attack button more generic - can reuse a lot of attack code for spying code. * More coming later today... \ No newline at end of file diff --git a/server/combat.js b/server/combat.js index 3173c79..f91316b 100644 --- a/server/combat.js +++ b/server/combat.js @@ -77,18 +77,29 @@ const attackRequest = (connection) => (req, res) => { }); }; -const attackStatusRequest = (connection) => (req, res) => { //TODO: proper credentials - isAttacking(connection, req.body.attacker, (err, attacking, defender) => { +const attackStatusRequest = (connection) => (req, res) => { + //verify the credentials + let query = 'SELECT COUNT(*) AS total FROM sessions WHERE accountId = ? AND token = ?;'; + connection.query(query, [req.body.id, req.body.token], (err, results) => { if (err) throw err; - res.status(200).json({ - status: attacking ? 'attacking' : 'idle', - attacker: req.body.attacker, - defender: defender, - msg: null - }); + if (results[0].total !== 1) { + res.status(400).write(log('Invalid attack status request credentials', req.body.id, req.body.token)); + res.end(); + return; + } - res.end(); + isAttacking(connection, req.body.id, (err, attacking, defender) => { + if (err) throw err; + + res.status(200).json({ + status: attacking ? 'attacking' : 'idle', + defender: defender, + msg: null + }); + + res.end(); + }); }); }; diff --git a/server/index.js b/server/index.js index 09e93ef..3237260 100644 --- a/server/index.js +++ b/server/index.js @@ -51,6 +51,12 @@ app.post('/attackstatusrequest', combat.attackStatusRequest(connection)); app.post('/combatlogrequest', combat.combatLogRequest(connection)); combat.runCombatTick(connection); +let spying = require('./spying.js'); +app.post('/spyrequest', spying.spyRequest(connection)); +app.post('/spystatusrequest', spying.spyStatusRequest(connection)); +app.post('/spylogrequest', spying.spyLogRequest(connection)); +spying.runSpyTick(connection); + let equipment = require('./equipment.js'); app.post('/equipmentrequest', equipment.equipmentRequest(connection)); app.post('/equipmentpurchaserequest', equipment.purchaseRequest(connection)); diff --git a/server/spying.js b/server/spying.js new file mode 100644 index 0000000..97b8de3 --- /dev/null +++ b/server/spying.js @@ -0,0 +1,62 @@ +//environment variables +require('dotenv').config(); + +//libraries +let CronJob = require('cron').CronJob; + +//utilities +let { logDiagnostics } = require('./diagnostics.js'); +let { log } = require('../common/utilities.js'); + +let { isSpying } = require('./utilities.js'); + +const spyRequest = (connection) => (req, res) => { + //TODO + res.status(400).write(log('Not yet implemented', 'spyRequest')); + res.end(); +}; + +const spyStatusRequest = (connection) => (req, res) => { + //verify the credentials + let query = 'SELECT COUNT(*) AS total FROM sessions WHERE accountId = ? AND token = ?;'; + connection.query(query, [req.body.id, req.body.token], (err, results) => { + if (err) throw err; + + if (results[0].total !== 1) { + res.status(400).write(log('Invalid spy status request credentials', req.body.id, req.body.token)); + res.end(); + return; + } + + isSpying(connection, req.body.id, (err, spying, defender) => { + if (err) throw err; + + res.status(200).json({ + status: spying ? 'spying' : 'idle', + attacker: req.body.attacker, + defender: defender, + msg: null + }); + + res.end(); + }); + }); +}; + +const spyLogRequest = (connection) => (req, res) => { + //TODO + res.status(400).write(log('Not yet implemented', 'spyLogRequest')); + res.end(); +}; + +const runSpyTick = (connection) => { + //TODO +}; + +module.exports = { + spyRequest: spyRequest, + spyStatusRequest: spyStatusRequest, + spyLogRequest: spyLogRequest, + runSpyTick: runSpyTick +}; + diff --git a/server/utilities.js b/server/utilities.js index afa59ac..af83686 100644 --- a/server/utilities.js +++ b/server/utilities.js @@ -40,7 +40,7 @@ const isAttacking = (connection, user, cb) => { } else if (typeof(user) === 'string') { query = 'SELECT * FROM pendingCombat WHERE attackerId IN (SELECT id FROM accounts WHERE username = ?);'; } else { - return cb(`Unknown argument type for user: ${typeof(user)}`); + return cb(`isAttacking: Unknown argument type for user: ${typeof(user)}`); } connection.query(query, [user], (err, results) => { @@ -59,8 +59,36 @@ const isAttacking = (connection, user, cb) => { }); }; +const isSpying = (connection, user, cb) => { + let query; + + if (isNormalInteger(user)) { + query = 'SELECT * FROM pendingSpying WHERE attackerId = ?;'; + } else if (typeof(user) === 'string') { + query = 'SELECT * FROM pendingSpying WHERE attackerId IN (SELECT id FROM accounts WHERE username = ?);'; + } else { + return cb(`isSpying: Unknown argument type for user: ${typeof(user)}`); + } + + connection.query(query, [user], (err, results) => { + if (err) throw err; + + if (results.length === 0) { + return cb(undefined, false); + } else { + //get the username of the person being spied on + let query = 'SELECT username FROM accounts WHERE id = ?;'; + connection.query(query, [results[0].defenderId], (err, results) => { + if (err) throw err; + return cb(undefined, true, results[0].username); + }); + } + }); +}; + module.exports = { getStatistics: getStatistics, getOwned: getOwned, - isAttacking: isAttacking + isAttacking: isAttacking, + isSpying: isSpying }; \ No newline at end of file diff --git a/sql/create_database_structure.sql b/sql/create_database_structure.sql index 89cdaf9..408883f 100644 --- a/sql/create_database_structure.sql +++ b/sql/create_database_structure.sql @@ -115,6 +115,41 @@ CREATE TABLE IF NOT EXISTS pastCombat ( CONSTRAINT FOREIGN KEY fk_defenderId(defenderId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE ); +#spying system +CREATE TABLE IF NOT EXISTS pendingSpying ( + id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, + td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + + eventTime TIMESTAMP, + + attackerId INTEGER UNSIGNED UNIQUE, + defenderId INTEGER UNSIGNED, + attackingUnits INTEGER UNSIGNED, + + CONSTRAINT FOREIGN KEY fk_attackerId(attackerId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FOREIGN KEY fk_defenderId(defenderId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS pastSpying ( + id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, + td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + + eventTime TIMESTAMP, + + attackerId INTEGER UNSIGNED, + defenderId INTEGER UNSIGNED, + attackingUnits INTEGER UNSIGNED, + + success ENUM ('success', 'failure'), + + spoilsGold INTEGER, + + /* check the table "equipmentStolen" for a list of equipment stolen */ + + CONSTRAINT FOREIGN KEY fk_attackerId(attackerId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FOREIGN KEY fk_defenderId(defenderId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE +); + #equipment system CREATE TABLE IF NOT EXISTS equipment ( id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, @@ -128,4 +163,19 @@ CREATE TABLE IF NOT EXISTS equipment ( type VARCHAR(50), CONSTRAINT FOREIGN KEY fk_accountId(accountId) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE -); \ No newline at end of file +); + +CREATE TABLE IF NOT EXISTS equipmentStolen ( + id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE, + td TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + + pastSpyingId INTEGER UNSIGNED, + + name VARCHAR(50), + quantity INTEGER, + + type VARCHAR(50), + + CONSTRAINT FOREIGN KEY fk_pastSpyingId(pastSpyingId) REFERENCES pastSpying(id) ON UPDATE CASCADE ON DELETE CASCADE +); + diff --git a/src/components/pages/profile.jsx b/src/components/pages/profile.jsx index 5c5e469..0913ee2 100644 --- a/src/components/pages/profile.jsx +++ b/src/components/pages/profile.jsx @@ -233,16 +233,30 @@ class Profile extends React.Component { setWarning={this.setWarning.bind(this)} attacker={this.props.account.username} defender={this.props.profile.username} - token={this.props.account.token} - /> + statusRequest={'/attackstatusrequest'} + attackRequest={'/attackrequest'} + pendingStatus={'attacking'} + pendingMsg={'Your soldiers are attacking'} + parseUnits={(json) => json.soldiers} + >Attack

Soldiers:

{this.props.profile.soldiers}

-
-
+ json.spies} + >Send Spies
diff --git a/src/components/panels/attack_button.jsx b/src/components/panels/attack_button.jsx index b355be3..1f2fdb6 100644 --- a/src/components/panels/attack_button.jsx +++ b/src/components/panels/attack_button.jsx @@ -6,11 +6,11 @@ class AttackButton extends React.Component { constructor(props) { super(props); this.state = { - soldiers: 0, //NOTE: not stored in profile afterall + units: 0, message: '' }; - this.sendRequest('/attackstatusrequest', {attacker: this.props.attacker}, this.attackStatus.bind(this)); + this.sendRequest(this.props.statusRequest, {/* SO MUCH FOR DEFAULT ARGUMENTS IN NODE */}, this.attackStatus.bind(this)); this.sendRequest('/profilerequest', {username: this.props.attacker}, this.profileData.bind(this)); } @@ -20,22 +20,21 @@ class AttackButton extends React.Component {

{this.state.message}

); } else { - //inject something extra let onClick = (e) => { - this.sendRequest('/attackrequest', {attacker: this.props.attacker, defender: this.props.defender}, this.attackStatus.bind(this)); + this.sendRequest(this.props.attackRequest, {attacker: this.props.attacker, defender: this.props.defender}, this.attackStatus.bind(this)); if (this.props.onClick) { - this.props.onClick(e); + this.props.onClick(e); //inject something extra } }; return ( - + ); } } //gameplay functions - sendRequest(url, args = {}, onSuccess) { //send a unified request, using my credentials + sendRequest(url, args, onSuccess) { //send a unified request, using my credentials //build the XHR let xhr = new XMLHttpRequest(); xhr.open('POST', url, true); @@ -62,13 +61,13 @@ class AttackButton extends React.Component { } attackStatus(json) { - if (json.status === 'attacking') { - this.setState({ message: `Your soldiers are attacking ${json.defender}` }); + if (json.status === this.props.pendingStatus) { + this.setState({ message: `${this.props.pendingMsg} ${json.defender}` }); } } profileData(json) { - this.setState({ soldiers: json.soldiers }); + this.setState({units: this.props.parseUnits(json)}); } }; @@ -78,6 +77,11 @@ AttackButton.propTypes = { attacker: PropTypes.string.isRequired, defender: PropTypes.string.isRequired, + statusRequest: PropTypes.string.isRequired, + attackRequest: PropTypes.string.isRequired, + pendingStatus: PropTypes.string.isRequired, + pendingMsg: PropTypes.string.isRequired, + parseUnits: PropTypes.func.isRequired, className: PropTypes.string, style: PropTypes.object, diff --git a/src/components/panels/login.jsx b/src/components/panels/login.jsx index c5efb72..6308295 100644 --- a/src/components/panels/login.jsx +++ b/src/components/panels/login.jsx @@ -30,12 +30,12 @@ class Login extends React.Component {
- +
- +
diff --git a/src/components/panels/password_change.jsx b/src/components/panels/password_change.jsx index 349d73a..e30d5b2 100644 --- a/src/components/panels/password_change.jsx +++ b/src/components/panels/password_change.jsx @@ -30,12 +30,12 @@ class PasswordChange extends React.Component {
- +
- +
diff --git a/src/components/panels/password_recover.jsx b/src/components/panels/password_recover.jsx index dab5061..fff52ed 100644 --- a/src/components/panels/password_recover.jsx +++ b/src/components/panels/password_recover.jsx @@ -27,7 +27,7 @@ class PasswordRecover extends React.Component {
- +
diff --git a/src/components/panels/password_reset.jsx b/src/components/panels/password_reset.jsx index c0c7ac4..122082c 100644 --- a/src/components/panels/password_reset.jsx +++ b/src/components/panels/password_reset.jsx @@ -29,12 +29,12 @@ class PasswordReset extends React.Component {
- +
- +
diff --git a/src/components/panels/signup.jsx b/src/components/panels/signup.jsx index a6992df..1eb3d01 100644 --- a/src/components/panels/signup.jsx +++ b/src/components/panels/signup.jsx @@ -30,22 +30,22 @@ class Signup extends React.Component {
- +
- +
- +
- +
diff --git a/src/components/panels/spy_button.jsx b/src/components/panels/spy_button.jsx deleted file mode 100644 index e69de29..0000000