SailsJS Policy based route with a view - sails.js

I'm trying to use the routes.js to define a route to '/account'.
I want whoever is trying to access that path to go through the UserController and the checkLogin action and if the security check passes, then the user should be rendered with the defined view which is home/account
Here is my code:
routes.js:
'/account': {
controller: 'UserController',
action: 'checkLogin',
view: 'home/account'
}
policies.js:
UserController: {
'*': 'isAuthenticated',
'login': true,
'checkLogin': true
}
It let's me view /account without going through the isAuthenticated policy check for some reason.

There looks to be a little confusion here as to how policies, controllers and views work. As #bredikhin notes above, your controller will never be called because the route is being bound to a view. It's also important to note that policies cannot be bound to views, only to controllers. The correct setup should be something like:
In config/routes.js:
'/account': 'UserController.account'
In config/policies.js:
UserController: {
'*': 'isAuthenticated' // will run on all UserController actions
// or
'account': 'isAuthenticated' // will run just on account action
}
In api/policies/isAuthenticated.js:
module.exports = function(req, res, next) {
// Your auth code here, returning next() if auth passes, otherwise
// res.forbidden(), or throw error, or redirect, etc.
}
In api/controllers/UserController.js:
module.exports = {
account: function(req, res) {
res.view('home/account');
}
}

To put it short: either controller/action-style or view-style routing should be used within the same route in routes.js, not both simultaneously.
According to the router's source code, once there is a view property in a route object, binding stops, so basically Sails never knows to which controller your /account path should be routed, which means that your UserController-specific policy config never fires.
So, just remove the view property from the route, you can always specify the view path (if you want a non-standard one) with explicit rendering from within your action.

For statics work with policies, you can set your route with controller and action:
'GET /login': 'AuthController.index',
And set view/layout in your controller:
index: function (req, res) {
res.view('auth/login', { layout: 'path/layout' } );
},

Related

Non functioning default policy in SailsJS

I'm trying to implement the basic Passport integration in SailsJS. In my policies.js file, I have the default settings that every tutorial mentions.
'*': ['passport', 'sessionAuth'],
'auth': {
'*': ['passport']
}
My issue is that going to the main page localhost:1337/ doesn't seem to get passed through either policy. If I just set false there, everything still works. If I set false on the auth object for '*' though, I will get Forbidden on any /auth/* route. So, the policies seem to work, I just don't understand why the default catch-all doesn't. Thanks.
Do you use a controller or do you directly serve a view like in the sample homepage?
If you are serving the view directly with something similar to this:
// in config/routes.js
module.exports.routes = {
'/': {
view: 'homepage'
}
}
then you will have to modify it and use a controller in order to te able to use policies.
Create a route to a controller instead of a view:
// in config/routes.js
module.exports.routes = {
// Delete the previous definition and declare a route
// to a controller "index"
'get /': 'indexController.home'
}
Create the controller:
// in api/controllers/IndexController.js
module.exports = {
home: function (req, res) {
// Render the view located in "views/homepage.ejs"
res.view('homepage');
}
};
Then you will be able to manage the policies to apply to the controller index in the file config/policies.js.

in sails set controller as default route instead of view

in config/routes.js what happens when controller is needed instead of view
module.exports.routes = {
'/': {
view: 'index'
}
};
basically I want to load some data on the index page but I cant because there is no controller, in addition I want to have other pages like about, contact etc... but I prefer to put them to a PublicController instead of routes.js
If I get your question, may be you are looking for something this,
module.exports.routes = {
'/': {
controller: 'User',
action: 'actionName'
}
};
At first I didn't notice your comment. If you want to put all of those sections under different routes(like /about for about section) then there is no way to do it very simply.
But yes I have done it using React.js front end framework, where you can define routes in a single view file using React Routes. Defining in routes.js is not necessary. Rendering that single file from only one controller would enable you to use all those routes defined in that view file. Can't tell you any other way.
Hope it helps.
As your comments show you can specific the controller, action in your routes.
In your controllers you can specify the view to be rendered.
PublicController.js
module.exports = {
randomAction : function(req,res,next){
res.view('./randomActionViewFile');
}
}
Note this is unnecessary if the view file is already in the folder structure api/views/public/randaomActionFile.ext. Instead you can just use res.ok

How to ensure that a controllers action is only being called once in sails.js app ..?

