Meteor Collection and Security - meteor

I am curious if I'm setting up the allow statement on this collection correctly. I'm using aldeed:autoform and aldeed:collection2.
Below is the snapshot of a issue-collection.js from a toy project.
Is this the proper way to set up allow checks? Do these run on both client (for minimongo) and server? Specifically, on most update calls, is return !!userId && (doc.userId == userId); enough to ensure the user is logged in AND the logged in user is the owner of the document?
Clarification and actual question: Do the allow and deny methods run on BOTH server and client? Or do they run only on the client?
Issues = new Mongo.Collection("issues");
if (Meteor.isClient){
Meteor.subscribe("issues");
}
if(Meteor.isServer){
Meteor.publish('issues', function () {
return Issues.find({}, {limit: ServerSettings.maxSubscribe});
});
}
Issues.attachSchema(new SimpleSchema({
issue: {
type: String,
label: "Describe the issue you noticed",
max:256
}
}));
//SECURITY - Allow Callbacks for posting
Issues.allow({
insert: function(userId, doc) {
/* Throw in some defaults. */
doc.userId = userId;
doc.sumbitDate = new Date();
doc.date = new Date();
// only allow posting if you are logged in
return !! userId;
},
update: function(userId, doc) {
// only allow updating if you are logged in
return !!userId && (doc.userId == userId);
},
remove: function(userID, doc) {
//only allow deleting if you are owner
return doc.submittedById === Meteor.userId();
}
});

Remember, allow/deny comes from the client. And you can't trust anything that comes from the client (userId, date, etc).
What you want to do is call a Meteor.method from the client & _.extend the document with trustworthy data from the server.
As an example, rewrite that code in the browser console & change the value for the userId.
Check out Discover Meteor blog to learn more (it's probably the best source for learning basic patterns) https://www.discovermeteor.com/blog.

Related

Different userId get same document with server publish this.userId

With autopublish package is removed, While this Meteor code is running, a different userId has been confirmed on 2 different browsers consoles Meteor.userId();
But when a string is typed in the inputText of one of them, and a collection.insert is done, the other shows the same string.
I thought that this.userId was good enough for the server to publish only the documents that belongs to each of the different clients simultaneously.
Why is this happening and how to fix it? Thanks
Server
Meteor.publish('displayCol', function () {
return DisplayCol.find({userId: this.userId});
});
DisplayCol.before.insert(function (userId, doc) {
doc.userId = userId;
});
Client
Template.index.helpers({
taskInputs: function () {
var ready = Meteor.subscribe('displayCol').ready();
var data = DisplayCol.find({});
return {items: data, ready: ready};
}
});
Do you have autopublish still installed? If so, both clients will get everything automatically. Remove it with 'meteor remove autopublish'
You can also add the {userId: Meteor.userId()} condition on the client side.

failed: Access denied on meteor collection

This Meteor app has the insecure and autopublish removed and accounts-password added.
It uses Accounts.createUser({username: someName, password: somePwrd}); which can be verified on the mongo prompt.
I am trying to Tasks1.insert(params); and getting access denied
I don't know why it get Access denied for update and insert on the browser console. Please tell me why and how to fix it? Thanks
//both.js
Tasks1 = new Mongo.Collection('tasks1');
/////////////////////////////////////////////////////
//server.js
Meteor.publish('tasks1', function(){
return Tasks1.find({userId: this.userId});
});
Meteor.methods({
logMeIn: function(credentials) {
var idPin = credentials[0] + credentials[1];
Accounts.createUser({username: idPin, password: credentials[1]});
}
});
Meteor.users.allow({
insert: function (userId, doc) {
console.log(userId);
//var u = Meteor.users.findOne({_id:userId});
return true;
}
});
/////////////////////////////////////////////////////
//client.js
Template.login.events({
'click #logMe': function() {
var credentials = [$('#id').val(), $('#pin').val()];
Meteor.call('logMeIn', credentials, function(err, result) {
if (result) {
console.log('logged in!');
}
});
}
});
Template.footer.events({
'click button': function () {
if ( this.text === "SUBMIT" ) {
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
var params = {};
params[inputs[i].name] = inputs[i].value;
Tasks1.insert(params); //<<<<<<----------------------
}
}
}
});
Update:
Since you have edited your question and added that Tasks1.insert(params); is getting access denied message, you should add allow rules on Tasks collection and not Meteor.users collection.
Tasks.allow({
insert: function (userId, doc) {
return true;
},
update: function (userId, doc, fieldNames, modifier) {
return true;
},
remove: function (userId, doc) {
return true;
}
});
If Accounts.createUser is working without allow rules on Meteor.users then please remove them as it might allow users to insert/delete others from client itself.
End of update.
Since you removed insecure, you need to add allow/deny rules for inserting, updating or deleting files from a collection.
Meteor.users.allow({
insert: function (userId, doc) {
//Normally I would check if (this.userId) to see if the method is called by logged in user or guest
//you can also add some checks here like user role based check etc.,
return true;
},
update: function (userId, doc, fieldNames, modifier) {
//similar checks like insert
return true;
},
remove: function (userId, doc) {
//similar checks like insert
return true;
}
});
Check the API documentation for more details.
Defining your Meteor.methods like this will define it for both server and client. This means the you will be trying to create a user TWICE, once on the server (the one that works) and another time on the client. The client does not have the right to insert user documents so you receive this error.
There are two options for you:
1: Define the method on the server only by surrounding it by if(Meteor.isServer) or putting it in a folder named "server"
2: leave it as is, it will not cause harm but keep showing the error in console.
I am sure there is a 3rd and maybe 4th solution, but those are the two I'd use.

