find a document with the current username in a helper - meteor

I'm trying to create a helper like this:
this.helpers({
data() {
return Customers.findOne({ user: Meteor.user().username });
}
});
but an error occurs, It seems that the user is logging in when the helper is executing, How can I execute the helper after the user is logged in ?

Don't know if is the best solution but I created a deferred promise that wait for the user to login and resolve the $state.
resolve: {
currentUser: ($q) => {
var deferred = $q.defer();
Meteor.autorun(function() {
if(!Meteor.loggingIn()) {
if(!Meteor.user()) {
deferred.reject('PERMISSION_REQUIRED');
} else {
deferred.resolve();
}
}
});
I hope that it can be useful for someone else.

Try this:
data() {
if(Meteor.user()){
return Customers.findOne({ user: Meteor.user().username });
}
}

Try builtin currentUser users helper, which check whether the user is logged in. Like that:
{{#if currentUser}}
{{data}}
{{/if}}

Related

Meteor Methods doesn't update database using Autoform

Using autoform, it seems the data is being passed from autoform as the Meteor method on my server does get the data, but then doing the database update inside of my method doesn't update my database...what am I missing?
Autoform code...
{{> quickForm collection="Rooms" type="method-update"
doc=this autosave=true id=makeUniqueID
meteormethod="updateRoom"}}
Meteor method:
updateRoom: function (room) {
console.log(room);
Rooms.update({_id: room._id}, { $set: {
checkIn: room.checkIn,
checkOut: room.checkOut,
tenantID: room.tenantID,
available: room.available,
needCleaning: room.needCleaning,
}});
},
My allow/deny rules:
Rooms.allow({
insert() { return false; },
update() { return false; },
remove() { return false; }
});
Rooms.deny({
insert() { return true; },
update() { return true; },
remove() { return true; }
});
Below is what I get from the console log in my from my meteor method. So I do get the changes (in this case change the tenantID and the false to available), but it doesn't update in the database. I'm missing a little detail somewhere but can't see it at this point.
The room variable you are passing to the method is nesting everything under the modifier and $set: keys.
You could simply do:
updateRoom: function (room) {
Rooms.update({_id: room._id}, room.modifier);
},
but that's really insecure because you're passing the whole modifier to the method and a hacker could pass in anything they wanted to.
Better:
updateRoom(room) {
check(room,Object);
check(room._id,String);
{checkIn, checkOut, tenantId, available, needCleaning } = room.modifier.$set;
Rooms.update(room._id, { $set: {checkIn, checkOut, tenantId, available, needCleaning }});
},

Restivus authentication for Meteor methods

I'm trying to make my meteor-app for REST services available. For this I use the package "Restivus" which also works just fine. But once I'd like to run a meteor method this.userId is undefined.
Api.addRoute('addArticle', {authRequired: true}, {
post: function () {
console.log(this.userId); //<-- hwuqtFXf8aKperJ5p
try {
Meteor.call("addArticle",this.bodyParams);
} catch (e) {
return {code:500,type:e.error,reason:e.reason};
}
}
});
the method:
new ValidatedMethod({
name: 'addArticle',
....
if (!this.userId) {
throw new Meteor.Error(...); //is thrown
}
What am I doing wrong?
In Meteor methods you get the current userId by doing
Meteor.userId()
and not
this.userId
So you would need to update your code to
if(!Meteor.userId()){
throw new Meteor.Error(403, '403:Forbidden', 'You shall not pass!')
}

How to access FlowRouter subscriptions in Meteor template helpers?

it seems like I can't access a FlowRouter template subscription in my helper. How can you do this?
In my server code:
Meteor.publish('AllUsers', function() {
return Meteor.users.find({}, {fields: {profile: 1}});
})
In my router code:
var userRoutes = FlowRouter.group({
subscriptions: function(params, queryParams) {
this.register('AllUsers', Meteor.subscribe('AllUsers'));
},
});
In my template code:
{{#if checkFlowRouterSubs}}
{{#each getTheUsers}}
{{>userPartial}}
{{/each}}
{{/if}}
In my helpers I have the 'guard':
checkFlowRouterSubs: function() {
if (FlowRouter.subsReady()) {
return true;
};
return false;
},
And then the getTheUsers helper:
...
var users = AllUsers.find(filterObject, { sort: { 'profile.firstname': 1 } }).fetch(); // the actual query definitely works
...
But I get an error:
Exception in template helper: ReferenceError: AllUsers is not defined
I should note that in the getTheUsers helper, FlowRouter.subsReady('AllUsers') returns true
so, first, this :
var userRoutes = FlowRouter.group({
subscriptions: function(params, queryParams) {
this.register('AllUsers', Meteor.subscribe('AllUsers'));
},
});
is NOT server code: it is Client code: the Flow-router is a client side router: counter intuitive but this is the basis of all these routers.
The hint here is that you are 'subscribing' to the publication in this code, so it is on the client side.
Iron-Router is routing both on the server and client-side so it makes things even more confusing when you come from there.
What you are missing here is the publish function on the server side.
Meteor.publish('AllUsers', function() {
return AllUsers.find();
});
EDIT:
The Error
Exception in template helper: ReferenceError: AllUsers is not defined
seems like because you did not define the collection on the client side
var AllUsers = Mongo.Collection('AllUsers'); //or whatever the actual collection
When you try to get data from a subscription, you want to call the actual collection you're looking to get data for, not the subscription name. In this case, I think you mean Meteor.users:
var users = Meteor.users.find(filterObject, { sort: { 'profile.firstname': 1 } });
if( users ) {
return users.fetch();
}

insert still works after Meteor.logout

This Meteor app has the insecure and autopublish removed and accounts-password added.
It uses Accounts.createUser({username: someName, password: somePwrd});
It avoids using allow/deny and uses instead Meteor.call to insert documents because reading in the docs, it says that
Server code is trusted and isn't subject to allow and deny restrictions. That includes methods that are called with Meteor.call — they are expected to do their own access checking rather than relying on allow and deny.
But when I fire up the Meteor.logout(), I am still able to insert new documents to Tasks1 collection. How can that be? I though logout will stop inserting any new documents. How can I fix it? Thanks
///////////////////////////
//both/both.js
///////////////////////////
Tasks1 = new Mongo.Collection('tasks1');
///////////////////////////
//server/server.js
///////////////////////////
Meteor.publish('tasks1', function(){
return Tasks1.find({userId: this.userId});
});
Meteor.methods({
addTasks1: function (doc) {
Tasks1.insert(doc);
}
});
///////////////////////////
//client/client.js
///////////////////////////
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;
Meteor.call('addTasks1', params);
}
}
}
});
Template.mainMenu.events({
'click #logout': function () {
Meteor.logout();
}
});
In your server addTasks1 method, you should first check if the user is a user like so:
Meteor.methods({
addTasks1: function(doc) {
if (!Meteor.userId()) {
throw new Meteor.Error("Not Authorized");
} else {
Tasks1.insert(doc);
}
}
})
Logout alone doesn't stop users from being able to insert. You must edit your method code to achieve this.
addTasks1: function (doc) {
if (Meteor.userId()) {
Tasks1.insert(doc);
}
}

Prevent client from calling server-side methods

so I have a login form that accepts a username and password. When a username/password is entered and submit is clicked, the first step is to check if the account exists and is enabled. I've accomplished that using the code below. The problem is, the server-side method that does the checking, is_user_enabled, can be accessed by the client via the browser console. Usually I can prevent this by doing:
my_method : function(doc) {
if (is_admin()) {
// Only admins can run this method.
}
}
But in the case of is_user_enabled, the user is not logged in yet. So, my question is, what is the correct way to handle this situation?
My code:
client/login.html
{{#autoForm schema=get_login_form_schema id="login_form"}}
{{> flashMessages}}
<fieldset>
<!-- <legend>Create User</legend> -->
{{> afQuickField name="username" placeholder="schemaLabel" label=false}}
{{> afQuickField name="password" placeholder="schemaLabel" type="password" label=false}}
<div>
<button type="submit" class="btn btn-primary">Login</button>
</div>
</fieldset>
{{/autoForm}}
client/lib/helpers.js
AutoForm.hooks({
login_form: {
onSubmit: function (insert_doc, update_doc, current_doc) {
Meteor.call("is_user_enabled", insert_doc, function(error, result) {
if (result) {
// Try to log user in via Meteor.loginWithPassword()
}
});
}
}
});
server/lib/methods.js
Meteor.methods({
is_user_enabled : function(doc) {
// Used by the login form. Returns true if user exists and account is enabled.
check(doc, schemas.login);
var user = Meteor.users.findOne({username: doc.username}, {fields: {status: 1}});
if (user.status === "enabled") {
return true;
}
}
});
Final Solution:
client/lib/helpers.js
AutoForm.hooks({
login_form: {
onSubmit: function (insert_doc, update_doc, current_doc) {
Meteor.loginWithPassword(insert_doc.username, insert_doc.password, function(error) {
// Called with no arguments on success
// or with a single Error argument on failure.
if (error) {
FlashMessages.sendError(error);
this.done();
} else {
// Successful login. Redirect to /.
this.done();
Router.go('/');
}
});
return false; // Prevent browser submit event.
},
}
server/lib/permissions.js
Accounts.validateLoginAttempt(function (info) {
if (info.user && info.user.status === "enabled") {
return true;
} else {
throw new Meteor.Error("Invalid credentials.");
}
});
More info about [Accounts.validateLoginAttempt][1]
You can't prevent the client from calling a server method. Your checks for is_user_enabled and is_admin need to happen inside your server methods as well as on the client. You can of course have private functions inside your methods.js file that only methods on the server can access. For more tips see http://0rocketscience.blogspot.com/2015/07/meteor-security-no-1-meteorcall.html
Yes you can prevent Meteor methods from being executed from the client side. this.connection will only be set inside a method when it is called from the client. When called from the server it will be null. The enables you to do something like this:
serverOnlyMethod: function () {
if(this.connection) throw(new Meteor.Error(403, 'Forbidden.'));
}

Resources