Throwing a Meteor.Error
is definitely a solid option for halting your method execution. I’m guessing you meant to write something like this:
checkAdmin = function (userId) {
userId = userId || Meteor.userId()
if (!Meteor.users.findOne(userId).admin) {
throw new Meteor.Error("not-authorized", "Not authorized.");
}
}
There are a couple improvements I’d make before using this checkAdmin
function a production application.
First, I wouldn’t assume that userId
has been checked before passing it into checkAdmin
. Imagine some scenario where you’re calling checkAdmin
on a userId
passed into a method (but never the current user’s _id
; always user this.userId
to get that). You might not check that userId
in your method because you assume that checkAdmin
will check it for you. In that case, a malicious user could pass up a query selector that always selects an admin, bypassing your checkAdmin
assertion.
Assume that userId
is { admin: true }
. Calling checkAdmin({ admin: true });
will always pass, as long as an admin user exists in your database.
Even worse, because you’re dropping the unchecked userId
directly into your findOne
call, a malicious user could DOS your entire application by passing up a userId
of { $where: "d = new Date; do {c = new Date;} while (c - d < 10000);" }
.
That said, definitely check
your userId
in checkAdmin
:
checkAdmin = function (userId) {
check(userId, Match.Optional(String));
userId = userId || Meteor.userId();
if (!Meteor.users.findOne(userId).admin) {
throw new Meteor.Error("not-authorized", "Not authorized.");
}
}
Lastly, if a user isn’t found for a given userId
, your checkAdmin
will throw a "can’t get field admin
of undefined
" error, rather than your “Not authorized” error. This still works in securing your method, but it probably isn’t as helpful. I’d check that your user exists before checking if admin
is truthy.
checkAdmin = function (userId) {
check(userId, Match.Optional(String));
userId = userId || Meteor.userId();
let user = Meteor.users.findOne(userId);
if (!user || !user.admin) {
throw new Meteor.Error("not-authorized", "Not authorized.");
}
}
Or a bit cleaner with some help from Lodash:
checkAdmin = function (userId) {
check(userId, Match.Optional(String));
userId = userId || Meteor.userId();
if (!_.get(Meteor.users.findOne(userId), 'admin') {
throw new Meteor.Error("not-authorized", "Not authorized.");
}
}
P.S. I’m working on a project called Secure Meteor, which will be a book/guide on securing your Meteor application with a heavy emphasis on explaining the nuts and bolts of Meteor-specific vulnerabilities, how they work, and how to prevent them. If you’re interested in that kind of thing, sign up and I’ll let you know when it’s finished!