From 81131ddb9f6129613409b552cca4626a745400cf Mon Sep 17 00:00:00 2001 From: Thaum Rystra Date: Thu, 21 May 2020 16:50:06 +0200 Subject: [PATCH] Allowed non-patreons to view, but not edit, sheets and libraries --- app/imports/api/creature/Creatures.js | 6 + app/imports/api/library/Libraries.js | 11 + app/imports/api/sharing/sharingPermissions.js | 46 ++- app/imports/api/users/patreon/tiers.js | 6 +- .../character/CharacterSheetToolbarItems.vue | 1 + .../ui/dialogStack/DialogComponentIndex.js | 3 + app/imports/ui/library/LibraryBrowser.vue | 266 ++++++++++-------- app/imports/ui/pages/CharacterList.vue | 50 ++-- app/imports/ui/pages/PatreonLevelTooLow.vue | 6 + .../ui/properties/forms/ResourcesForm.vue | 5 +- app/imports/ui/router.js | 27 +- app/imports/ui/user/TierTooLowDialog.vue | 49 ++++ 12 files changed, 303 insertions(+), 173 deletions(-) create mode 100644 app/imports/ui/user/TierTooLowDialog.vue diff --git a/app/imports/api/creature/Creatures.js b/app/imports/api/creature/Creatures.js index 8ca02be9..27ce6859 100644 --- a/app/imports/api/creature/Creatures.js +++ b/app/imports/api/creature/Creatures.js @@ -4,6 +4,7 @@ import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js'; import SharingSchema from '/imports/api/sharing/SharingSchema.js'; import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js'; +import { getUserTier } from '/imports/api/users/patreon/tiers.js'; import '/imports/api/creature/removeCreature.js'; @@ -108,6 +109,11 @@ const insertCreature = new ValidatedMethod({ throw new Meteor.Error('Creatures.methods.insert.denied', 'You need to be logged in to insert a creature'); } + let tier = getUserTier(this.userId); + if (!tier.paidBenefits){ + throw new Meteor.Error('Creatures.methods.insert.denied', + `The ${tier.name} tier does not allow you to insert a creature`); + } // Create the creature document let charId = Creatures.insert({ diff --git a/app/imports/api/library/Libraries.js b/app/imports/api/library/Libraries.js index 5be82d49..dbbf013c 100644 --- a/app/imports/api/library/Libraries.js +++ b/app/imports/api/library/Libraries.js @@ -1,8 +1,10 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; import SimpleSchema from 'simpl-schema'; import SharingSchema from '/imports/api/sharing/SharingSchema.js'; import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js'; import { assertEditPermission, assertOwnership } from '/imports/api/sharing/sharingPermissions.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; +import { getUserTier } from '/imports/api/users/patreon/tiers.js' /** * Libraries are trees of library nodes where each node represents a character @@ -38,6 +40,15 @@ const insertLibrary = new ValidatedMethod({ ], schema: LibrarySchema.omit('owner', 'isDefault'), run(library) { + if (!this.userId) { + throw new Meteor.Error('Libraries.methods.insert.denied', + 'You need to be logged in to insert a library'); + } + let tier = getUserTier(this.userId); + if (!tier.paidBenefits){ + throw new Meteor.Error('Libraries.methods.insert.denied', + `The ${tier.name} tier does not allow you to insert a library`); + } library.owner = this.userId; return Libraries.insert(library); }, diff --git a/app/imports/api/sharing/sharingPermissions.js b/app/imports/api/sharing/sharingPermissions.js index c65596e6..00deca12 100644 --- a/app/imports/api/sharing/sharingPermissions.js +++ b/app/imports/api/sharing/sharingPermissions.js @@ -1,17 +1,18 @@ import { _ } from 'meteor/underscore'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; +import { getUserTier } from '/imports/api/users/patreon/tiers.js'; function assertIdValid(userId){ if (!userId || typeof userId !== 'string'){ - throw new Meteor.Error("Permission denied", - "No user ID given for edit permission check"); + throw new Meteor.Error('Permission denied', + 'No user ID given for edit permission check'); } } function assertdocExists(doc){ if (!doc){ - throw new Meteor.Error("Permission denied", - `No such document exists`); + throw new Meteor.Error('Permission denied', + 'No such document exists'); } } @@ -21,8 +22,8 @@ export function assertOwnership(doc, userId){ if (doc.owner === userId ){ return true; } else { - throw new Meteor.Error("Permission denied", - `You are not the owner of this document`); + throw new Meteor.Error('Permission denied', + 'You are not the owner of this document'); } } @@ -35,11 +36,34 @@ export function assertOwnership(doc, userId){ export function assertEditPermission(doc, userId) { assertIdValid(userId); assertdocExists(doc); - if (doc.owner === userId || _.contains(doc.writers, userId)){ + let user = Meteor.users.findOne(userId, { + fields: { + 'services.patreon': 1, + 'roles': 1, + } + }); + + // Admin override + if (user.roles && user.roles.includes('admin')){ + return true; + } + + // Ensure the user is of a tier with paid benefits + let tier = getUserTier(user); + if (!tier.paidBenefits){ + throw new Meteor.Error('Edit permission denied', + `The ${tier.name} tier does not allow you to edit this document`); + } + + // Ensure the user is authorized for this specific document + if ( + doc.owner === userId || + _.contains(doc.writers, userId) + ){ return true; } else { - throw new Meteor.Error("Edit permission denied", - `You do not have permission to edit this document`); + throw new Meteor.Error('Edit permission denied', + 'You do not have permission to edit this document'); } } @@ -74,8 +98,8 @@ export function assertViewPermission(doc, userId) { ){ return true; } else { - throw new Meteor.Error("View permission denied", - `You do not have permission to view this character`); + throw new Meteor.Error('View permission denied', + 'You do not have permission to view this character'); } } diff --git a/app/imports/api/users/patreon/tiers.js b/app/imports/api/users/patreon/tiers.js index 3b58984a..ccb3df1c 100644 --- a/app/imports/api/users/patreon/tiers.js +++ b/app/imports/api/users/patreon/tiers.js @@ -59,7 +59,11 @@ export function getTierByEntitledCents(entitledCents = 0){ export function getUserTier(user){ if (!user) throw 'user must be provided'; if (typeof user === 'string'){ - user = Meteor.users.findOne(user); + user = Meteor.users.findOne(user, { + fields: { + 'services.patreon': 1, + } + }); if (!user) throw 'User not found'; } const entitledCents = getEntitledCents(user); diff --git a/app/imports/ui/creature/character/CharacterSheetToolbarItems.vue b/app/imports/ui/creature/character/CharacterSheetToolbarItems.vue index 979a1d22..99e7357f 100644 --- a/app/imports/ui/creature/character/CharacterSheetToolbarItems.vue +++ b/app/imports/ui/creature/character/CharacterSheetToolbarItems.vue @@ -1,6 +1,7 @@ diff --git a/app/imports/ui/pages/CharacterList.vue b/app/imports/ui/pages/CharacterList.vue index ef6af7ca..33b63365 100644 --- a/app/imports/ui/pages/CharacterList.vue +++ b/app/imports/ui/pages/CharacterList.vue @@ -53,6 +53,18 @@ + + add + + @@ -86,6 +100,7 @@ import Creatures, {insertCreature} from '/imports/api/creature/Creatures.js'; import Parties from '/imports/api/campaign/Parties.js'; import LabeledFab from '/imports/ui/components/LabeledFab.vue'; + import { getUserTier } from '/imports/api/users/patreon/tiers.js'; const characterTransform = function(char){ char.url = `/character/${char._id}/${char.urlName || '-'}`; @@ -136,26 +151,21 @@ }, methods: { insertCharacter(){ - insertCreature.call((error, result) => { - if (error){ - console.error(error); - } else { - this.$router.push({ path: `/character/${result}`}) - } - }); - - /* - store.commit("pushDialogStack", { - component: CharacterCreationDialog, - data: {}, - element: undefined, - returnElement: undefined, - callback(result){ - if (!result) return; - insertCreature.call(result); - }, - }); - */ + let tier = getUserTier(Meteor.userId()); + if (tier.paidBenefits){ + insertCreature.call((error, result) => { + if (error){ + console.error(error); + } else { + this.$router.push({ path: `/character/${result}`}) + } + }); + } else { + this.$store.commit('pushDialogStack', { + component: 'tier-too-low-dialog', + elementId: 'new-character-button', + }); + } }, } }; diff --git a/app/imports/ui/pages/PatreonLevelTooLow.vue b/app/imports/ui/pages/PatreonLevelTooLow.vue index ef0de5c2..b81026dc 100644 --- a/app/imports/ui/pages/PatreonLevelTooLow.vue +++ b/app/imports/ui/pages/PatreonLevelTooLow.vue @@ -12,6 +12,12 @@ You need to be at least Adventurer tier (or be invited by a Patron of a higher tier) to access this beta + + Join now + diff --git a/app/imports/ui/properties/forms/ResourcesForm.vue b/app/imports/ui/properties/forms/ResourcesForm.vue index f82b5450..60337f0e 100644 --- a/app/imports/ui/properties/forms/ResourcesForm.vue +++ b/app/imports/ui/properties/forms/ResourcesForm.vue @@ -28,7 +28,7 @@