user role on UI-STATE - angularjs

in my app i have many different USER ROLE's.
And on one state i need to set different roles. Bellow code, with only one user role, working ok
.state('distributor-dashboard', {
url: '/distributor-dashboard',
templateUrl: 'assets/html_templates/distributor-dashboard/distributor-dashboard.html',
controller: 'distributorDashboardCtrl',
authentication: {role: admin}
})
But, if i try to add another roles, they don' work here is example
.state('distributor-dashboard', {
url: '/distributor-dashboard',
templateUrl: 'assets/html_templates/distributor-dashboard/distributor-dashboard.html',
controller: 'distributorDashboardCtrl',
authentication: {role: ["admin" || "user"]}
})
Here is where i check USER ROLE
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
$rootScope.previousState = fromState.name;
var auth = authService.authentication.isAuth;
if (toState.name !== "login" && toState.name !== "signup" && toState.name !== "resset-password" && toState.name !== "new-password" && toState.name !== "account-activation" && auth === false) {
event.preventDefault();
$state.go('login', {});
}
var role = authService.authentication.role;
if (toState.authentication !== undefined && toState.authentication.role !== role) {
event.preventDefault();
$state.go('login', {});
}
}
Thnx.

I found a solution. It is necessary to set on STATE under authentification any role that we want to use.
authentication: {roles: ["admin", "user"]}
and then make a check if the roll there is in the allowed rolls
var userRole = authService.authentication.role;
if (toState.authentication !== undefined && toState.authentication.roles.indexOf(userRole) < 0) {
event.preventDefault();
$state.go('login', {});
}
I use indexOf for check, is roles in array. If someone have any question, feel free to ask

Related

how to get current url from UI Router - State?