Add extra user field

In my Meteor app I use the default accounts package, which gives me the default login and registration functionality. Now I want to add an extra field to user, say nickname, and for the logged in user the possibility to edit this information.
For editing the profile I suppose I should be doing something like this:
Template.profileEdit.events({
'submit form': function(e) {
e.preventDefault();
if(!Meteor.user())
throw new Meteor.Error(401, "You need to login first");
var currentUserId = this._id;
var user = {
"profile.nickname": $(e.target).find('[name=nickname]').val()
};
Meteor.users.update(currentUserId, {
$set: user
}, function(error){
if(error){
alert(error.reason);
} else {
Router.go('myProfile', {_id: currentUserId});
}
});
}
});
But I doesn't store the info if I look in Mongo. Also when showing the profile, {{profile.nickname}} returns empty. What is wrong here?
Edit: added collections\users.js to show permissions:
Meteor.users.allow({
update: function (userId, doc) {
if (userId && doc._id === userId) {
return true;
}
}
});
Meteor.users.deny({
update: function(userId, user, fieldNames) {
return (_.without(fieldNames, 'profile.nickname').length > 0);
}
});
Yeah, I believe that should do the job, although I haven't actually run the code. The idea is certainly right.
The main things to be aware of are:
The necessity to allow the user doc to be edited from the client with an appropriate Meteor.users.allow() block on the server, assuming you're going to remove the "insecure" package (which you need to before doing anything in production).
The fact that "by default the server publishes username, emails, and profile", so you'll need to write a Meteor.publish function on the server and subscribe to it if you want to expose any other fields within the user document to the client once you've removed the "autopublish" package (which again, you really should).

How do I use Firebase Simple Login with email & password

