Restivus authentication for Meteor methods - meteor

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!')
}

Related

Meteor method create insert hook and bind userId on the server

I implemented a hook function, where I attach some createdAt and updatedAt fields to the doc that is inserted to a collection. I can attach this to any collection like this:
export const insertHook = function (doc) {
try {
const user = Meteor.user();
doc.createdBy = user && user._id ? user._id : null;
doc.createdAt = new Date().getTime();
} catch (e) {
console.err(e);
}
};
Attaching the hook to the collection is basically passing it via a third option in the constructor:
class HookedCollection extends Mongo.Collection {
constructor(name, options, hooks={}) {
super(name, options);
this.insertHook = hooks.insertHook;
}
insert(doc, callback) {
if (this.insertHook && Meteor.isServer)
this.insertHook.call(this, doc);
}
}
export const MyDocs = new HookedCollection("mydocs", {}, {insertHook});
In a Meteor method I just do a normal insert:
Meteor.methods({
insertDoc:function(doc) {
//check doc...
return MyDocs.insert(doc);
}
});
Which creates basically the following error:
Error: Meteor.userId can only be invoked in method calls or publications.
I tried several ways of bind but always ended up in this error. Is there really no way at all to bind the userId to the function?
According to Meteor docs Meteor.userId() is available anywhere but publish functions (Server side Publish function).
You aren't using Meteor.userId() directly in the method but in a callback (see discussion in this github issue). You can pass the userId information to your callback function as a parameter from the method, for example:
// Using Meteor.userId()
Meteor.methods({
insertDoc:function(doc) {
//check doc...
return MyDocs.insert(doc, Meteor.userId());
}
});
// Or using this.userId
Meteor.methods({
insertDoc:function(doc) {
//check doc...
return MyDocs.insert(doc, this.userId());
}
});
As a general rule use Meteor.userId() in the client (that queries the database) and this.userId in the server. More information in this other question Meteor - Why should I use this.userId over Meteor.userId() whenever possible? and in Meteor forums

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 }});
},

How do I return an error from a Meteor.call method inside another Meteor.call