I am trying to implement user authentication in my sails app.. But I am encountering a problem in different controllers that their action are being called twice.. I have checked from my browser and the request is only being sent once.. Here is an example..
// api/controllers/AuthController.js
...
logout: function (req, res) {
console.log("Loggin out");
req.logOut();
res.json({message: 'Logged out succesfully'});
},
...
Following is my config/routes.js file. (using get for many action just for sake of ease for testing api..)
module.exports.routes = {
// By default, your root route (aka home page) points to a view
// located at `views/home/index.ejs`
//
// (This would also work if you had a file at: `/views/home.ejs`)
'/': {
view: 'home/index'
},
// testing the api
'get /users/check' : 'UserController.test',
'get /login' : 'AuthController.process',
'get /logout' : 'AuthController.logout',
'get /signup': 'UserController.add',
'get /verify/username/:username?' : 'UserController.checkUsername',
'get /verify/email/:email?' : 'UserController.checkEmail',
// add friend
'get /:user?/addfriend': 'FriendController.addFriend',
// accept request
'get /:user?/friendrequest/:request?/accept': 'FriendController.acceptRequest',
};
I have applied the isAuthenticated policy on this action.. which is like
module.exports = function(req, res, next) {
if(req.isAuthenticated()) {
console.log("Valid User");
return next();
}
else {
console.log("User not logged in");
return res.json({error: "Please login"});
}
};
No whenever I call <myhost>/logout I get the following json back..
{
"error": "Please login"
}
and here is the output on the server..
Valid User
Loggin out
User not logged in
This means that my controller's action is being called twice.. and this is not the problem with only this controller. The UserController.add action has the same problem. I seem to be doing every thing fine but I don't know where this problem is coming from. Can any one suggest how can I debug it . Or what could be the root of the problem. As far as I have check..
Browser is not sending the request twice.
The Controller's action is being called twice and so are the middleware assosiated with it.
Oh i have the same Problem a few weeks ago.
Sails also call the middleware on static files (like your styles.css). Console.log the req-object than you see what your browser requested.
There a two Ways to handle this Problem:
1.) Try to set skipAssets: true in your route (see: http://beta.sailsjs.org/#/documentation/concepts/Routes/RouteTargetSyntax.html)
2.) In your policy add an if-condition to skip assets (like ".js", ".css" and so on).

How to create a simple landing page in MVC2

I'm trying to create a http://domain.com/NotAuthorized page.
went to Views\Shared and added a View called NotAuthorized witch originates the file name NotAuthorized.aspx
in my Routes I wrote
routes.MapRoute(
"NotAuthorized", // Route name
"NotAuthorized.aspx" // Route Url
);
but every time I access http://domain.com/NotAuthorized I get an error
The resource cannot be found.
What am I missing?
How can access this without using View("NotAuthorized") in the Controller, in other words, not passing through any controller.
You can't access views directly without passing through a controller. All pages in the Views folder cannot be served directly. So one way to accomplish what you are looking for is to write a custom[Authorize] attribute and set the error page:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
{
filterContext.Result = new ViewResult { ViewName = "NotAuthorized" };
}
}
I still have no idea on how to accomplish it, but what I did was use the Home Controller and create an Action called NotAuthorized
public ActionResult NotAuthorized()
{
return View();
}
And add a route like
routes.MapRoute(
"NotAuthorized", // Route name
"NotAuthorized", // URL with parameters
new { controller = "Home", action = "NotAuthorized" } // Parameter defaults
);
And works fine now, I can easily redirect in any part of my Business Logic to /Notauthorized and that will route fine.

Custom route doesn't find controller action

For some reason my application isn't routing to my controller method correctly. I have a routelink like this in my webpage -
<%= Html.RouteLink("View", "Blog", new { id=(item.BlogId), slug=(item.Slug) }) %>
In global.asax.cs I have the following routes -
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"MoreBlogs",
"Blog/Page/{page}",
new { controller = "Blog", action = "Index" }
);
routes.MapRoute(
"Blog",
"Blog/View/{id}/{slug}",
new { controller = "Blog", action = "View"}
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Blog", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
And then I have a class BlogController that has a method -
public ActionResult View(int id, string slug)
{
... etc.
}
I put a breakpoint in the first line of the View method but it's not getting hit at all. I checked with a route debugger for the format localhost/Blog/View/1/test and it matched my custom route. All I'm getting is a 404 while running this, I can't work out why the route won't post to the view method in my controller - any ideas?
(Assuming the route debugger was based on Phil Haack's debugger post)
If you're getting a 404, that makes me think the actual view page itself cannot be found.
Assuming that you are also using areas within the application, and that RouteLink is actually being called from within an Area, I think you may need to specify area="" in your routeValues object ( you also need to specify the controller). In general, I think that you will need to add the area="..." part with all your Routelink calls when using Areas.
This is something that I have picked up about RouteLink and Area, but cant seem to find any reference material detailing the limitations.