By conditionally, I require to route my application to a new path. so I would like to know the current url of my application.
how can i get my current url from sate like toState.name?
here is my scenario:
.state('serialCreateCase', {
url: '/sn={serialNumber}', //sn=12345;
templateUrl:'app/createCase/create-case.html',
controller: 'createCaseController as ctrl',
data: {
requireLogin: false
}
here is the condition:
if(toState.name==="serialCreateCase"){ // i am getting proper result
console.log('toState.name', toState );
$rootScope.goToView("serialCreateCase"); //i am asking to go there. but not works! //sn=12345;
event.preventDefault();
}
Thanks in advance.
You don't need the url because you are using states
You should use this method
$state.go("serialCreateCase", {sn: 12345});
This will send the user to state serialCreateCase with the data
Key : sn
Value : 12345
EDIT
You code inside the controller should look like this
if (toState.name === "serialCreateCase") { // i am getting proper result
console.log('toState.name', toState);
$state.go("serialCreateCase", { sn: 12345 });
event.preventDefault();
}
Edited Code:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
var curentpath = fromState.url;
var split = curentpath.split(':');
var params = $stateParams.sn;
var mainUrl = split[0] +params;
console.log(mainUrl);
});
&& Route:
.state('serialCreateCase', {
url: '/sn={serialNumber}', //sn=12345;
templateUrl:'app/createCase/create-case.html',
controller: 'createCaseController as ctrl',
data: {
requireLogin: false
}

Resyncing $urlRouterProvider.deferIntercept(); for angular 1 ui router

In Angular 1.5 and angular ui router, I want to change the back button behaviour of my states to recognise the names of the states and not the params as I have a url /restaurant/1?param1=value&param2=value which dynamically changes without refreshing the page. I have the binding and url not changing when a property changes, but when I hit the back button it goes to the previous params state, not the saved state in $rootScope by name. I've debugged this for hours and my current solution only works sometimes, it's not consistent because the url sync isn't always called in time thus the state does not update when the url updates. I'm correctly identifying when the back button is hit but it's not refreshing the state. Right now, I have to use a location.assign and yet it only works some of the time. Is there anyway to resync the deferIntercept in angular ui router?
function bind(propGetters,
getUrl) {
let unwatch = this._$rootScope.$watchGroup(propGetters, () => {
let trimmedUrl = getUrl();
let remove = this._$rootScope.$on('$locationChangeSuccess', () => {
this._$rootScope.disableBackButton = false;
remove();
});
this._$rootScope.disableBackButton = true;
if (!this._$rootScope._hitBackButton) {
this._$rootScope.shouldSync = false;
this._$location.url(trimmedUrl);
}
});
return {
unbind(): void {
unwatch();
}
};
module.run(
($urlRouter, $rootScope) => {
$rootScope.shouldSync = true;
$rootScope.$on('$locationChangeSuccess', e => {
if ($rootScope.shouldSync) {
$urlRouter.sync();
} else if (!$rootScope._hitBackButton) {
e.preventDefault();
}
$rootScope.shouldSync = true;
});
$rootScope.$on("$stateChangeSuccess", (event, toState, toParams, fromState, fromParams) => {
if ($rootScope.previousState) {
if ($rootScope.previousState.name !== fromState.name && !$rootScope.disableBackButton) {
$rootScope.previousState = lodash.merge(fromState, { params: fromParams });
console.log($rootScope.previousState.name, 'previousState');
}
} else {
this._$rootScope.previousState = lodash.merge(fromState, { params: fromParams });
}
});
$rootScope.$on('$locationChangeSuccess', (evt, newUrl, oldUrl) => {
$rootScope.actualPrevious = oldUrl;
if ($rootScope._hitBackButton) {
this._urlProvider.sync();
$rootScope._hitBackButton = false;
}
});
$rootScope.$on('$locationChangeStart', (evt, newUrl, oldUrl) => {
if ($rootScope.actualPrevious === newUrl && $rootScope.previousState && !$rootScope.disableBackButton && !$rootScope._hitBackButton) {
$rootScope.shouldSync = true;
event.preventDefault();
console.log('hit back', $rootScope.previousState.name);
$rootScope._hitBackButton = true;
window.location.assign(this._urlService.getFullPath(this._$state.href($rootScope.previousState.name, $rootScope.previousState.params)))
// this._$state.go($rootScope.previousState.name, $rootScope.previousState.params, { reload: true }); - this doesn't seem to always work because of watch collisions?
}
});
$urlRouter.listen();
});
After playing around for 5 hours,
I found it's best to avoid using deferIntercept all together and instead reloadOnSearch: false like so
$stateProvider.state('app.restaurant', {
url: '/restaurant/{slug}?param1&param2&param3',
resolve: {
param1: function 1,
param2: function 2,
param3: function 3
},
reloadOnSearch: false,
params: {
slug: { value: null },
},
controller: 'restaurantController',
template:
});
Note you will have to use deferIntercept for slashes instead of params. I'd strongly advise using params if your url changes. The back button should work fine with my solution above minus the deferIntercept and plus the reloadOnSearch: false
Full modified implementation:
this._$rootScope.$on('$locationChangeStart', (evt, newUrl, oldUrl) => {
if (this._$rootScope.actualPrevious === newUrl && this._$rootScope.previousState && !this._$rootScope._hitBackButton) {
evt.preventDefault();
this._$rootScope._hitBackButton = true;
this._$state.go(this._$rootScope.previousState.name, this._$rootScope.previousState.params);
}
});
this._$rootScope.$on("$stateChangeSuccess", (event, toState, toParams, fromState, fromParams) => {
if (this._$rootScope.previousState) {
if (this._$rootScope.previousState.name !== fromState.name) {
this._$rootScope.previousState = lodash.merge(fromState, { params: fromParams });
}
} else {
this._$rootScope.previousState = lodash.merge(fromState, { params: fromParams });
}
});
this._$rootScope.$on('$locationChangeSuccess', (evt, newUrl, oldUrl) => {
this._$rootScope.actualPrevious = oldUrl;
if (this._$rootScope._hitBackButton) {
this._$rootScope._hitBackButton = false;
}
});

Infinite loop in State Change Events in angularjs

I want to change the state from "mycarts" state to "carts" state. so I used $stateChangeStart angularjs event in "mycarts" controller and set a condition if the state you are going from "mycarts" state is "carts" state so go to the carts state.
Here is my angular event in my controller :
$scope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.name === 'carts' && fromState.name === 'mycarts') {
$state.go('carts', fromParams, true);
event.preventDefault();
return;
}
if (toState.name === 'mycarts' && fromState.name === 'mycarts') {
//Nothing
} else {
$rootScope.selectedItemsMyCarts = undefined;
}
});
My code will be stuck in :
if (toState.name === 'carts' && fromState.name === 'mycarts') {
$state.go('carts', fromParams, true);
event.preventDefault();
return;
}
Here is my state in app.js
.state('mycarts', {
url : '/mycarts/:type/:vendor?vendors&price_to&price_from&manufacturer&editor&theme&page&order&filter_by&no',
params: {
type: {squash: true, value: null},
vendor: {squash: true, value: null}
},templateUrl : 'partials/mycarts.html',
controller : 'MyCartsController'
}).state('carts', {
url : '/carts/:id?vendors&price_to&price_from&manufacturer&editor&theme&page&order&no',
templateUrl : 'partials/carts.html',
controller : 'CartsController'
}
And I get angular.js:12416 RangeError: Maximum call stack size exceeded error.
why this happens? And what is the solution if I want to change a state due to a condition in angular event? And what kind of documents I can read to understand what exactly going on when events like this happends.
If he is already going to the carts state, why are you doing this? :
$state.go('carts', fromParams, true);

angularjs ui-router generates infinite loop

I am aware this question was discussed multiple times but the solutions didnt fit into my requirement. The quest is simple. If the user is not logged in the user should be redirected to login page. When i do it in $on, it generates infinite loop. Let me know what is the best solution.
var adminpanel = angular.module('administrator', ['ngMessages','ui.router','ui.bootstrap','ngCookies']);
adminpanel.config(function ($stateProvider, $urlRouterProvider) {
// $urlRouterProvider.otherwise("/login");
$urlRouterProvider.otherwise( function($injector, $location) {
var $state = $injector.get("$state");
$state.go("login");
});
$stateProvider
.state('login', {
url: "/login",
controller:"userCtrl",
templateUrl: "views/login.tpl.html",
permissions:{except:['admin']}
}
)
.state('menu', {
templateUrl: "views/menu.tpl.html",
controller:'adminCtrl',
permissions:{allow : ['admin']}
}
)
.state('menu.dashboard', {
url: "/dashboard",
templateUrl: "views/dashboard.tpl.html",
permissions:{allow : ['admin']}
}
)
});
adminpanel.run([ '$rootScope', '$state', '$stateParams','$cookieStore',function ($rootScope, $state, $stateParams,$cookieStore) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeStart',function(event, toState, fromState){
if($rootScope.session == undefined && $cookieStore.get('name') == undefined){$rootScope.session={}}
else if($rootScope.session == undefined && $cookieStore.get('name') != undefined){
$rootScope.session={set:true, name : $cookieStore.get('name'), userid :$cookieStore.get('userid'), role:$cookieStore.get('role')};
};
//Added below lines as update
if(toState.name === "login"){
return;
}
//Added above lines as update
var authorized = true;
if(Object.keys($rootScope.session).length === 0){
event.preventDefault();
$state.go('login');
}
else if(Object.keys(toState.permissions).length !=0){
console.log($rootScope.session.role);
angular.forEach(toState.permissions, function(value,key){
angular.forEach(value,function(role){
if(key === 'except' && role === $rootScope.session.role)
{authorized = false;}
else if(key === 'allow' && role !== $rootScope.session.role)
{authorized = false;};
});
});
}
if(!authorized){
event.preventDefault();
$state.go('menu.dashboard');
};
});
}]);
Thanks in advance for help.
Update 1 :
The solution works fine. But if the user is logged in, the user shall not access the login page if he tries to hit it through address bar. So i created a permission parameter with except key.
But if the user hits login page through address bar, the login page is generated, which should not and it should be redirected to menu.dashboard.
Hope i am clear.
The point here, is to avoid redirection, if it already happened, if already redirected:
...
// never redirect to state, if already going there
if(toState.name === "login"){
return;
}
// evaluate some IF here only if we go to other state then login
if(Object.keys($rootScope.session).length === 0) {
event.preventDefault();
$state.go('login');
return;
}
Changed the if loop and it worked as required. Below is the working code.
if(Object.keys($rootScope.session).length === 0){
if(toState.name === "login"){
return;
}
else{
event.preventDefault();
$state.go('login');
}
}
else if(Object.keys(toState.permissions).length !=0){
angular.forEach(toState.permissions, function(value,key){
angular.forEach(value,function(role){
if(key === 'except' && role === $rootScope.session.role)
{authorized = false;}
else if(key === 'allow' && role !== $rootScope.session.role)
{authorized = false;};
});
});
};
if(!authorized){
event.preventDefault();
$state.go('menu.dashboard');
};