Firebase Simple login provides an email/password option, how do I use it? Starting from from creating a user, storing data for that user, to logging them in and out.
There are three distinct steps to be performed (let's assume you have jQuery):
1. Set up your callback
var ref = new Firebase("https://demo.firebaseio-demo.com");
var authClient = new FirebaseAuthClient(ref, function(error, user) {
if (error) {
alert(error);
return;
}
if (user) {
// User is already logged in.
doLogin(user);
} else {
// User is logged out.
showLoginBox();
}
});
2. User registration
function showLoginBox() {
...
// Do whatever DOM operations you need to show the login/registration box.
$("#registerButton").on("click", function() {
var email = $("#email").val();
var password = $("#password").val();
authClient.createUser(email, password, function(error, user) {
if (!error) {
doLogin(user);
} else {
alert(error);
}
});
});
}
3. User login
function showLoginBox() {
...
// Do whatever DOM operations you need to show the login/registration box.
$("#loginButton").on("click", function() {
authClient.login("password", {
email: $("#email").val(),
password: $("#password").val(),
rememberMe: $("#rememberCheckbox").val()
});
});
}
When the login completes successfully, the call you registered in step 1 will be called with the correct user object, at which point we call doLogin(user) which is a method you will have to implement.
The structure of the user data is very simple. It is an object containing the following properties:
email: Email address of the user
id: Unique numeric (auto-incrementing) ID for the user
FirebaseAuthClient will automatically authenticate your firebsae for you, not further action is required. You can now use something like the following in your security rules:
{
"rules": {
"users": {
"$userid": {
".read": "auth.uid == $userid",
".write": "auth.uid == $userid"
}
}
}
}
This means, if my User ID is 42, only I can write or read at example.firebaseio-demo.com/users/42 - when I am logged in - and no-one else.
Note that Simple Login does not store any additional information about the user other than their ID and email. If you want to store additional data about the user, you must do so yourself (probably in the success callback for createUser). You can store this data as you normally would store any data in Firebase - just be careful about who can read or write to this data!
Just incase someone is reached to this thread and looking for some example application using the firebase authentication. Here are two examples
var rootRef = new Firebase('https://docs-sandbox.firebaseio.com/web/uauth');
......
.....
....
http://jsfiddle.net/firebase/a221m6pb/embedded/result,js/
http://www.42id.com/articles/firebase-authentication-and-angular-js/

meteor allow rules

I have a question on meteor's parties example.
If I call this code:
Parties.allow({
insert: function () {
return true;
},
remove: function (){
return true;
},
update: function() {
return true;
}
});
everybody can do insert, remove and update.
The code from the example is
Parties.allow({
insert: function (userId, party) {
return false; // no cowboy inserts -- use createPage method
},
update: function (userId, parties, fields, modifier) {
return _.all(parties, function (party) {
if (userId !== party.owner)
return false; // not the owner
var allowed = ["title", "description", "x", "y"];
if (_.difference(fields, allowed).length)
return false; // tried to write to forbidden field
// A good improvement would be to validate the type of the new
// value of the field (and if a string, the length.) In the
// future Meteor will have a schema system to makes that easier.
return true;
});
},
remove: function (userId, parties) {
return ! _.any(parties, function (party) {
// deny if not the owner, or if other people are going
return party.owner !== userId || attending(party) > 0;
});
}
});
So my question is where the variables useriD and party at this line for example
insert: function (userId, party) {
are defined?
Are these the variables I call in the method
Meteor.call("createParty", variable1, variable2)
? But this wouldn't make sense because the client calls
Meteor.call('createParty', {
title: title,
description: description,
x: coords.x,
y: coords.y,
public: public
}
I hope somebody can explain the allow functions to me? Thanks!
To understand allow/deny, you need to understand where the userId and doc parameters come from. (Just as in any function definition, the actual parameter names don't matter.) Looking just at the Parties insert example:
Parties.allow({
insert: function (userId, party) {
return false; // no cowboy inserts -- use createPage method
}
});
The party parameter is the doc that's being inserted:
Parties.insert(doc);
The userId parameter is set automatically IF you're using the Meteor Accounts auth system. Otherwise, you have to set it yourself on the server. How do you do that?
In general, you call code on the server from the client by using Meteor.call(). Since there's no built-in API to set userId (other than Accounts), you have to write your own (goes in your server code):
Meteor.methods({
setUserId: function(userId) {
this.setUserId(userId);
}
});
Then you can call it like this, anywhere in your client code:
Meteor.call('setUserId', userId);
1) Where the variables useriD and party are defined? Nowhere! the intention is that no user can call this function.
This is in order to proctect the database from users that could insert manually new parties using the console. Remmember that Meteor replicates the database in client and server.
Any user could insert manually new parties through the console. This is fine. But then the server would reject the insert since it is not allowed.
2) Are these the variables I call in the method Meteor.call("createParty", variable1, variable2)? Yes the variables are available, but this code is not using the correct definition which is:
Meteor.methods({
createParty: function (options) {
And afterwards it is used as
Meteor.call('createParty',
{ title: title, public: public, ... }, // options array!!
function (error, party) { ... } // function executed after the call
);
Did it help you?

Resources