How can I access express request object in Mongoose virtual get func - javascript

I tried to find this, but I could not find a solution which will answer my doubt.
I have a virtual mongoose property defined as :
postSchema.virtual('permissions').get() => {
});
What I am trying to achieve is fill this permissions property of string array type, with a list of permissions that user has on each post. This logic is derived through the owner user id, which is stored in the post mongo database and the user id present in the request object of express, which is coming from the requestor.
However, I realised that the request object is not available to virtual method:
postSchema.virtual('permissions').get((req) => {
// req is null.
});
Now, I do have a solution to fix this issue by making a find result to lean at service level.
app.get('/api/posts', (req, res) => {
PostModel.find({}, '-fbEmailAddress', { sort: { created_at: -1 } }).lean().exec(function (err, posts) {
posts.array.forEach(element => {
element.permissions = // write your permission logic here
});
res.send(posts);
});
});
However, if I do so, it will not stop calling . any virtual properties that I have also defined. I loose the opportunity to manipulate firstname, lastname to fullname et. al.
Do you guys have any recommendations to resolve this in a nicest possible way, so that I have an access to the current user id coming in the request object?
Please note that it is an API, so I don't want to introduce any kind of session object.
Cheers

I think you should look at this module https://github.com/othiym23/node-continuation-local-storage
It allows to set context variables in your request middleware and then use them in all functions called from it, e.g. mongoose model handlers.

Related

Parse Server Node.js SDK: Alternative to Parse.User.become?

I want to completely dissociate my client app from Parse server, to ease the switch to other Baas/custom backend in the future. As such, all client request will point to a node.js server who will make the request to Parse on behalf of the user.
Client <--> Node.js Server <--> Parse Server
As such, I need the node.js server to be able to switch between users so I can keep the context of their authentification.
I know how to authentificate, then keep the sessionToken of the user, and I ve seen during my research than the "accepted" solution to this problem was to call Parse.User.disableUnsafeCurrentUser, then using Parse.User.become() to switch the current user to the one making a request.
But that feels hackish, and I m pretty sure it will, sooner or later, lead to a race condition where the current user is switched before the request is made to Parse.
Another solution I found was to not care about Parse.User, and use the masterKey to save everything by the server, but that would make the server responsible of the ACL.
Is there a way to make request from different user other than thoses two?
Any request to the backend (query.find(), object.save(), etc) takes an optional options parameter as the final argument. This lets you specify extra permissions levels, such as forcing the master key or using a specific session token.
If you have the session token, your server code can make a request on behalf of that user, preserving ACL permissions.
Let's assume you have a table of Item objects, where we rely on ACLs to ensure that a user can only retrieve his own Items. The following code would use an explicit session token and only return the Items the user can see:
// fetch items visible to the user associate with `token`
fetchItems(token) {
new Parse.Query('Item')
.find({ sessionToken: token })
.then((results) => {
// do something with the items
});
}
become() was really designed for the Parse Cloud Code environment, where each request lives in a sandbox, and you can rely on a global current user for each request. It doesn't really make sense in a Node.js app, and we'll probably deprecate it.
I recently wrote a NodeJS application and had the same problem. I found that the combination of Parse.User.disableUnsafeCurrentUser and Parse.User.become() was not only hackish, but also caused several other problems I wasn't able to anticipate.
So here's what I did: I used
Parse.Cloud.useMasterKey(); and then loaded the current user by session ID as if it was a regular user object. It looked something like this:
module.exports = function(req, res, next) {
var Parse = req.app.locals.parse, query;
res.locals.parse = Parse;
if (req.session.userid === undefined) {
res.locals.user = undefined;
return next();
}
Parse.Cloud.useMasterKey();
query = new Parse.Query(Parse.User);
query.equalTo("objectId", req.session.userid);
query.first().then(function(result) {
res.locals.user = result;
return next();
}, function(err) {
res.locals.user = undefined;
console.error("error recovering user " + req.session.userid);
return next();
});
};
This code can obviously be optimized, but you can see the general idea. Upside: It works! Downside: No more use of Parse.User.current(), and the need to take special care in the backend that no conditions occur where someone overwrites data without permission.

Using Promises / Node

I'm trying to find a way/best way to use promises in my Node application.
I'll be honest, I don't 100% understand promises, but I'm getting better with more practice.
What I'm currently looking to do is after someone logs in:
Get a facebook access token
Access the users profile, update the user document in Mongodb or create a new user document if they don't exist
Access facebook friends list and cross reference a "friends" document in Mongodb.
Problem I have is to do point 3, I require the access token from point 1 AND the user document either existing or newly created in point 2 to pass into the third function.
My chain of functions looks like:
getAccessToken(req)
.then(function(accessToken){
getUserProfile(req, accessToken)
.then(findUser.bind(null, req, res))
.then(...)
});
getAccessToken returns "accessToken" from Facebook.
getUserProfile returns the user from Facebook.
findUser finds existing or creates new user Document in Mongodb.
How do I return the existing or newly created user object and pass it through coupled with the accessToken in the form of a promise?
You can either nest promises:
getAccessToken(req)
.then(function(accessToken) {
return getUserProfile(req, accessToken)
.then(findUser.bind(null, req, res))
.then(function(user) {
// use accessToken & user
})
});
Or store resolved values in the outer scope:
var _accessToken;
getAccessToken(req)
.then(function(accessToken) {
_accessToken = accessToken;
return getUserProfile(req, accessToken);
})
.then(findUser.bind(null, req, res))
.then(function(user) {
// use _accessToken & user
});
Remember to always return the nested promise.

