diff --git a/README.md b/README.md index e99c9c06..f70527ca 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,6 @@ Getting started `git clone https://github.com/ThaumRystra/DiceCloud1 dicecloud` `cd dicecloud` -`cd rpg-docs` +`cd app` `bower install` `meteor` diff --git a/app/Routes/API.js b/app/Routes/API.js index 6ad6d71b..7df32907 100644 --- a/app/Routes/API.js +++ b/app/Routes/API.js @@ -6,7 +6,7 @@ Router.map(function() { this.response.setHeader("Content-Type", "application/json"); var query = this.params.query; var key = query && query.key; - ifKeyValid(key, this.response, () => + ifKeyValid(key, this.response, "vmixCharacter", () => this.response.end(vMixCharacter(this.params._id)) ); }, @@ -18,25 +18,46 @@ Router.map(function() { this.response.setHeader("Content-Type", "application/json"); var query = this.params.query; var key = query && query.key; - ifKeyValid(key, this.response, () => + ifKeyValid(key, this.response, "vmixParty", () => this.response.end(vMixParty(this.params._id)) ); }, }); + + this.route("jsonCharacterSheet", { + path: "/character/:_id/json", + where: "server", + action: function() { + this.response.setHeader("Content-Type", "application/json"); + var query = this.params.query; + var key = query && query.key; + ifKeyValid(key, this.response, "jsonCharacterSheet", () => { + if (canViewCharacter(this.params._id, userIdFromKey(key))){ + this.response.end(JSONExport(this.params._id)) + } else { + this.response.writeHead(403, "You do not have permission to view this character"); + this.response.end(); + } + } + ); + }, + }); }); -var ifKeyValid = function(apiKey, response, callback){ +var ifKeyValid = function(apiKey, response, method, callback){ if (!apiKey){ response.writeHead(403, "You must use an api key to access this api"); response.end(); } else if (!isKeyValid(apiKey)){ response.writeHead(403, "API key is invalid"); response.end(); - } else if (isRateLimited(apiKey)){ + } else if (isRateLimited(apiKey, method)){ response.writeHead(429, "Too many requests"); - response.end(); + response.end(JSON.stringify({ + "timeToReset": rateLimiter.check({apiKey: apiKey, method: method}).timeToReset + })); } else { - rateLimiter.increment({apiKey}) + rateLimiter.increment({apiKey: apiKey, method: method}) callback(); } }; @@ -48,11 +69,19 @@ var isKeyValid = function(apiKey){ return !blackListed; }; -var rateLimiter = new RateLimiter(); -rateLimiter.addRule({apiKey: String}, 2, 10000); +var userIdFromKey = function(apiKey){ + var user = Meteor.users.findOne({apiKey}); // we know user exists from isKeyValid + return user._id; +} -var isRateLimited = function(apiKey){ - const limited = !rateLimiter.check({apiKey}).allowed +var rateLimiter = new RateLimiter(); +rateLimiter.addRule({apiKey: String}, 5, 5000); +rateLimiter.addRule({apiKey: String, method: "vmixCharacter"}, 2, 10000); +rateLimiter.addRule({apiKey: String, method: "vmixParty"}, 2, 10000); +rateLimiter.addRule({apiKey: String, method: "jsonCharacterSheet"}, 5, 5000); + +var isRateLimited = function(apiKey, method){ + const limited = !rateLimiter.check({apiKey: apiKey, method: method}).allowed if (limited) { console.log(`Rate limit hit by API key ${apiKey}`); return true; diff --git a/app/lib/functions/characterJSON.js b/app/lib/functions/characterJSON.js new file mode 100644 index 00000000..861bb796 --- /dev/null +++ b/app/lib/functions/characterJSON.js @@ -0,0 +1,17 @@ +JSONExport = function(charId) { + var character = { + "attacks": Attacks.find({charId: charId}).fetch(), + "characters": Characters.find({_id: charId}).fetch(), + "classes": Classes.find({charId: charId}).fetch(), + "containers": Containers.find({charId: charId}).fetch(), + "effects": Effects.find({charId: charId}).fetch(), + "experience": Experiences.find({charId: charId}).fetch(), + "features": Features.find({charId: charId}).fetch(), + "items": Items.find({charId: charId}).fetch(), + "notes": Notes.find({charId: charId}).fetch(), + "proficiencies": Proficiencies.find({charId: charId}).fetch(), + "spellLists": SpellLists.find({charId: charId}).fetch(), + "spells": Spells.find({charId: charId}).fetch() + }; + return JSON.stringify(character); +} diff --git a/app/lib/functions/permissions.js b/app/lib/functions/permissions.js index 9e658d03..8f160297 100644 --- a/app/lib/functions/permissions.js +++ b/app/lib/functions/permissions.js @@ -9,10 +9,11 @@ canViewCharacter = function(charId, userId){ userId = userId || Meteor.userId(); var char = Characters.findOne( charId, - {fields: {owner: 1, writers: 1, readers: 1}} + {fields: {owner: 1, writers: 1, readers: 1, "settings.viewPermission": 1}} ); if (!char) return true; return userId === char.owner || + char.settings.viewPermission === "public" || _.contains(char.writers, userId) || _.contains(char.readers, userId); }; diff --git a/app/public/browserconfig.xml b/app/public/browserconfig.xml index 7e4b2b57..e3ad7fcc 100644 --- a/app/public/browserconfig.xml +++ b/app/public/browserconfig.xml @@ -1,12 +1,12 @@ - - - - - - - - - #b91d1d - - - + + + + + + + + + #b91d1d + + +