Where to add role based authentication to MeanJS app? - javascript

I have a meanjs starter template (with yeoman generator).
Where can I add specific permissions to my modules? For instance,
'use strict';
// Configuring the Articles module
angular.module('adminpanel').run(['Menus',
function(Menus) {
// Set top bar menu items
//Menus.addMenuItem('topbar', 'admin panel', 'adminpanel/', 'adminpanel');
Menus.addMenuItem('topbar', 'Admin Panel', 'adminpanel', 'dropdown', '/buildings(/create)?');
Menus.addSubMenuItem('topbar', 'adminpanel', 'List Collections', 'adminpanel/collections');
}
]);
and the routes like so
'use strict';
//Setting up route
angular.module('adminpanel').config(['$stateProvider',
function($stateProvider) {
// Adminpanels state routing
$stateProvider.
state('listCollections', {
url: '/adminpanel/collections',
templateUrl: 'modules/adminpanels/views/list-collections.client.view.html'
}).
state('showCollection', {
url: '/adminpanel/collections/:collectionName',
templateUrl: 'modules/adminpanels/views/show-collection.client.view.html'
}).
state('showCollectionItem', {
url: '/adminpanel/collections/:collectionName/:itemId',
templateUrl: 'modules/adminpanels/views/show-item.client.view.html'
});
}
]);
Are these the correct places to add role-based authentication (on the client side), with added measure on the serverside (I've already done that)?
Does anybody know how I can add an option to the Menus.(some function), such as 'admin.hasPermission', without breaking it? Any resources on this sort of thing?
Thanks for the help!

I don't believe it is right practice to put your authentication, authorization code at the client side as well as server side. They should be on the server side only.
The point is, you have to replicate your authentication and authorization code in the client, anyone can read your mechanism to handle these situation and once a loophole is discovered, it would simply be followed by your server code as well.
I believe authentication and authorization logic should be restricted to server side only. If I am up against someone professional, it would at least make his task tougher.
In case you insist, you can create a wrapper around $http service, maintain a key value pair of what role can do what, and ensure all AJAX request go through your wrapper service where you can check whether it should be allowed. If yes, you can simply forward the request using $http and if not, throw an error.

Not sure about any previous version, but with version 0.4.0 there are parameter in the client config to control the visibility:
If you set isPublic: false and add a roles array you can set the user that can see the menu entry:
// Add the dropdown listCollentcions item
Menus.addSubMenuItem('topbar', 'adminpanel', {
title: 'listCollections',
isPublic: false,
roles:['admin'],
state: 'adminpanel.listCollections'
});
The implementation is in the core module (menu.client.services.js):
// A private function for rendering decision
var shouldRender = function(user) {
if (user) {
if (!!~this.roles.indexOf('*')) {
return true;
} else {
for (var userRoleIndex in user.roles) {
for (var roleIndex in this.roles) {
if (this.roles[roleIndex] === user.roles[userRoleIndex]) {
return true;
}
}
}
}
} else {
return this.isPublic;
}
return false;
};
Maybe you can give version 0.4.0 a try or have a look at the code and try to implement it urself.

Related

Angular ui routing and permissions

I am currently using ui.router for my page routing in my Angular application but it am starting to get a bit of issues with exploiting parts of my applications to users that don't have permissions to it.
I have tried to find documentation on this with no luck so fare so hope somebody in here have an approach for this.
In my example below i show that there are three pages in my application, but not all are allowed to go to the moderators page. If they do i will of course validate the permission server side and redirect them, but i feel that showing all possible pages in my application is a bit of exploiting it, can this in some way be limited with ui.router, and not by my auto generating the router file server side, or is that just something i need to live with :)
$stateProvider
.state('default', {
url: '/',
views: {
'mainViewContainer': {
templateUrl: 'pages/default.html'
}
}
})
.state('news', {
url: '/news',
views: {
'mainViewContainer': {
templateUrl: 'pages/news.html'
}
}
})
.state('moderators', {
url: '/moderators',
views: {
'mainViewContainer': {
templateUrl: 'pages/moderators.html'
}
}
});
Try adding using resolve - a list of things that are preconditions for a state.
.state(..., {
...
resolve: {
adminPermission: (AdminService) => {
return AdminService.validate();
}
}
...
})
Then, if AdminService returns a promise that fails, the app will not enter the state (and you can catch it using a $onStateChangeError event)
Edit:
How about writing the moderator state in an if clause i.e.
If(isAdmin)
stateProvider.state(...)