Mongoose pass req object to middleware

I am writing a middleware for mongoose that gets executed for every find object using pre query hook.
postSchema.pre('query', function(query, next) {
// I want to access the req.user object here
query.populate('Category');
next();
});
I want to access req.user object inside the pre for every request made to the api server. How can i pass the object to the middleware?
Is it even possible?
https://github.com/Automattic/mongoose/issues/1931
I found the above but it doesnt talk about passing req object.
====================
Edit after some confusion about the question.
What i am trying to accomplish is to get the req.user role and the model name pass it to another function to get the query condition for find. So depending on the user role and the type of model accessed the query condition will change.
Wrap the middleware in another middleware that has access to req.
Something like, assuming express
router.verb('/some-route', function (req, res, next) {
postSchema.pre('query', function(query, next) {
console.log(req);
query.populate('Category');
next();
});
});
Edit
- Attach this only to the route that you want the prehook for.
Disclaimer - Not tested.
I know i'm joining this party late but you can use a service to pass the data you want between the request object and the mongoose prehook method. In your service create private variables that hold the data that you want to pass. Set those variables in your custom middleware and call the service get method to get the values in the mongoose prehook method.
Use
query.op to get the type of query
query.options to get other options like {runValidators: true}
query._condition to get the conditions of the query
query._update to get the incoming body
query._fields to get the selected fields,
You can also log the query to the terminal to see various options
Yes, it is possible.
restify.serve(router, model, {
preCreate: function (req, res, next) {
req.body.createdBy = req.user._id
next()
}
})
Follow this doc

Parse.com: Authenticate user before updating object?

The scenario is similar to Evernote: a user can own multiple notes that can be viewed publicly but cannot be edited by anyone other than himself.
(I have implemented creating notes, but now get confused on updating notes. Have read docs of data & security, but it didn't help me much; though I've known ACL.)
I tried this in backend:
Parse.beforeSave("Notes", function (request, response) {
Parse.User.current();
});
But I got error saying null (or undefined? sorry for forgetting the exact error) is not a function. I think that meant there is no Parse.User or Parse.User.current available in Cloud Function.
Then according to a question in archive of Parse.com forum I found:
Parse.beforeSave("Notes", function (request, response) {
var userId = new Parse.User({id : request.params.userId});
});
But it doesn't seem to be like a method of authentication. I mean, by this, I just trust data form client without authenticating it.
request.user will contain the user object that is making the request. Do whatever check you have to do on that object and return an error if the user is not allowed to edit it.
Even better, set proper ACL on each note object, for example "Public Read" and only write access for the creator. That should make the beforeSave hook unnecessary.

Using the PUT method with Express.js

I'm trying to implement update functionality to an Express.js app, and I'd like to use a PUT request to send the new data, but I keep getting errors using PUT. From everything I've read, it's just a matter of using app.put, but that isn't working. I've got the following in my routes file:
send = function(req, res) {
req.send(res.locals.content);
};
app.put('/api/:company', function(res,req) {
res.send('this is an update');
}, send);
When I use postman to make a PUT request, I get a "cannot PUT /api/petshop" as an error. I don't understand why I can't PUT, or what's going wrong.
You may be lacking the actual update function. You have the put path returning the result back to the client but missing the part when you tell the database to update the data.
If you're using MongoDB and ExpressJS, you could write something like this :
app.put('/api/:company', function (req, res) {
var company = req.company;
company = _.extend(company, req.body);
company.save(function(err) {
if (err) {
return res.send('/company', {
errors: err.errors,
company: company
});
} else {
res.jsonp(company);
}
})
});
This mean stack project may help you as it covers this CRUD functionality which I just used here swapping their articles for your companies. same same.
Your callback function has the arguments in the wrong order.
Change the order of callback to function(req, res).
Don't use function(res, req).
Also if you want to redirect in put or delete (to get adress), you can't use normal res.redirect('/path'), you should use res.redirect(303, '/path') instead. (source)
If not, you'll get Cannot PUT error.
Have you been checking out your headers information?
Because header should be header['content-type'] = 'application/json'; then only you will get the update object in server side (node-express), otherwise if you have content type plain 'text/htm' like that you will get empty req.body in your node app.

Categories

Resources