Authorization Service fails on page refresh in angularjs

Implemented the authorization using the POST
The problem is when i go to a privileged page say '/admin' it works but when i refresh the page
manually, the admin page is redirecting to the '/unauthorized' page
Permissions service
angular.module('myApp')
.factory('PermissionsService', function ($rootScope,$http,CookieService) {
var permissionList;
return {
setPermissions: function(permissions) {
permissionList = permissions;
$rootScope.$broadcast('permissionsChanged')
},
getPermissions: function() {
var roleId = 5
if(CookieService.getLoginStatus())
var roleId = CookieService.getUserData().ROLE_ID;
return $http.post('api/user-permissions', roleId).then(function(result){
return result.data;
});
},
hasPermission: function (permission) {
permission = permission.trim();
return _.some(permissionList, function(item) {
if(_.isString(item.name))
return item.name.trim() === permission
});
}
};
});
hasPermissions directive
angular.module('myApp')
.directive('hasPermission', function(PermissionsService) {
return {
link: function(scope, element, attrs) {
if(!_.isString(attrs.hasPermission))
throw "hasPermission value must be a string";
var value = attrs.hasPermission.trim();
var notPermissionFlag = value[0] === '!';
if(notPermissionFlag) {
value = value.slice(1).trim();
}
function toggleVisibilityBasedOnPermission() {
var hasPermission = PermissionsService.hasPermission(value);
if(hasPermission && !notPermissionFlag || !hasPermission && notPermissionFlag)
element.show();
else
element.hide();
}
toggleVisibilityBasedOnPermission();
scope.$on('permissionsChanged', toggleVisibilityBasedOnPermission);
}
};
});
app.js
var myApp = angular.module('myApp',['ngRoute','ngCookies']);
myApp.config(function ($routeProvider,$httpProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/module/public/index.html',
header: 'app/partials/header.html',
footer: 'app/partials/footer.html'
})
.when('/login', {
templateUrl: 'app/module/login/login.html',
header: 'app/partials/header.html',
footer: 'app/partials/footer.html'
})
.when('/home', {
templateUrl: 'app/module/home/home.html',
header: 'app/partials/header.html',
footer: 'app/partials/footer.html'
})
.when('/register', {
templateUrl: 'app/module/register/register.html',
header: 'app/partials/header.html',
footer: 'app/partials/footer.html'
})
.when('/admin', {
templateUrl: 'app/module/admin/admin.html',
header: 'app/partials/header.html',
footer: 'app/partials/footer.html',
permission: 'admin'
})
.when('/unauthorized', {
templateUrl: 'app/partials/unauthorized.html',
header: 'app/partials/header.html',
footer: 'app/partials/footer.html'
})
.otherwise({redirectTo: '/'});
$httpProvider.responseInterceptors.push('securityInterceptor');
});
myApp.provider('securityInterceptor', function() {
this.$get = function($location, $q) {
return function(promise) {
return promise.then(null, function(response) {
if(response.status === 403 || response.status === 401) {
$location.path('/unauthorized');
}
return $q.reject(response);
});
};
};
});
myApp.run(function($rootScope, $location, $window, $route, $cookieStore, CookieService, PermissionsService) {
PermissionsService.getPermissions().then(function(permissionList){
PermissionsService.setPermissions(permissionList);
});
// Check login status on route change start
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
if(!CookieService.getLoginStatus() && $location.path() != '/register' && $location.path() != '/login') {
$location.path("/");
$rootScope.$broadcast('notloggedin');
}
if(CookieService.getLoginStatus() && $location.path() == '/login') {
$location.path("/home");
}
var permission = next.$$route.permission;
if(_.isString(permission) && !PermissionsService.hasPermission(permission))
$location.path('/unauthorized');
});
// Adds Header and Footer on route change success
$rootScope.$on('$routeChangeSuccess', function (ev, current, prev) {
$rootScope.flexyLayout = function(partialName) { return current.$$route[partialName] };
});
});
CookieService
angular.module('myApp')
.factory('CookieService', function ($rootScope,$http,$cookieStore) {
var cookie = {
data: {
login: false,
user: undefined
},
saveLoginData: function(user) {
cookie.data.login = true;
cookie.data.user = user;
$cookieStore.put('__iQngcon',cookie.data);
},
deleteLoginData: function() {
cookie.data.login = false;
cookie.data.user = undefined;
$cookieStore.put('__iQngcon',cookie.data);
},
getLoginStatus: function() {
if($cookieStore.get('__iQngcon') === undefined)
return cookie.data.login;
return $cookieStore.get('__iQngcon').login;
},
getUserData: function() {
return $cookieStore.get('__iQngcon').user;
}
};
return cookie;
});
It seems like the permissions data are lost on page refresh. Is there any other way i can solve the problem? Or is there any problem with the code??
when i refresh the page manually, the admin page is redirecting to the
'/unauthorized' page
Isn't that expected behavior? If you reload the page; then all UI state is lost; it is just like shutting down the app and starting from scratch.
It seems like the permissions data are lost on page refresh. Is there
any other way i can solve the problem? Or is there any problem with
the code??
If you want to be able to retain UI state after a page reload, you'll have to retain the Login information somehow, such as in a browser cookies. When the app loads; check for that cookie value. If it exists, you can load the user info from the database, essentially mirroring a login.
I'd be cautious about storing actual user credentials in a cookie without some type of encryption.
One approach I've used is to store a unique user key which can be sent to the DB to load user info. Sometimes this may be a UUID associated with the user, Avoid using an auto-incrementing primary key because that is easy to change to get access to a different user's account.
Well had to think for some time and made the following change for it to work. It may not be the best practice but ya worked for me. Would appreciate if anyone suggested me a better solution in the comments if found.
myApp.run(function($rootScope, $location, $window, $route, $cookieStore, CookieService, PermissionsService) {
var permChanged = false;
PermissionsService.getPermissions().then(function(permissionList){
PermissionsService.setPermissions(permissionList);
});
// Check login status on route change start
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
console.log('$routeChangeStart');
if(!CookieService.getLoginStatus() && $location.path() != '/register' && $location.path() != '/login') {
$location.path("/");
$rootScope.$broadcast('notloggedin');
}
if(CookieService.getLoginStatus() && $location.path() == '/login') {
$location.path("/home");
}
$rootScope.$on('permissionsChanged', function (ev, current, prev) {
permChanged = true;
});
if(CookieService.getLoginStatus() && permChanged) {
var permission = next.$$route.permission;
if(_.isString(permission) && !PermissionsService.hasPermission(permission))
$location.path('/unauthorized');
}
});
// Adds Header and Footer on route change success
$rootScope.$on('$routeChangeSuccess', function (ev, current, prev) {
$rootScope.flexyLayout = function(partialName) { return current.$$route[partialName] };
});
});
What i did was wait for the permissions to be set and then use the permissionChanged broadcast to set a permChanged variable to true and then combined with if user loggedin status and permchanged had to check the permissions if had
$rootScope.$on('permissionsChanged', function (ev, current, prev) {
permChanged = true;
});
if(CookieService.getLoginStatus() && permChanged) {
var permission = next.$$route.permission;
if(_.isString(permission) && !PermissionsService.hasPermission(permission))
$location.path('/unauthorized');
}

Resources