I'd like to create a new endpoint "/user/me" to call an action on my User controller called findMe(req, res). I'd like it to behave almost identically to the blueprint "/user/:id" which routes to findOne(req, res), except it will return the current user's data based on the logged in user's id (which is stored in req.session after login).
Is there an easy way to write findMe so it can pass the user ID to findOne and leave all the remaining request processing to the core sails findOne action?
I'm keen not to work with the findOne methods on User model directly but rather, use findOne on the User controller because it has added benefits, like honouring the ?polulate=... parameter, etc. That's said, tell me if this is a bad idea. I'm slightly concerned about the security implications - could s malicious user get to other users data via a forged /user/me request?
In other environments I've known this as server side redirects (different to client side redirects / 30x).
Thanks
Adam
You can totally access the blueprints from a controller. This is how you would do it if you store the user id inside the session as req.session.user.id:
// UserController.js
module.exports = {
me: function (req, res) {
req.params.id = req.session.user.id;
return sails.hooks.blueprints.middleware.findone(req, res);
}
};
However, this would not allow you to use other params such as populate.
I personally think you should implement your own action as it won't be really complicated and will be easier to secure the requests.
Related
I am building a blog-style app where a user can edit and delete his own posts. When he "deletes" his post, a property in the post object is changed from published: true to published: false . I am not actually removing the post from the database. Now, I sitll want to allow this post to be viewable by the owner of the post if he navigates directly to the post by typing in URL (he may have it bookmarked, too), but I DO NOT want it to be viewable by anyone else, obviously.
I thought maybe page middleware is what I need to use here, but I am unsure how to approach it. Do I somehow check the boolean value of the post's published property in the middleware? If so, how? I know how I can check the user state via something like context.store.state.user.uid , but how would I write the logic to check for the published property? Do I need to do a firestore query?
Here's an example of a blog post's URL schema (contains post id):
mysite.com/users/XmdmZMZkzkRdpZ4oLX5u6MYA9B62/posts/hello-world-title-here
Note: IF there's a better way to approach this rather than using Middleware (maybe I am overthinking this goal) , let me know! Thanks.
Obviously you need to fetch the post somewhere in order to know the published boolean.
A middleware is a good idea if you want to redirects the user going to a post he doesn't have access to (or show the error page).
That requires that you fetch the post from here, and that you have access to the current user from the request.
const validatorMiddleware = async ({ req, error, store }) => {
const post = await axios.get('mypost')
const user = req.user // can come from a previous middleware
if (post.author.id !== user.id) {
error() // Show error page
}
store.commit('addPost', post)
}
export default validatorMiddleware
This is a possibility, then it depends on how you fetch your data etc.
I want to proceed with more advanced queries in a sailsjs and mongodb stack where sailsjs is setup to serve as an api with data against a front end client. I've been able to fetch data with some basic queries but now looking into on how to proceed with more advanced ones. For example, I want to query the database for entries where the string match either the title or the text, something like this,
db.mycol.find({$or:[{"text":/.*test.*/},{"title": /.*test.*/}]})
My question, where do I put this logic? Any hints, links, tutorial that could point me into the right direction for this would be appreciated.
If you generate an api, for example:
sails generate api Customer
Sails will create a CustomerController for you in api>controllers.
You can add whatever custom endpoints you want to that.
If I put in CustomerController
blah: function(req, res) {
res.json(200, 'You are at blah');
}
and I navigate to customer/blah, it will hit that endpoint. So you can add whatever custom endpoints you want to for that controller. Go crazy.
Do not forget that sails has policy enforcement that you need to set up in the config/policies.js. This lets you expose, block, or add whatever middleware you need to in order to keep your back end as secure or open as it needs to be.
We are trying to move some of our app to use backbone and tastypie. I have the REST api set up and it is working on some basic examples. However, there are a few issues where currently we post an ajax request to a custom url, and in that view do a few things like
make related objects
call a few related functions
However, now that I've switched some of this functionality to using backbone and the REST api, I'm not sure where all of that should go!
For example, I had a view to make a Message, and when I made a Message, I also made a Notification and called a function to add some points to the user. Something like
def ajax_send_message(request):
## ... set up some variables ...
## Make the new message
message = Message(user=user, content=message)
message.save()
## Make the notification
notification = Notification(message=message)
notification.save()
## Give the user points
user.add_points_for_message();
return json_response({"status": "ok"})
Now--am I just supposed to do this all in JavaScript? I have a Message Backbone model as well.
// Create message backbone object
var msg = new Message({content:content, user: user});
// Post to server
msg.save();
// Add to backbone collection
messages.add(msg);
I've looked at different parts of tastypie, and it seems you can create custom URL endpoints, and also do validation, but that doesnt seem like the right spot to call related methods. It seems that calling related methods goes against the REST part of it---but then where are they supposed to go?
If I want to add some logic to backbone only when an object is created, where does that go?
The first thing I would suggest is to switch your mindset to an event-based model, where your code reacts to events. In your example above, you save the model to the server then immediately dd it to the collection. How do you even know that the model was saved correctly? That procedural style of programming works better in a synchronous, server-side style of programming.
In the asynchronous world of client-side programming, you make a request and then set up callbacks which determine what will happen next, depending on the events your are listening for. In your case, you want to react a certain way when the message is saved successfully, correct? You can define a success callback for your save operation like so:
msg.save({
success: function(model, response, options) {
messages.add(model);
// code to add notification
// code to add points
}
});
Basically, you are saying "I would like to save this model, and then listen for a success event. When the event comes in, execute the following code." Notice also that I am adding the model returned from the API to the collection, since this is the exact object that was persisted to the server so it's more appropriate to add than the model you created.
I'm successfully using Node.js + Express + Everyauth ( https://github.com/abelmartin/Express-And-Everyauth/blob/master/app.js ) to login to Facebook, Twitter, etc. from my application.
The problem I'm trying to wrap my head around is that Everyauth seems to be "configure and forget." I set up a single everyauth object and configure it to act as middleware for express, and then forget about it. For example, if I want to create a mobile Facebook login I do:
var app = express.createServer();
everyauth.facebook
.appId('AAAA')
.appSecret('BBBB')
.entryPath('/login/facebook')
.callbackPath('/callback/facebook')
.mobile(true); // mobile!
app.use(everyauth.middleware());
everyauth.helpExpress(app);
app.listen(8000);
Here's the problem:
Both mobile and non-mobile clients will connect to my server, and I don't know which is connecting until the connection is made. Even worse, I need to support multiple Facebook app IDs (and, again, I don't know which one I will want to use until the client connects and I partially parse the input). Because everyauth is a singleton which in configured once, I cannot see how to make these changes to the configuration based upon the request that is made.
What it seems like is that I need to create some sort of middleware which acts before the everyauth middleware to configure the everyauth object, such that everyauth subsequently uses the correct appId/appSecret/mobile parameters. I have no clue how to go about this...
Suggestions?
Here's the best idea I have so far, though it seems terrible:
Create an everyauth object for every possible configuration using a different entryPath for each...
Apparently I jumped the gun and wrote this before my morning cup of coffee, because I answered my own question, and it was quite easy to implement. Basically I just had to create my own custom express middleware to switch the everyauth configuration before the everyauth gets its grubby paws on the request, so...
var configureEveryauth = function()
{
return function configure(req, res, next) {
// make some changes to the everyauth object as needed....
next();
};
}
and now my setup becomes:
var app = express.createServer();
everyauth.facebook
.entryPath('/login/facebook')
.callbackPath('/callback/facebook');
app.use(configureEveryauth());
app.use(everyauth.middleware());
everyauth.helpExpress(app);
app.listen(8000);
Notice that I don't even bother fully configuring the everyauth Facebook object during the startup, since I know that the middleware will fill in the missing params.
There are plenty of related posts to what I'm asking, but after some lengthy searches couldn't quite find what I was looking for, my apologies if it exists somewhere.
My goal -- ALL requests to my Zend App must go through a preDispatch plugin, then pass to a custom Auth controller that will decide whether existing auth credentials are sufficient for the requested operation. 'Sufficient' depends on the logic of the app, hence why I want to do this at the controller+model level. If they suffice, they send the original request along to the specified controller+action, if not they default to a 'get lost' action.
At present I'm using an auth custom plugin set in the preDispatch to simply check for POST'ed auth credentials (if we are logging in), then in all cases the plugin stores the original request and redirects everyone (auth'd or not) to my auth controller, a-la:
$request->setModuleName('default')
->setControllerName('auth')
->setActionName('check')
->setParam('oreq',$request->getParams());
My problem/question is that within my auth->check action, how should I perform the redirect after a decision is made? If I use:
$this->_helper->redirector($or['action'], $oreq['controller']);
then I obviously get an infinite loop as these requests pass through the preDispatch plugin again. Sure I could pass something with the redirect so that the Auth plugin ignores such requests, but this is clearly a security hole. I had thought about maybe generating and storing an md5 hash, storing it to session and passing that as an escape param, but that seems a little sketchy.
Any better ideas out there? Perhaps a redirect method that does not go through the standard predispatch routine in Zend App? Thanks in advance!
This is not how it is done usually in Zend Framework. Not all requests go to a common place and gets redirected to the original requested place authentication.
For access control, use Zend_Acl. Through that, you could easily determine whether the current user has the necessary auth to access the content, else redirect to 'get lost' action.
If you are still adamant on using your technique, use _forward method instead of redirect method.
Since _forward is an internal redirect, you could pass additional arguments and check that in preDispath to avoid a loop.
$this->_forward($action, $controller, $module, $params)