Reputation: 566
I am new to meanjs but I am currently trying to create a simple app where users can connect to each other.
Right now I just want to implement a function where users can look each other up by search and nothing else but I keep running into a 403 error. I tried to fix it by replicating the documents that make up the admin 'manage users' view.
I didn't want to give everyone the 'admin' role so I added a new policy for users where they are given only 'get' permissions. I also added a new route, view and controller for regular user that is modified from the admin files.
It has worked pretty well but I still can't get the system to allow access to the new userDirectory view when logged in under a non-admin role. I think it has something to do with the auth.interceptor.client.service.js file and the modules/core/client/app/init.js file and the $state being provided by the new files I made but I have tried to modify them and it still is throwing up the /forbidden view in the body of my app.
I am at a loss for what else I can do. Any help would be greatly appreciated.
The app starts with home.client.view.html view:
<section ng-controller="HomeController">
</div>
<div class="jumbotron text-center" ng-hide="authentication.user">
<div class="row">
<div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12">
<img alt="MEAN.JS" class="img-responsive text-center" src="modules/core/client/img/brand/Logo.png" />
</div>
</div>
<br>
<div class="row">
<p class="lead">
An app
</p>
</div>
<div class="row">
<p>
<!-- <a class="btn btn-primary btn-lg" href="http://meanjs.org" target="_blank">Learn more</a> -->
</p>
</div>
</div>
<div ng-if="authentication.user">
<h2>Congratulations! You are logged in.</h2>
<div ng-include="'/modules/users/client/views/user/user-directory.client.view.html'"></div>
<!-- <div ng-include="'/api/users'"></div> -->
</div>
</section>
Here is my view for the userDirectory (modified from modules/users/client/views/admin/list-users.client.view.html):
<section ng-controller="UserDirectoryController">
<div class="page-header">
<div class="row">
<div class="col-md-4">
<h1>Users</h1>
</div>
<div class="col-md-4" style="margin-top: 2em">
<input class="form-control col-md-4" type="text" ng-model="search" placeholder="Search" ng-change="figureOutItemsToDisplay()" />
</div>
</div>
</div>
<div class="list-group">
<a ng-repeat="user in pagedSearchItems" ui-sref="users.user({userId: user._id})" class="list-group-item">
<h4 class="list-group-item-heading" ng-bind="user.username"></h4>
<p class="list-group-item-text" ng-bind="user.email"></p>
</a>
</div>
<pagination boundary-links="true" max-size="8" items-per-page="itemsPerPage" total-items="filterLength" ng-model="currentPage" ng-change="pageChanged()"></pagination>
</section>
My Controller (modified from modules/users/client/controllers/admin/list-users.client.controller.js):
'use strict';
angular.module('users.user').controller('UserDirectoryController', ['$scope', '$filter', 'User',
function ($scope, $filter, User) {
User.query(function (data) {
$scope.users = data;
$scope.buildPager();
});
$scope.buildPager = function () {
$scope.pagedSearchItems = [];
$scope.itemsPerPage = 15;
$scope.currentPage = 1;
$scope.figureOutItemsToDisplay();
};
$scope.figureOutItemsToDisplay = function () {
$scope.filteredItems = $filter('filter')($scope.users, {
$: $scope.search
});
$scope.filterLength = $scope.filteredItems.length;
var begin = (($scope.currentPage - 1) * $scope.itemsPerPage);
var end = begin + $scope.itemsPerPage;
$scope.pagedSearchItems = $scope.filteredItems.slice(begin, end);
};
$scope.pageChanged = function () {
$scope.figureOutItemsToDisplay();
};
}
]);
My Service (modified from modules/users/client/services/users.client.service.js):
'use strict';
// Users service used for communicating with the users REST endpoint
angular.module('users').factory('User', ['$resource',
function ($resource) {
return $resource('api/userDirectory', {}, {
update: {
method: 'PUT'
}
});
}
]);
//TODO this should be Users service
angular.module('users.user').factory('User', ['$resource',
function ($resource) {
return $resource('api/userDirectory/:userId', {
userId: '@_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
angular.module('users.user').factory('User', ['$resource',
function ($resource) {
return $resource('api/userDirectory/:userId', {
userId: '@_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
My new route.js (modified from modules/users/server/routes/admin.server.routes.js):
'use strict';
/**
* Module dependencies.
*/
var userPolicy = require('../policies/userDirectory.server.policy'),
userDirectory = require('../controllers/userDirectory.server.controller');
module.exports = function (app) {
// User route registration first. Ref: #713
require('./users.server.routes.js')(app);
// Users collection routes
app.route('/api/userDirectory')
.get(userPolicy.isAllowed, userDirectory.list);
//Single user routes
app.route('/api/userDirectory/:userId')
.get(userPolicy.isAllowed, userDirectory.read);
// Finish by binding the user middleware
app.param('userId', userDirectory.userByID);
};
And finally my new policy for userDirectory (modified from modules/users/server/policies/admin.server.policy.js):
'use strict';
/**
* Module dependencies.
*/
var acl = require('acl');
// Using the memory backend
acl = new acl(new acl.memoryBackend());
/**
* Invoke User Permissions
*/
exports.invokeRolesPolicies = function () {
acl.allow([{
roles: ['user'],
allows: [{
resources: '/api/userDirectory',
permissions: ['post', 'get']
}, {
resources: '/api/userDirectory/:userId',
permissions: ['post', 'get']
}]
}]);
};
/**
* Check If User Policy Allows
*/
exports.isAllowed = function (req, res, next) {
var roles = (req.user) ? req.user.roles : ['guest'];
// Check for user roles
acl.areAnyRolesAllowed(roles, req.route.path, req.method.toLowerCase(), function (err, isAllowed) {
if (err) {
// An authorization error occurred.
return res.status(500).send('Unexpected authorization error');
} else {
if (isAllowed) {
// Access granted! Invoke next middleware
return next();
} else {
return res.status(403).json({
message: 'User is not authorized'
});
}
}
});
};
Upvotes: 0
Views: 173
Reputation: 1103
I think the easiest way to do it is to add a new route to the server which only users can access and create a service to retrieve that data.
(This is only a service for getting the data from the DB, you can use it in any view/controller, or create a new view which is only accessible to the users/admins)
In your module (client side) create a service to perform a http post/get to the server:
(function() {
'use strict';
angular
.module('yourModule')
.factory('UserListService', function($http) {
var routes = {};
routes.getUserList = function(username) {
return $http.post('/api/userlist', username); //you can do a 'post' to search for one username or just 'get' to see all users.
};
return routes;
}
);
})();
and then in modulses/yourModule/server/routes
add the route:
app.route('/api/userlist').all(yourModulePolicy.isAllowed)
.post(yourModule.getUserList); //get or post its up to you
don't forget to add permissions in modulses/yourModule/server/policies
roles: ['user'],
allows: [{
resources: '/api/userlist',
permissions: ['post'] //get or post
}]
then handle the request in modulses/yourModule/server/controllers
exports.getUserList = function(req, res) {
var name_to_search = req.body.username;
//here you can query the database for all users or the name you
//received in the post.
//NOTE: if you query the user DB there can be sensitive information
//such as the hashed password etc, make sure you delete that here
//before you respond with the user/user list
res.jsonp(user_list);
};
after this you can use the user information in the client side controller like this:
UserListService.getUserList({
username: 'john'
}).then(function(data) {
vm.userlist = data.data;
});
Make sure to inject UserListService into the controller and pass it to the controller function:
YourController.$inject = ['UserListService'];
and
function YourController(UserListService){...}
Hope this helps, i'm also new to the whole mean stack, don't even know if this is the best solution, but it will work.
EDIT:
When you create a new view on the client side with a controller you need to add it to the client side routes in modules/yourModule/client/config/yourModule.client.routes.js
.state('your.state', {
url: '-yourUrl',
templateUrl: 'modules/yourModule/client/views/user-list.client.view.html',
controller: 'YourController',
controllerAs: 'vm',
data: {
roles: ['user', 'admin'],
pageTitle: 'User List'
}
})
Upvotes: 1