Vulcan/packages/vulcan-core/lib/modules/default_mutations.js
Eric Burel bf9ebaf57c
Allow guests to create document as a default
Hi, using when using the default mutations, you can't allow a guest user to create a document, even if you allowed it for `guests` when setting up permissions. This is because a undefined `user` will always trigger a `false` during the "new" check.

I think it is safe to remove this, because anyway you have to manually tell who can create document (or so I think, maybe I'm  wrong?) so you can't allow guests to create documents by mistake.
2018-03-03 23:11:33 +01:00

245 lines
8.8 KiB
JavaScript

/*
Default mutations
*/
import { registerCallback, newMutator, editMutator, removeMutator, Utils, Connectors } from 'meteor/vulcan:lib';
import Users from 'meteor/vulcan:users';
export const getDefaultMutations = (collectionName, options = {}) => {
// register callbacks for documentation purposes
registerCollectionCallbacks(collectionName);
return {
// mutation for inserting a new document
new: {
name: `${collectionName}New`,
description: `Mutation for inserting new ${collectionName} documents`,
// check function called on a user to see if they can perform the operation
check(user, document) {
if (options.newCheck) {
return options.newCheck(user, document);
}
// check if they can perform "foo.new" operation (e.g. "movies.new")
return Users.canDo(user, `${collectionName.toLowerCase()}.new`);
},
async mutation(root, {document}, context) {
const collection = context[collectionName];
// check if current user can pass check function; else throw error
Utils.performCheck(this.check, context.currentUser, document);
// pass document to boilerplate newMutator function
return await newMutator({
collection,
document: document,
currentUser: context.currentUser,
validate: true,
context,
});
},
},
// mutation for editing a specific document
edit: {
name: `${collectionName}Edit`,
description: `Mutation for editing a ${collectionName} document`,
// check function called on a user and document to see if they can perform the operation
check(user, document) {
if (options.editCheck) {
return options.editCheck(user, document);
}
if (!user || !document) return false;
// check if user owns the document being edited.
// if they do, check if they can perform "foo.edit.own" action
// if they don't, check if they can perform "foo.edit.all" action
return Users.owns(user, document) ? Users.canDo(user, `${collectionName.toLowerCase()}.edit.own`) : Users.canDo(user, `${collectionName.toLowerCase()}.edit.all`);
},
async mutation(root, {documentId, set, unset}, context) {
const collection = context[collectionName];
// get entire unmodified document from database
const document = await Connectors.get(collection, documentId);
// check if user can perform operation; if not throw error
Utils.performCheck(this.check, context.currentUser, document);
// call editMutator boilerplate function
return await editMutator({
collection,
documentId: documentId,
set: set,
unset: unset,
currentUser: context.currentUser,
validate: true,
context,
});
},
},
// mutation for upserting a specific document
upsert: {
name: `${collectionName}Upsert`,
description: `Mutation for upserting a ${collectionName} document`,
async mutation(root, { search, set, unset }, context) {
const collection = context[collectionName];
// check if document exists already
const existingDocument = await Connectors.get(collection, search, { fields: { _id: 1 } });
if (existingDocument) {
const editArgs = {
documentId: existingDocument._id,
set,
unset,
};
return await collection.options.mutations.edit.mutation(root, editArgs, context);
} else {
return await collection.options.mutations.new.mutation(root, { document: set }, context);
}
},
},
// mutation for removing a specific document (same checks as edit mutation)
remove: {
name: `${collectionName}Remove`,
description: `Mutation for deleting a ${collectionName} document`,
check(user, document) {
if (options.removeCheck) {
return options.removeCheck(user, document);
}
if (!user || !document) return false;
return Users.owns(user, document) ? Users.canDo(user, `${collectionName.toLowerCase()}.remove.own`) : Users.canDo(user, `${collectionName.toLowerCase()}.remove.all`);
},
async mutation(root, {documentId}, context) {
const collection = context[collectionName];
const document = await Connectors.get(collection, documentId);
Utils.performCheck(this.check, context.currentUser, document, context);
return await removeMutator({
collection,
documentId: documentId,
currentUser: context.currentUser,
validate: true,
context,
});
},
},
}
};
const registerCollectionCallbacks = collectionName => {
collectionName = collectionName.toLowerCase();
registerCallback({
name: `${collectionName}.new.validate`,
arguments: [{document: 'The document being inserted'}, {currentUser: 'The current user'}, {validationErrors: 'An object that can be used to accumulate validation errors'}],
runs: 'sync',
returns: 'document',
description: `Validate a document before insertion (can be skipped when inserting directly on server).`
});
registerCallback({
name: `${collectionName}.new.before`,
arguments: [{document: 'The document being inserted'}, {currentUser: 'The current user'}],
runs: 'sync',
returns: 'document',
description: `Perform operations on a new document before it's inserted in the database.`
});
registerCallback({
name: `${collectionName}.new.after`,
arguments: [{document: 'The document being inserted'}, {currentUser: 'The current user'}],
runs: 'sync',
returns: 'document',
description: `Perform operations on a new document after it's inserted in the database but *before* the mutation returns it.`
});
registerCallback({
name: `${collectionName}.new.async`,
arguments: [{document: 'The document being inserted'}, {currentUser: 'The current user'}, {collection: 'The collection the document belongs to'}],
runs: 'async',
returns: null,
description: `Perform operations on a new document after it's inserted in the database asynchronously.`
});
registerCallback({
name: `${collectionName}.edit.validate`,
arguments: [{modifier: 'The MongoDB modifier'}, {document: 'The document being edited'}, {currentUser: 'The current user'}, {validationErrors: 'An object that can be used to accumulate validation errors'}],
runs: 'sync',
returns: 'modifier',
description: `Validate a document before update (can be skipped when updating directly on server).`
});
registerCallback({
name: `${collectionName}.edit.before`,
arguments: [{modifier: 'The MongoDB modifier'}, {document: 'The document being edited'}, {currentUser: 'The current user'}],
runs: 'sync',
returns: 'modifier',
description: `Perform operations on a document before it's updated in the database.`
});
registerCallback({
name: `${collectionName}.edit.after`,
arguments: [{modifier: 'The MongoDB modifier'}, {document: 'The document being edited'}, {currentUser: 'The current user'}],
runs: 'sync',
returns: 'document',
description: `Perform operations on a document after it's updated in the database but *before* the mutation returns it.`
});
registerCallback({
name: `${collectionName}.edit.async`,
arguments: [{newDocument: 'The document after the edit'}, {document: 'The document before the edit'}, {currentUser: 'The current user'}, {collection: 'The collection the document belongs to'}],
runs: 'async',
returns: null,
description: `Perform operations on a document after it's updated in the database asynchronously.`
});
registerCallback({
name: `${collectionName}.remove.validate`,
arguments: [{document: 'The document being removed'}, {currentUser: 'The current user'}, {validationErrors: 'An object that can be used to accumulate validation errors'}],
runs: 'sync',
returns: 'document',
description: `Validate a document before removal (can be skipped when removing directly on server).`
});
registerCallback({
name: `${collectionName}.remove.before`,
arguments: [{document: 'The document being removed'}, {currentUser: 'The current user'}],
runs: 'sync',
returns: null,
description: `Perform operations on a document before it's removed from the database.`
});
registerCallback({
name: `${collectionName}.remove.async`,
arguments: [{document: 'The document being removed'}, {currentUser: 'The current user'}, {collection: 'The collection the document belongs to'}],
runs: 'async',
returns: null,
description: `Perform operations on a document after it's removed from the database asynchronously.`
});
}