My meteor code goes a couple Meteor.call methods deep at some points. If I have an error in the 2nd layer and I want to throw that meteor error back to the client side how can I do that?
Currently I have something like this, but I'm getting very confusing outputs and I don't think I fully understand what is happening when I'm calling throw new Meteor.Error(500, e.category_code, e.description);
In client.js
Meteor.call('firstCall', data, function (error, result) {
if(result) {
doSomething();
}
else{
console.log(error);//just shows 500
}
});
In server.js
var Future = Meteor.npmRequire("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut.return(result);
}, function (error) {
console.log(error);
fut.throw(error);
});
return fut.wait();
}
firstCall: function (data){
try{
Meteor.call('secondCall', data, 'http://testhref.com/test', 'http://testhref2.com/test' function (error, result) {
return result;
});
}
catch(e){
throw new Meteor.Error(500, e.category_code, e.description);
}
}
secondCall: function (data, paymentHref, otherHref){
try{
var associate = extractFromPromise(balanced.get(paymentHref).associate_to_customer(otherHref).debit({
"amount": data.paymentInformation[0].total_amount * 100,
"appears_on_statement_as": "Trash Mountain"}));
}
catch(e){
Collection.update(data.id, {
$set: {
'failed.category_code': e.category_code,
'failed.description': e.description
}
});
throw new Meteor.Error(500, e.category_code, e.description);
}
}
In your case, the catch in firstCall is not going to have anything defined for e.category_code and e.description when secondCall throws. This is because in secondCall you are passing these two as arguments to Meteor.Error, which takes as its arguments error, reason, and details:
https://github.com/meteor/meteor/blob/devel/packages/meteor/errors.js
In order to pass these through, you will need to amend firstCall to use these properties:
firstCall: function (data){
try{
Meteor.call('secondCall', data, 'http://testhref.com/test', 'http://testhref2.com/test');
}
catch(e){
throw new Meteor.Error(500, e.reason, e.details);
}
}
I'm not even sure you need to split it up into two calls for modularity, as you can just use normal Javascript functions. But we can discuss that elsewhere.
I few things to mention here:
Async function don't throw exceptions (except you make them kind of sync using Meteor._wrapAsync as I will explain later), they return the error on another way (as the first argument in NodeJS callback-style). This applies both for Meteor.call and to your doSomeAsyncThing.
I can't see the benefit of using Meteor.call on the server. Meteor.call is meant to call server methods from the client. In this case you could just call YourObj.secondCall from inside of firstCall.
Returning something from inside of a callback (as you are doing inside firstCall) doesn't have any effect. You want your async code to work as sync code, so I suggest using Meteor._wrapAsync which is very well explained here.
So, I would implement server side a bit different:
firstCall: function (data){
try{
return this.secondCall(data);
}
catch(e){
throw new Meteor.Error(500, e.category_code, e.description);
}
secondCall: function (data){
try{
return Meteor._wrapAsync(doSomeAsyncThing)(data);
}
catch(e){
Collection.update(data.id, {
$set: {
'failed.category_code': e.category_code,
'failed.description': e.description
}
});
throw new Meteor.Error(500, e.category_code, e.description);
}
Hope this helps!

How to verify if a Meteor.user username exists?

I'm building a messenger application, and before a conversation is created I want to verify if a user exists. If it does, then it will create the conversation. If not, then it should return an error. I've been working with this code on the server side but for some reason it won't work. I've tried many different tweaks, but this is basically my structure:
Meteor.methods({
createConversation: function(secondPerson) {
function doesUserExist(secondPerson) {
var userx = Meteor.users.findOne({username: secondPerson});
if (userx === secondPerson) {
return false;
} else {
return true;
}
}
if (doesUserExist()) {
Conversations.insert({
person1: Meteor.user().username,
person2: secondPerson
});
} else {
Conversations.insert({
person1: "didn't work"
});
}
}
});
The main point you were missing is that find returns a cursor, whereas findOne returns a document. Here is one way to implement the method:
Meteor.methods({
createConversation: function(username) {
check(username, String);
if (!this.userId) {
throw new Meteor.Error(401, 'you must be logged in!');
}
if (Meteor.users.findOne({username: username})) {
return Conversations.insert({
person1: Meteor.user().username,
person2: username
});
} else {
throw new Meteor.Error(403, username + " does not exist!");
}
}
});
Note the following features:
validates that username is a string
requires that the user be logged in to create a conversation
reduces the user existence check to a single line
returns the id of the new conversation
uses Meteor.Error with explanations which can be seen on the client
To use it just open your browser console and try making calls like:
Meteor.call('createConversation', 'dweldon', function(err, id){console.log(err, id);});

How to properly replace this.stop() with pause() on Iron Router blaze integration

When I upgrade Iron Router to blaze integration branch, I began receiving this warning:
"You called this.stop() inside a hook or your action function but you should use pause() now instead"
Chrome console --> iron-router.js:2104 --> client/route_controller.js:193 from package
The code is on client:
Router.before(mustBeSignedIn, {except: ['userSignin', 'userSignup', 'home']});
var mustBeSignedIn = function () {
if (!Meteor.user()) {
// render the home template
this.redirect('home');
// stop the rest of the before hooks and the action function
this.stop();
return false;
}
return true;
}
I tried replacing this.stop() with: pause(), Router.pause() and this.pause() but still does not work. Also I haven't found pause function on iron-router package.
How do I properly replace this.stop() with pause()?
Thanks
From what I can tell the pause function is the first parameter your before hook is getting called with. Not in the docs anywhere, but that's what I gathered from the code and it seems to work.
Here's what I use:
var subscribeAllPlanItems = function (pause) {
var planId = this.params._id;
this.subscribe('revenues', planId).wait();
this.subscribe('expenses', planId).wait();
};
var waitForSubscriptions = function (pause) {
if (this.ready()) { //all the subs have come in
//NProgress.done();
setPlan(this.params._id);
} else { //all subscriptions aren't yet ready, keep waiting
//NProgress.start();
pause();
}
};
Router.map(function () {
this.route('calendar', {
path: '/calendar/:_id',
template: 'calendar',
before: [
subscribeAllPlanItems,
waitForSubscriptions
],
});
//Other routes omitted
});
var requireLogin = function (pause) {
if (Meteor.loggingIn()) { //still logging in
pause();
}
if (!Meteor.user()) { //not logged in
this.render('signIn');
pause();
} else { //logged in, life is good
console.log("requireLogin: logged in");
}
};
//This enforces login for all pages except the below ones.
Router.before(requireLogin, {
except: ['landing', 'signUp', 'signIn', 'forgotPassword', 'resetPassword']
});
I opened an issue on Github about this. Here's the response I got:
Oops I may have not changed the redirect method yet. Just use Router.go as it will work fine now. I will change over this.redirect sometime next week or a PR is welcome. Controllers are now automatically stopped if you change routes in a hook. You can pause the current run by calling the pause method which is passed as a parameter to your hooks and action functions.

Resources