mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Document allow/deny.
This commit is contained in:
@@ -203,6 +203,8 @@ object, which provides the following:
|
||||
* `isSimulation`: a boolean value, true if this invocation is a stub.
|
||||
* `unblock`: when called, allows the next method from this client to
|
||||
begin running.
|
||||
* `userId`: a function that returns the id of the current user.
|
||||
* `setUserId`: a function that associates the current client with a user.
|
||||
|
||||
Calling `methods` on the client defines *stub* functions associated with
|
||||
server methods of the same name. You don't have to define a stub for
|
||||
@@ -217,10 +219,23 @@ intended to *simulate* the result of what the server's method will do,
|
||||
but without waiting for the round trip delay. If a stub throws an
|
||||
exception it will be logged to the console.
|
||||
|
||||
|
||||
{{> api_box method_invocation_userId}}
|
||||
|
||||
The user for a method is determined when the method starts running and
|
||||
will not change during method execution, even if `unblock` is called.
|
||||
|
||||
{{> api_box method_invocation_setUserId}}
|
||||
|
||||
Calling this function from a method will result in all future methods
|
||||
received on the same connection having their `userId` set to the
|
||||
provided value. Future calls to `this.userId` in the same method will
|
||||
also return the new value.
|
||||
|
||||
Values should correspond to the `_id` field of a document in the
|
||||
<a href="#meteor_users">`Meteor.users`</a> collection.
|
||||
|
||||
|
||||
{{> api_box method_invocation_isSimulation}}
|
||||
|
||||
{{> api_box method_invocation_unblock}}
|
||||
@@ -631,24 +646,141 @@ Example:
|
||||
|
||||
{{> api_box allow}}
|
||||
|
||||
`allow` and its sibling function <a href='#deny'>`deny`</a> control
|
||||
permission to write to the server database from the client using
|
||||
auto-generated `insert`, `update` and `remove` methods. This lets
|
||||
clients use the same API as the server to update the database while
|
||||
maintaining the ability to enforce security against rogue clients.
|
||||
|
||||
Writes initiated on the server, such as from a method, are always
|
||||
allowed. The `allow` and <a href='#deny'>`deny`</a> functions only
|
||||
control the client's access to the server database API. Using custom <a
|
||||
href="#methods_header">methods</a> instead of `allow`ing direct database
|
||||
access is a good approach to creating a secure app, but it is sometimes
|
||||
easier and faster to develop with a simple access control function based
|
||||
approach to security.
|
||||
|
||||
Each access control function receives the `userId` of the currently
|
||||
logged in user (or `null` if the user is not logged in) as the first
|
||||
argument, and the document or documents in question as the second
|
||||
argument. If the function returns `true`, it indicates that the write
|
||||
should be allowed (in the case of `allow`) or denied (in the case of
|
||||
`deny`). If the function returns false it does not allow or deny the
|
||||
request. If no other `allow` statement allows the request, it is denied.
|
||||
|
||||
|
||||
<dl class="callbacks">
|
||||
{{#dtdd "insert(userId, doc)"}}
|
||||
blah blah
|
||||
`doc` is the document the client wants to insert.
|
||||
{{/dtdd}}
|
||||
|
||||
{{#dtdd "update(userId, docs, fields, modifier)"}}
|
||||
blah
|
||||
`docs` is an array of documents that the client is asking to modify. These
|
||||
are fetched from the database and have not yet had the request modifications
|
||||
applied. If you pass the `fetch` argument to `allow` and `deny`, these
|
||||
documents can contain a subset of the database fields, which can be a
|
||||
performance improvement.
|
||||
|
||||
`fields` is a list of top level fields in the document the client is
|
||||
asking to modify. If the client asks to modify a sub-field
|
||||
(eg with `{$set: {'foo.bar': true}}`) this will contain only the top level
|
||||
field (eg `['foo']`).
|
||||
|
||||
`modifier` is the raw mongo modifier the client sent. Many uses will not
|
||||
need to inspect this parameter.
|
||||
{{/dtdd}}
|
||||
|
||||
{{#dtdd "remove(userId, docs)"}}
|
||||
blah
|
||||
`docs` is the list of documents the client is asking to remove. As with
|
||||
`update` above, the `fetch` option controls which fields are populated
|
||||
in `docs`.
|
||||
{{/dtdd}}
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
Example: XXX test me!
|
||||
|
||||
// Create a collection where users can only write documents that
|
||||
// they 'own' using an 'owner' field in each document.
|
||||
Posts = new Meteor.Collection("posts");
|
||||
|
||||
Posts.allow({
|
||||
insert: function (userId, doc) {
|
||||
// the user must be logged in, and the document must be owned by the user
|
||||
return (userId && doc.owner === userId);
|
||||
},
|
||||
update: function (userId, docs, fields, modifier) {
|
||||
// can only change your own documents
|
||||
return _.all(docs, function(doc) {
|
||||
return doc.owner === userId;
|
||||
});
|
||||
},
|
||||
remove: function (userId, docs) {
|
||||
// can only remove your own documents
|
||||
return _.all(docs, function(doc) {
|
||||
return doc.owner === userId;
|
||||
});
|
||||
},
|
||||
fetch: ['owner']
|
||||
});
|
||||
|
||||
Posts.deny({
|
||||
update: function (userId, docs, fields, modifier) {
|
||||
// can't change owners
|
||||
return _.contains(fields, 'owner');
|
||||
},
|
||||
remove: function (userId, docs) {
|
||||
// can't remove locked documents
|
||||
return _.any(docs, function (doc) {
|
||||
return doc.locked;
|
||||
});
|
||||
},
|
||||
fetch: ['locked']
|
||||
});
|
||||
|
||||
|
||||
|
||||
`allow` and `deny` can be called multiple times to allow or deny
|
||||
additional actions. The access control functions are evaluated as follows:
|
||||
|
||||
- If any deny function returns true, the request is denied.
|
||||
- Otherwise, if any allow function returns true, the request is allowed.
|
||||
- Otherwise, the request is denied.
|
||||
|
||||
In other words, the results of multiple functions are logically `OR`-ed
|
||||
together. The execution order of multiple functions is not specified,
|
||||
and execution of validators is not guaranteed. If the system can
|
||||
determine the result after executing only a subset of the validators, it
|
||||
will not call the remaining ones.
|
||||
|
||||
The `insecure` package controls what is allowed when you do not call
|
||||
`allow` or `deny` on a collection. With the `insecure` package
|
||||
installed, all writes are allowed on collections that have never had
|
||||
`allow` or `deny` called. This can be useful during development, to
|
||||
quickly prototype differnt schemas without worring about security. Calling
|
||||
`allow({})` on a collection will disable all client writes, even when
|
||||
the `insecure` package is loaded.
|
||||
|
||||
|
||||
|
||||
{{> api_box deny}}
|
||||
|
||||
This is the sibling function to <a href='#allow'>`allow`</a>, it takes
|
||||
the same arguments. The only difference is that when an access control
|
||||
function registered with `deny` returns true, the request is denied
|
||||
instead of allowed.
|
||||
|
||||
Calling `deny` only has an effect when there are also calls to <a
|
||||
href='#allow'>`allow`</a>. If there are no <a href='#allow'>`allow`</a>
|
||||
functions registered, `deny` functions are not called, as there is no
|
||||
way the request would be permitted.
|
||||
|
||||
`deny` functions take precedence over <a href='#allow'>`allow`</a>
|
||||
functions. If any `deny` function returns true the request is denied and
|
||||
the <a href='#allow'>`allow`</a> functions are not evaluated.
|
||||
|
||||
|
||||
|
||||
<h2 id="meteor_collection_cursor"><span>Cursors</span></h2>
|
||||
|
||||
|
||||
@@ -199,18 +199,18 @@ Template.api.method_invocation_userId = {
|
||||
id: "method_userId",
|
||||
name: "<i>this</i>.userId()",
|
||||
locus: "Anywhere",
|
||||
descr: ["Returns the id of current user, or `null` if no user logged in. XXX Will remain constant."]
|
||||
descr: ["Returns the id of the current user, or `null` if no user is logged in."]
|
||||
};
|
||||
|
||||
Template.api.method_invocation_setUserId = {
|
||||
id: "method_setUserId",
|
||||
name: "<i>this</i>.setUserId(userId)",
|
||||
locus: "Server",
|
||||
descr: ["Set the userId for this session. This affects all future method calls. Pass `null` to log the user out."],
|
||||
descr: ["Set a user id for this session."],
|
||||
args: [
|
||||
{name: "userId",
|
||||
type: "String",
|
||||
descr: "XXX"}
|
||||
descr: "The id of the user for this connection, or `null` to log the user out."}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -458,11 +458,14 @@ Template.api.allow = {
|
||||
id: "allow",
|
||||
name: "<em>collection</em>.allow(options)",
|
||||
locus: "Server",
|
||||
descr: ["Control access to XXX"],
|
||||
descr: ["Specify access control functions to allow clients to write to this collection using the default Mongo mutator methods."],
|
||||
options: [
|
||||
{name: "insert",
|
||||
{name: "insert, update, remove",
|
||||
type: "Function",
|
||||
descr: "XXX"}
|
||||
descr: "Access control functions that are called for each client-initiated database write. Return true to allow the write. See below for details on the arguments to access control functions. You can specify any combination of the three functions."},
|
||||
{name: "fetch",
|
||||
type: "Array of Strings",
|
||||
descr: "Specific fields to retrieve when reading documents from the database to pass to `update` and `remove` access control functions. If this is not specified, all fields are retrieved. Specifying which fields to fetch can be a performance improvement."}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -470,11 +473,14 @@ Template.api.deny = {
|
||||
id: "deny",
|
||||
name: "<em>collection</em>.deny(options)",
|
||||
locus: "Server",
|
||||
descr: ["Control access to XXX"],
|
||||
descr: ["Specify access control functions to forbid clients from writing to this collection using the default Mongo mutator methods."],
|
||||
options: [
|
||||
{name: "insert",
|
||||
{name: "insert, update, remove",
|
||||
type: "Function",
|
||||
descr: "XXX"}
|
||||
descr: "Access control functions that are called for each client-initiated database write. Return true to deny the write. See <a href='#allow'>`allow`</a> for details."},
|
||||
{name: "fetch",
|
||||
type: "Array of Strings",
|
||||
descr: "Specific fields to retrieve when reading documents from the database to pass to `update` and `remove` access control functions. If this is not specified, all fields are retrieved. Specifying which fields to fetch can be a performance improvement."}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -265,6 +265,10 @@ dl.callbacks dt .name, dl.methods dt .name {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
dl.callbacks {
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
#main dd p {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user