mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Allow and deny rules with factories
This commit is contained in:
@@ -599,7 +599,6 @@ _.extend(Meteor._LivedataSession.prototype, {
|
||||
|
||||
fence.arm(); // we're done adding writes to the fence
|
||||
unblock(); // unblock, if the method hasn't done it already
|
||||
|
||||
exception = wrapInternalException(
|
||||
exception, "while invoking method '" + msg.method + "'");
|
||||
|
||||
|
||||
@@ -8,8 +8,11 @@ _.each(['STRING', 'MONGO'], function (idGeneration) {
|
||||
|
||||
// helper for defining a collection, subscribing to it, and defining
|
||||
// a method to clear it
|
||||
var defineCollection = function(name, insecure) {
|
||||
var collection = new Meteor.Collection(name + idGeneration, {idGeneration: idGeneration});
|
||||
var defineCollection = function(name, insecure, defaultFactory) {
|
||||
var collection = new Meteor.Collection(name + idGeneration, {
|
||||
idGeneration: idGeneration,
|
||||
defaultFactory: defaultFactory
|
||||
});
|
||||
collection._insecure = insecure;
|
||||
|
||||
if (Meteor.isServer) {
|
||||
@@ -57,7 +60,26 @@ _.each(['STRING', 'MONGO'], function (idGeneration) {
|
||||
var restrictedCollectionForFetchAllTest = defineCollection(
|
||||
"collection-restrictedForFetchAllTest", true /*insecure*/);
|
||||
|
||||
var restrictedCollectionWithFactory = defineCollection(
|
||||
"withFactory", false, function (doc) {
|
||||
return doc.a;
|
||||
});
|
||||
|
||||
restrictedCollectionWithFactory.allow({
|
||||
insert: function (userId, doc) {
|
||||
return doc.foo === "foo";
|
||||
},
|
||||
update: function (userId, docs) {
|
||||
return _.all(docs, function (doc) {
|
||||
return doc.foo === "foo";
|
||||
});
|
||||
},
|
||||
remove: function (userId, docs) {
|
||||
return _.all(docs, function (doc) {
|
||||
return doc.bar === "bar";
|
||||
});
|
||||
}
|
||||
});
|
||||
//
|
||||
// Set up allow/deny rules for test collections
|
||||
//
|
||||
@@ -256,6 +278,8 @@ _.each(['STRING', 'MONGO'], function (idGeneration) {
|
||||
}
|
||||
|
||||
if (Meteor.isClient) {
|
||||
|
||||
|
||||
// test that if allow is called once then the collection is
|
||||
// restricted, and that other mutations aren't allowed
|
||||
testAsyncMulti("collection - partial allow, " + idGeneration, [
|
||||
@@ -319,6 +343,41 @@ _.each(['STRING', 'MONGO'], function (idGeneration) {
|
||||
}
|
||||
|
||||
if (Meteor.isClient) {
|
||||
var item1;
|
||||
var item2;
|
||||
testAsyncMulti("collection - restrected factories " + idGeneration, [
|
||||
function (test, expect) {
|
||||
restrictedCollectionWithFactory.insert({
|
||||
a: {foo: "foo", bar: "bar", baz: "baz"}
|
||||
}, expect(function (e, res) {
|
||||
test.isFalse(e);
|
||||
test.isTrue(res);
|
||||
item1 = res;
|
||||
}));
|
||||
restrictedCollectionWithFactory.insert({
|
||||
a: {foo: "foo", bar: "quux", baz: "quux"},
|
||||
b: "potato"
|
||||
}, expect(function (e, res) {
|
||||
test.isFalse(e);
|
||||
test.isTrue(res);
|
||||
item2 = res;
|
||||
}));
|
||||
restrictedCollectionWithFactory.insert({
|
||||
a: {foo: "adsfadf", bar: "quux", baz: "quux"},
|
||||
b: "potato"
|
||||
}, expect(function (e, res) {
|
||||
test.isTrue(e);
|
||||
}));
|
||||
},
|
||||
function (test, expect) {
|
||||
restrictedCollectionWithFactory.remove(item1, expect(function (e, res) {
|
||||
test.isFalse(e);
|
||||
}));
|
||||
restrictedCollectionWithFactory.remove(item2, expect(function (e, res) {
|
||||
test.isTrue(e);
|
||||
}));
|
||||
}
|
||||
]);
|
||||
testAsyncMulti("collection - insecure, " + idGeneration, [
|
||||
function (test, expect) {
|
||||
insecureCollection.callClearMethod(test.runId(), expect(function () {
|
||||
|
||||
@@ -557,6 +557,12 @@ Meteor.Collection.prototype._validatedInsert = function(userId, doc) {
|
||||
self._collection.insert.call(self._collection, doc);
|
||||
};
|
||||
|
||||
var factoryAll = function (validator, docs) {
|
||||
if (validator.factory)
|
||||
return _.map(docs, validator.factory);
|
||||
return docs;
|
||||
};
|
||||
|
||||
// Simulate a mongo `update` operation while validating that the access
|
||||
// control rules set by calls to `allow/deny` are satisfied. If all
|
||||
// pass, rewrite the mongo operation to use $in to set the list of
|
||||
@@ -585,7 +591,7 @@ Meteor.Collection.prototype._validatedUpdate = function(
|
||||
}
|
||||
});
|
||||
|
||||
var findOptions = {};
|
||||
var findOptions = {factory: null};
|
||||
if (!self._validators.fetchAllFields) {
|
||||
findOptions.fields = {};
|
||||
_.each(self._validators.fetch, function(fieldName) {
|
||||
@@ -605,11 +611,15 @@ Meteor.Collection.prototype._validatedUpdate = function(
|
||||
docs = [doc];
|
||||
}
|
||||
|
||||
var factoriedDocs;
|
||||
|
||||
// call user validators.
|
||||
// Any deny returns true means denied.
|
||||
if (_.any(self._validators.update.deny, function(validator) {
|
||||
if (!factoriedDocs)
|
||||
factoriedDocs = factoryAll(validator, docs);
|
||||
return validator(userId,
|
||||
_.map(docs, _.partial(docToValidate, validator)),
|
||||
factoriedDocs,
|
||||
fields,
|
||||
mutator);
|
||||
})) {
|
||||
@@ -617,8 +627,10 @@ Meteor.Collection.prototype._validatedUpdate = function(
|
||||
}
|
||||
// Any allow returns true means proceed. Throw error if they all fail.
|
||||
if (_.all(self._validators.update.allow, function(validator) {
|
||||
if (!factoriedDocs)
|
||||
factoriedDocs = factoryAll(validator, docs);
|
||||
return !validator(userId,
|
||||
_.map(docs, _.partial(docToValidate, validator)),
|
||||
factoriedDocs,
|
||||
fields,
|
||||
mutator);
|
||||
})) {
|
||||
@@ -656,7 +668,7 @@ Meteor.Collection.prototype._validatedUpdate = function(
|
||||
Meteor.Collection.prototype._validatedRemove = function(userId, selector) {
|
||||
var self = this;
|
||||
|
||||
var findOptions = {};
|
||||
var findOptions = {factory: null};
|
||||
if (!self._validators.fetchAllFields) {
|
||||
findOptions.fields = {};
|
||||
_.each(self._validators.fetch, function(fieldName) {
|
||||
@@ -671,13 +683,13 @@ Meteor.Collection.prototype._validatedRemove = function(userId, selector) {
|
||||
// call user validators.
|
||||
// Any deny returns true means denied.
|
||||
if (_.any(self._validators.remove.deny, function(validator) {
|
||||
return validator(userId, docs);
|
||||
return validator(userId, factoryAll(validator, docs));
|
||||
})) {
|
||||
throw new Meteor.Error(403, "Access denied");
|
||||
}
|
||||
// Any allow returns true means proceed. Throw error if they all fail.
|
||||
if (_.all(self._validators.remove.allow, function(validator) {
|
||||
return !validator(userId, _.map(docs, _.partial(docToValidate, validator)));
|
||||
return !validator(userId, factoryAll(validator, docs));
|
||||
})) {
|
||||
throw new Meteor.Error(403, "Access denied");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user