I'm building an authentication system and I've done almost everything. I'm stuck in the authorization based on user role. If I use numbers, like user.role > 1, pass, if not, return false, I can make it work properly.
But I'm planning to move to an array based role, so I can create 'modules' and make a better control.
Auth process
It all starts with a return from the database, if the user/pass is ok, the DB will return a json array containing user name, id and roles, like this:
{
id: 1,
name: 'John Doe',
role: ['user', 'order', 'service']
}
In my $state, i have it defined like this:
.state('home', {
[...code...]
data: {
requireLogin: true,
role: ['user']
}
})
.state('sales', {
[...code...]
data: {
requireLogin: true,
role: ['sales', 'admin']
}
})
.state('order', {
[...code...]
data: {
requireLogin: true,
role: ['order', 'admin']
}
})
So for example, that user should be able to access the state 'order' and 'home', because at least one of the roles is in the .state data role. But I'm having trouble validating it inside my factory. I kind of made it, but sometimes it returns some other unxpected numbers, or at least i don't know the proper validation.
This is my factory:
//token = user data returned from data base;
//type = user role from state;
var _getTipo = function(token, type) {
var authUser = token.indexOf(type);
return authUser;
};
Sometimes i get the return -1, 10 or 2..
What i did wrong? Or whats the proper way to check if at least one of the user roles is in the state role?
Whenever I do user authentication I use a directive as an attribute like this:
app.directive('permissions', function (currentUser) {
return {
restrict: 'A',
priority: 100000,
scope: false,
link: function (scope, element, attr) {
var accessDenied = true;
var attribute = attr.permissions;
if (currentUser.role.indexOf(attribute) !== -1) {
accessDenied = false;
}
if (accessDenied) {
element.children().remove();
element.remove();
}
}
};
});
You would then call it like this:
<div permissions="user">
//info
</div>
You would just need to pass the currentUser object to the directive. Or you could just change your factory:
var _getTipo = function(token, type) {
var authenticated = false
for(var i = 0;i < token.role.length;i++){
var userRole = token.role[i];
for(var x = 0;x < type.length;x++){
var stateRole = type[x];
if (userRole == stateRole) {
authenticated = true;
}
}
}
return authenticated;
};
To turn this string: "['user', 'sales']" into an array:
JAVASCRIPT
var string = "['user', 'sales']";
var array = string.replace('"[',"").replace(']"',"").split("', '");
PHP
$string = "['user', 'sales']";
$string = str_replace('"[',"",$string);
$string = str_replace(']"',"",$string);
$array = explode("', '",$string);
Related
How to implement Role Based Access Control in Blur-Admin template for angularJS? Where to define roles? Which files are concerned?
Perfect and Working Solution! This solution basically provides restricted access to the roles allowed for that component.
define params in all of your main modules in this way - (for example) -
(function() {
'use strict';
angular.module('BlurAdmin.pages.components', [
'BlurAdmin.pages.components.mail',
// 'BlurAdmin.pages.components.timeline',
// 'BlurAdmin.pages.components.tree',
// 'BlurAdmin.pages.components.fileUpload',
])
.config(routeConfig);
/** #ngInject */
function routeConfig($stateProvider) {
$stateProvider
.state('main.components', {
url: '/components',
template: '<ui-view autoscroll="true" autoscroll-body-top></ui-view>',
abstract: true,
title: 'Components',
sidebarMeta: {
icon: 'ion-gear-a',
order: 100,
},
authenticate: true,
params: { // <-- focusing this one
authRoles: ['admin'] // <-- roles allowed for this module
}
});
}
})();
Modify baSidebar.service.js, add a new function getAuthorizedMenuItems just below getMenuItems (for easy understanding). And then just add a single parameter authRoles in defineMenuItemStates().
So, getAuthorizedMenuItems() will contain following code -
this.getAuthorizedMenuItems = function(user) {
var states = defineMenuItemStates();
var menuItems = states.filter(function(item) {
return item.level == 0 && _.includes(item.authRoles, user.role);
});
menuItems.forEach(function(item) {
var children = states.filter(function(child) {
return child.level == 1 && child.name.indexOf(item.name) === 0;
});
item.subMenu = children.length ? children : null;
});
return menuItems.concat(staticMenuItems);
};
And updated defineMenuItemStates() will be -
function defineMenuItemStates() {
return $state.get()
.filter(function(s) {
return s.sidebarMeta;
})
.map(function(s) {
var meta = s.sidebarMeta;
return {
name: s.name,
title: s.title,
level: ((s.name.match(/\./g) || []).length - 1),
order: meta.order,
icon: meta.icon,
stateRef: s.name,
authRoles: s.params ? s.params.authRoles : undefined // <-- added this
};
})
.sort(function(a, b) {
return (a.level - b.level) * 100 + a.order - b.order;
});
}
Now, it's time to use the newly added method getAuthorizedMenuItems in BaSidebarCtrl.js. Use it this way -
// FYI, I got userCreds in BaSidebarCtrl as following -
var userCreds = localStorage.getObject('dataUser');
// note that getMenuItems is just replaced with getAuthorizedMenuItems(userCreds)
// $scope.menuItems = baSidebarService.getMenuItems();
$scope.menuItems = baSidebarService.getAuthorizedMenuItems(userCreds);
So, your user object will look something like this -
var userCreds = {
userName: 'test#mail.com',
passWord: 'testpwd',
role: 'admin'
};
That's it!
I need a bit of guidance figuring out how to make two similar values in angular js from two different tables. The tables am using are a user table and a school table. So what I need to do is based on the logged in user I am supposed to get a school name related to that user but currently am not sure how to go about this. Any assistance in guiding me on how to go about this would be greatly appreciated.
School Service Controller Code:
angular.module('starter').factory('Schools',['apiUrl','$resource', 'UserInfo', function(apiUrl,$resource, UserInfo){
var factory = $resource(apiUrl + '/schools/:schoolId',
{
schoolId: '#_id'
}, {
update: {
method: 'GET'
}
});
return factory;
}]);
Profile Controller Code Snippet:
angular.module('starter.controllers')
.controller('ProfileController', function($scope,$http,$state,Schools, $ionicPopup, apiUrl, UserInfo,$ionicLoading,$ionicHistory,Users,Authentication) {
$scope.apiUrl = apiUrl;
$scope.authentication = Authentication;
$scope.state = $state;
$scope.selected = {};
var user = Authentication.user.school;
$scope.find = function(queryParams){
$scope.place = [];
var user = $scope.authentication.user.school;
var school = Schools.query({_id: user.school})
var params = queryParams || {};
params._id = user;
Schools.query(params, function(response){
if (params._id === school){
// response.splice(0, $scope.place.lengh);
// for(var x = 0; x< response.lengh; x++){
// $scope.place.push(response[x]);
// }
$scope.place = response[0].name;
}
else{
$scope.place = response;
}
})
}
$scope.signOut = function(){
$ionicPopup.confirm({
title: "SIGN OUT",
template: '<p align="center">' + 'PLEASE CONFIRM IF YOU WANT TO SIGN OUT'+ '</p>',
buttons: [
{ text: "NO",
type: 'button button-outline button-assertive',
onTap:function(e){
return false;
}
},
{
text: "YES",
type:'button-positive',
onTap:function(e){
//now you can clear history or goto another state if you need
$ionicHistory.clearHistory();
$ionicHistory.nextViewOptions({ disableBack: true, historyRoot: true });
$state.go('signin');
return true;
}
}
]
});
};
});
My value for "$scope.find function" is returned properly but is the whole list of school names and details rather than the one which matches the user.
I think this is server side logic. When you pass logged in user to your api then do some database query based on that user in the server side and return json object with all the list of schools .
I finally figured a way to get the correct values based on the id reference of school in the user table. I had to use the angularjs forEach method to first query the data then filter it in a if statement to get the results I was looking for.
Solution:
$scope.find = function(){
var school = $scope.authentication.user.school;
$scope.schoolName = Schools.query(function(results) {
results.forEach(function(result) {
if (result._id === school) {
$scope.schoolName = result.name;
return $scope.schoolName;
}
});
});
}
Currently I'm working with Blur Admin template and I have a case which sidebar menu should appear depends on user role.
After gave it a try I discover that if I comment $stateProvider.state(....); for example in app/pages/components/components.module.js so Components menu will disappear from sidebar menu.
So from here I think I can implement defining state depends on user role maybe something like this
if(user_role == "partner"){
$stateProvider.state(....);
}
So on in other pages module.
So far I have factory which check current user role which is get from localStorage
My problem is state definition located at .config() which only accept Provider.
So my question is How I can make provider to check current user role when I have factory to do it.
Here is app/pages/components/components.module.js
angular.module('BlurAdmin.pages.components', [
'BlurAdmin.pages.components.mail',
'BlurAdmin.pages.components.timeline',
'BlurAdmin.pages.components.tree',
])
.config(routeConfig);
// should .provider("CheckCurrentUserRole", CheckCurrentUserRole);
function CheckCurrentUserRole(){
///provider code here
}
/** #ngInject */
function routeConfig($stateProvider, /*CheckCurrentUserRoleProvider*/) {
//if(CheckcurrentUserRole.method() == "partner"){
$stateProvider
.state('components', {
url: '/components',
template : '<ui-view></ui-view>',
abstract: true,
title: 'Components',
sidebarMeta: {
icon: 'ion-gear-a',
order: 200,
},
});
//} so if current user is not partner then components sidebar menu doesn't appear.
}
Here is my factory to check user role
angular.module('BlurAdmin.pages')
.factory('authFactory', function (sessionFactory, $http) {
var authFactory = {};
...
authFactory.isPartner = function () {
if(sessionFactory.get('role') == "partner"){
return true;
};
};
authFactory.isCustomer = function () {
if(sessionFactory.get('role') == "customer"){
return true;
};
};
authFactory.isAdmin = function () {
if(sessionFactory.get('role') == "admin"){
return true;
};
};
authFactory.currentRole = function () {
return sessionFactory.get("role");
}
return authFactory;
})
.factory('sessionFactory', function () {
var sessionFactory = {};
...
sessionFactory.get = function (key){
return localStorage.getItem(key);
};
...
return sessionFactory;
});
The above is my logic as new in angular, if you have better approach or correction please let me know.
I am having difficulty getting my head around on how I could link my users contacts in Firebase with md-contacts-chips from https://material.angularjs.org/0.11.2/#/demo/material.components.chips
Basically, each registered user can add people they know via email to their contacts list. The users firebase structure is as follows:
firebase
-$uid1
contacts
$uid2 - userObject
$uid3 - userObject
-$uid2
contacts
$uid1 - userObject
$uid3 - userObject
-$uid3
contacts
$uid1 - userObject
$uid2 - userObject
etc..
Is it possible to ng-repeat a users contacts as an array of objects?
How should I configure the md-contacts-chip?
The example has a function called loadContacts() which has the contacts set.
How would I be able to set my user objects as contacts? The return object is contact and I would like to find a way for it to return the queried object.
function loadContacts() {
var contacts = [
'Marina Augustine',
'Oddr Sarno',
'Nick Giannopoulos',
'Narayana Garner',
'Anita Gros',
'Megan Smith',
'Tsvetko Metzger',
'Hector Simek',
'Some-guy withalongalastaname'
];
return contacts.map(function (c, index) {
var cParts = c.split(' ');
var contact = {
name: c,
email: cParts[0][0].toLowerCase() + '.' + cParts[1].toLowerCase() + '#example.com',
image: 'http://lorempixel.com/50/50/people?' + index
};
contact._lowername = contact.name.toLowerCase();
return contact;
});
}
Thanks
I'm by no means an expert, just a trial and error fanatic. That being said I did get this to work. I the issues is that "md-contact-chip" uses "push and splice" to adjust the array, and as firebase states that's a bad idea. If we had access to replace push with $add() or splice with $remove() this should work properly.
I was looking at the custom chips setup and it seems possible because you can call a function during the chip's add and remove, then with a custom chip template could maybe get the same look as the contact-chips.
Anyway here is what I did to get it working with md-contact-chips. Also I've adjusted this one to work with a list of items, not contacts, cause I wanted the picture for the items.
The key to it should be get your whole person obj, then set the ng-model="ctrl.person.contacts" inside the controller make sure to have an array created if person.contacts does not exist. "ctrl.person.contacts = ctrl.person.contacts || [];
Yes your are not properly updating the firebase object but you when you run
ctrl.person.$save() you are just completely updating the db.
Html
<div layout="column" ng-cloak>
<div>
<p>Items selected</p>
<pre>{{ctrl.item.installedItems}}</pre>
</div>
<input type="button" ng-click="ctrl.updateInstalledItems()" value='update'>
<md-content class="md-padding autocomplete" layout="column">
<md-contact-chips
ng-model="ctrl.item.installedItems"
md-contacts="ctrl.querySearch($query)"
md-contact-name="alseSn"
md-contact-image="image"
md-require-match="true"
md-highlight-flags="i"
filter-selected="ctrl.filterSelected"
placeholder="Select installed items">
</md-contact-chips>
</md-content>
</div>
Controller
app.controller('ItemChipCtrl', ['items', 'item', '$q', '$log',
function (items, item, $q, $log) {
var ctrl = this;
ctrl.items = items;
ctrl.item = item;
ctrl.item.installedItems = ctrl.item.installedItems || [];
ctrl.querySearch = querySearch;
ctrl.allItems = loadItems(ctrl.items);
ctrl.filterSelected = true;
ctrl.updateInstalledItems = function() {
$log.info('Update....')
$log.log(ctrl.installedItems);
ctrl.item.$save();
}
/**
* Search for contacts.
*/
function querySearch (query) {
var results = query ?
ctrl.allItems.filter(createFilterFor(query)) : [];
return results;
}
/**
* Create filter function for a query string
*/
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.alseSn.indexOf(lowercaseQuery) != -1);
};
}
function loadItems(items) {
/*var items = $scope.items */
return items.map(function (c, index) {
var item = {
alseSn: c.alseSn || c,
alseCard: c.alseCard,
installedOn: c.installedOn || null,
image: 'img/items/47/'+c.alseCard+'.jpg' || null
};
return item;
});
}
}
]);
route injections
.when('/settings/:alseSn', {
templateUrl: 'settings.html',
controller: 'ItemChipCtrl as ctrl',
resolve: {
auth: function($location, Auth){
return Auth.$requireAuth().then(function(auth) {
return auth;
},function(error){
$location.path('/login');
});
},
item: function($route, Items, Auth){
return Auth.$requireAuth().then(function(){
return Items.getItem($route.current.params.alseSn).$loaded();
});
},
items: function(Items, Auth){
return Auth.$requireAuth().then(function(){
return Items.all.$loaded();
});
}
}
})
.controller('CyclesController', function ($scope, $state) {
$scope.age = 0;
$scope.name = "";
$scope.email = "";
$scope.calculateByAge = function (age, name, email) {
$scope.data = $scope.data || {};
if (age > 0) {
$scope.data.age = age;
$scope.data.name = name;
$scope.data.email = email;
$state.go('tab.cycles-detail');
}
}
})
.controller('CyclesDetailController', function ($scope, $stateParams, CyclesService) {
console.log('scope data', $scope.data); // <--- undefined.
})
This may be a dumb question, but can get to get the data from the form on the CyclesDetailController controller.
If it's simple property you could do it by routing. Just change your "tab.cycles-detai' to 'tab.cycles-detai/:age' in your ui-router configuration and pass it when you're redirecting: $state.go('tab.cycles-detail', {age: age});
in 'CyclesDetailController' access it by $stateParams.age;
e.g:
//app.config
//...
.state('tab.cycles-detail', {
url: "^detail/{age:int}",
controller: 'CyclesDetailController',
templateUrl: "url_for_detail_template"
})
//...
// CyclesController
.controller('CyclesController', function ($scope, $state) {
$scope.age = 0;
$scope.calculateByAge = function (age) {
$scope.data = $scope.data || {};
if (age > 0) {
$scope.data.age = age;
$state.go('tab.cycles-detail', {age: age);
}
}
})
//CyclesDetailController
.controller('CyclesDetailController', function ($scope, $stateParams, CyclesService) {
console.log('scope data', $stateParams.age);
})
//
If you want to pass data from one route to another but dont want to expose it in browser menu bar, you can use squash.
Example -
.state('app.enroll', {
url: '/enroll/',
params: {
classId: {
value: null,
squash: true
},
className: {
value: null,
squash: true
}
},
title: 'Students Enrollment',
templateUrl: helper.basepath('enroll.html')
})
2nd Technique -
You can use localStorage / cookies to save data and retrieve at later stage.
3rd Technique -
You can always share data via services/factory in between controllers.