How to create a custom route in Sails JS

I'm trying to create a custom route in Sails and according to their documentation, if I add the following in config/routes.js:
'post /api/signin': 'AuthController.index',
The request would be dealt with by the index action in the AuthController but that doesn't seems to work at all. When I try the /api/login in Postman, I get nothing back.
Please note that I've added restPrefix: '/api' in my config/blueprints.js. Please note I'm using Sails 0.12.x
What am I missing here?
Since you are pointing to a controller with method index on it, you need to add it to your controllers and send a JSON response from there, (since you are using post). here is a simple example
config/routes.js
'post /api/signin': 'AuthController.index',
api/controllers/AuthController.js
module.exports = {
index: function(req, res) {
var id = req.param('id');
if(!id) {
return res.json(400, { error: 'invalid company name or token'});
}
/* validate login..*/
res.json(200, {data: "success"};
}
}
Update
Since you already have the above its probably caused by the blueprints you have.
Blueprint shortcut routes
Shortcut routes are activated by default in new Sails apps, and can be
turned off by setting sails.config.blueprints.shortcuts to false
typically in /config/blueprints.js.
Sails creates shortcut routes for any controller/model pair with the
same identity. Note that the same action is executed for similar
RESTful/shortcut routes. For example, the POST /user and GET
/user/create routes that Sails creates when it loads
api/controllers/UserController.js and api/models/User.js will
respond by running the same code (even if you override the blueprint
action)
with that being said from sails blueprint documentation, maybe turning off shortcuts and remove the prefix you've added.
possibly the shortcuts are looking elsewhere other than your controllers thus returning 404.
the prefix is being added to your blueprint connected route, hence you need /api/api/signin to access it?
Note
I am unable to replicate your issue on my computer as its working fine over here. but i have all blueprint settings turned off.
module.exports.blueprints = {
actions: false,
rest: false,
shortcuts: false,
// prefix: '',
pluralize: false,
populate: false,
autoWatch: false,
};

How to set a Angular config var to set server address?

How can I do some centralized solution to store strings on AngularJS? I've asked a similar question regarding NodeJS, but I don't know how to use the same approach to Angular as I've used to Node.
I've thought of 2 approaches:
1 - Setting a Angular Constant
app.constant('config',
{
serverUrl: 'http://localhost:8080'
});
This is my actual solution, but it comes with the downside that I need to update my code with the server address every time I send the code to the cloud.
2 - Using something like Node's process.env
And I have no idea how.
3 - Using something like require('./config.js')
I'm thinking on the Node like approach. Don't know how.
Well, is there a good, effective way of accomplishing this?
I do not think that there is one right answer. I prefer your first approach:
app.constant('config',
{
serverUrl: 'http://localhost:8080'
});
It is because I think about node.js backend and angular frontend as separete applications. For example I can add to node.js second version of api and connect other angular application (or event in another technology) to new api, but also use old angular app with previous api version.
In short in my opinion it is more flexible
Edit:
So, I need to update this line everytime I upload my come to Git
I don't add configuration file to repository. I create example config file which we add to repository. But actual config is ignored.
Edit2:
In one of mine project we use this module https://www.npmjs.com/package/gulp-ng-constant for build app.constant from config.json
This is how we switch the server url without rewriting it every time
var hostname = window.location.hostname;
if (hostname.indexOf("our.live.url") > -1) {
usedEnvironment = availableEnvironments.LIVE;
} else if (hostname.indexOf("localhost") > -1) {
usedEnvironment = availableEnvironments.LOCAL;
} else {
usedEnvironment = availableEnvironments.TEST;
}
switch (usedEnvironment) {
case availableEnvironments.LIVE:
angular.module('app')
.value('apiHost', 'https://our.live.backend.com');
angular.module('app')
.value('IntercomConfig', {
Enabled: true,
AppId: ''
});
break;
case availableEnvironments.TEST:
angular.module('app')
.value('apiHost', 'our.test.backend');
angular.module('app')
.value('IntercomConfig', {
Enabled: false,
});
break;
default:
angular.module('app')
.value('apiHost', 'http://localhost:8080');
angular.module('app')
.value('IntercomConfig', {
Enabled: false,
});
}

Sails.js create Index(root) Controller

I was wondering if there is a way to have an index controller with an index action. my root is a login page and I wanted to detect if the users session is already authenticated and if so redirect them to another page.
Is there specific notation for how the controller is named? I have already tried IndexController.js and MainController.js. I can't seem to find anything in the documentation about this.
Sails.js Ver: 0.11.0
You need to make the controller and action yourself. From there, set up a Policy to define access.
To make the controller, run sails generate controller Index in console.
Then, open api/controllers/IndexController.js, make it look something like this:
module.exports = {
index: function (req, res) {
// add code to display logged in view
}
};
Set up config/routes.js to look like this:
module.exports.routes = {
'get /': 'IndexController.index',
};
Afterwards, define a policy which has your authentication logic. Alternatively, you can use the included session authentication located at api/policies/sessionAuth.js assuming that your login action sets req.session.authenticated = true;. See the docs on policies for more info.
Lastly, connect the policy to the action in config/policies.js:
module.exports.policies = {
IndexController: {
'*': false, // set as default for IndexController actions
index: 'sessionAuth' // or the name of your custom policy
}
}

Calling the app config method inside ajax response - AngularJS

I am developing an app using angularjs and this is my first hands on using angular. Although, I have started understanding it and have developed some part of the app but I am stuck at one particular point.
I am trying to implement login functionality, so as the page loads, I am authenticating user and redirecting him to login page. On successful login, I am storing some values of user in one of the config provider.
Now I am using an API which has their own method of authentication and they have expose the ajax method which I can use to authenticate a user.
I have provided a snippet below. What I am primarily doing is using the external API, authenticating the user and once authenticated, I am getting roles associated to that user using another ajax method of the API, called "GetUserDetails".
And inside the response of the "GetUserDetails", I am injecting a provider and setting some values, so I can use this across my app.
The problem here is the app.config method is never called/executded. I mean the ajax request is returning response, and the alert is displayed on my page, but app.config is never executed.
But the same app.config if I call inside the done() of GetUser method, the app.config gets executed and stores values in my provider. But I want the GetuserDetails values also to be stored before I do anything in my app as I want to execute certain functionality based on user.
Below is my function in main.js file
function(angular,angularRoute,app,routes,configService){
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function() {
$.c.authentication.getUser()
.done(function(response){
if(response.userName!="anonymous"){
$.c.ajax({
method: "GetUserDetails",
parameters: {
User: response.user
}
})
.done(function(res) {
alert("I have reached the destination").
app.config(['configServiceProvider', function(configServiceProvider){
configServiceProvider.setLoginStatus(true);
configServiceProvider.setUserName(response.userName);
configServiceProvider.setUserObject(response);
configServiceProvider.setUserRoleDetails(res);
}]);
})
.fail(function(res) {
alert("Error while getting user roles ."+res);
});
angular.resumeBootstrap([app['name']]);
}
else
{
app.config(['configServiceProvider', function(configServiceProvider){
configServiceProvider.setLoginStatus(false);
configServiceProvider.setUserName(response.userName);
}]);
//Show Login Screen
var url = window.location.href.split("#")[0];
window.location.href = url + "#/Login";
angular.resumeBootstrap([app['name']]);
}
})
.fail(function(response){
$rootScope.isLoggedIn=false;
});
});
Here is my configServiceProvider
define(['../app'],function(app){
return app.provider('configService', function(){
var options={};
this.setLoginStatus = function(status){
//$rootScope.isLoggedIn = status;
options.isLoggedIn=status;
};
this.setPreLoginInfo=function(info){
options.preLoginInfo=info;
};
this.setUserName=function(name){
options.username=name;
}
this.setUserObject = function(userObject) {
options.userObject = userObject;
}
this.setUserRoleDetails = function(userRoleDetails) {
options.userRoleDetails = userRoleDetails;
}
this.$get=[function(){
if(!options){
}
return options;
}];
});
})
Can anyone please explain me what's going wrong here or what I am missing ?
Also, is there any alternative to achieve the same functionality ?
No luck in figuring out why the above scenario was not working. Since I had already spent lot of time behind this, I have found a workaround to achieve the same with the use of services.

Categories