I'm working on application which is part of family apps. Every app works with the same token but with different settings, I have wrote a piece of code which allows checks configs before loading controller and page template, but the problem is that I have around 50 routes and I don't want to duplicate it for every route. So I wonder if angular has some interceptor allows resolve my check function before loading route and don't duplicate this piece of code
P.S. of course the simple way is write factory for this and add simple line of "resolve return factory" for every route, but I'm curious if there any other way?
Next is piece of my resolve function
page: 'Home Page',
templateUrl: Config.baseURL + '/views/welcome.html',
controller: 'welcome',
resolve: {
getConfig: function (lovServices) {
var config = App.storage('App_config');
if (config == null){
return lovServices.appConfig()
.then(function (response) {
App.storage('App_config', response);
})
} else {
return false
}
},
getPermissions: function (lovServices) {
var permissions = App.storage('App_access');
if (permissions == null){
return lovServices.permissions()
.then(function (response) {
function checkAvailability(arr, val) {
return arr.some(function(arrVal) {
return val === arrVal.allowed;
});
}
if (checkAvailability(response, true)){
var permissions = JSON.stringify(response);
App.storage('App_access', permissions);
} else {
localStorage.removeItem('App_token');
localStorage.removeItem('App_config');
localStorage.removeItem('App_access');
return false
}
});
}
}
}
})
So based on this answer Inside of my route provider before route definitions I have put this code:
function ($routeProvider, $locationProvider) {
var originalWhen = $routeProvider.when;
$routeProvider.when = function(path, route) {
route.resolve || (route.resolve = {});
angular.extend(route.resolve, {
getConfig: function (lovServices) {
var config = App.storage('App_config');
if (config == null && path !== '/login'){
return lovServices.AppConfig()
.then(function (response) {
App.storage('App_config', response);
})
} else {
return false
}
},
getPermissions: function (lovServices) {
var permissions = App.storage('App_access');
if (permissions == null && path !== '/login'){
return lovServices.permissions()
.then(function (response) {
function checkAvailability(arr, val) {
return arr.some(function(arrVal) {
return val === arrVal.allowed;
});
}
if (checkAvailability(response, true)){
var permissions = JSON.stringify(response);
App.storage('App_access', permissions);
} else {
localStorage.removeItem('App_token');
localStorage.removeItem('App_config');
localStorage.removeItem('App_access');
return false
}
});
}
}
});
return originalWhen.call($routeProvider, path, route);
};
$routeProvider.when('/', { ....
This code works as I expected.
Related
In my code, I define the following two routes:
$routeProvider.when('/problem/report', {
templateUrl: '/app/management/problem/reportAProblem.html',
controller: 'reportAProblemCtrl',
resolve: {
authorized: function($http, $location) {
var path = $location.path();
return $http.get('/svc/authorize/view?urlPath=' + path).then(function(response) {
var data = response.data;
if (response.data.result === 'NOT_AUTHORIZED') {
throw "NOT_AUTHORIZED";
}
return data;
})
}
}
});
$routeProvider.when('/problem', {
templateUrl: '/app/management/problem/problem.tmpl.html',
controller: 'problemCtrl',
resolve: {
authorized: ['$authorization', function($authorization) {
$authorization.authorize();
}]
}
});
The first case seems to work. However, the second case has had the function refactored into a Service, and AngularJS does not seem to be waiting for the Promise to resolve before displaying the page.
The refactored code looks like the following:
angular.module('authorization', [])
.factory('$authorization', ['$http', '$location',function($http, $location) {
var $authorization = {};
$authorization.authorize = function() {
var path = $location.path();
return $http.get('/svc/authorize/view?urlPath=' + path).then(function(response) {
var data = response.data;
if (response.data.result === 'NOT_AUTHORIZED') {
throw "NOT_AUTHORIZED";
}
return data;
});
}
return $authorization;
}]);
Can anyone tell me why the second case above doesn't wait for the promise to resolve before displaying the page?
Add return:
authorized: ['$authorization', function($authorization) { **return** $authorization.authorize(); }]
In my code, I define the following two routes:
$routeProvider.when('/problem/report', {
templateUrl: '/app/management/problem/reportAProblem.html',
controller: 'reportAProblemCtrl',
resolve: {
authorized: function($http, $location) {
var path = $location.path();
return $http.get('/svc/authorize/view?urlPath=' + path).then(function(response) {
var data = response.data;
if (response.data.result === 'NOT_AUTHORIZED') {
throw "NOT_AUTHORIZED";
}
return data;
})
}
}
});
$routeProvider.when('/problem', {
templateUrl: '/app/management/problem/problem.tmpl.html',
controller: 'problemCtrl',
resolve: {
authorized: ['$authorization', function($authorization) {
$authorization.authorize();
}]
}
});
The first case seems to work. However, the second case has had the function refactored into a Service, and AngularJS does not seem to be waiting for the Promise to resolve before displaying the page.
The refactored code looks like the following:
angular.module('authorization', [])
.factory('$authorization', ['$http', '$location',function($http, $location) {
var $authorization = {};
$authorization.authorize = function() {
var path = $location.path();
return $http.get('/svc/authorize/view?urlPath=' + path).then(function(response) {
var data = response.data;
if (response.data.result === 'NOT_AUTHORIZED') {
throw "NOT_AUTHORIZED";
}
return data;
});
}
return $authorization;
}]);
Can anyone tell me why the second case above doesn't wait for the promise to resolve before displaying the page?
Add return:
authorized: ['$authorization', function($authorization) { **return** $authorization.authorize(); }]
How do I prevent a state change for a specific "to" state in ui-router (is it using onEnter?) assuming I have this route:
.state('auth.confirm', {
url: '/confirm/:resetToken',
template: '<confirm-page></confirm-page>',
data: { pageTitle: 'Confirm Reset', specialClass: 'gray-bg' }
})
and this service with this promise-based function:
validateResetToken: function(resetToken) {
var self = this;
var deferred = $q.defer();
$http.post(AppConstants.hostRootUrl + '/auth/reset/validate', { resetToken: resetToken })
.then(function(response) {
if(response.data && response.data.success) {
// if we got a 200 return and it indicates success in the response, resolve
self.message = 'Success';
deferred.resolve(self.message);
}
else if (response.data && !response.data.success && response.data.error) {
// if we got a 200 return, but success is falsey and there's an error message, reject with that message
self.message = response.data.error;
deferred.reject(self.message);
}
else {
// error with generic message
self.message = 'Unknown response. Contact administrator.';
deferred.reject(self.message);
}
}, function(errPost) {
if (errPost.data && errPost.data.error) {
self.message = errPost.data.error;
deferred.reject(self.message);
}
else {
self.message = 'Could not connect.';
deferred.reject(self.message);
}
});
return deferred.promise;
},
For posterity (and Googlers) sake, Alon Eitan made me take a second look at my resolve approach, and I realized that my addition of the catch() was causing the rejected promise to not percolate up. This final code works:
.state('auth.confirm', {
url: '/confirm/:resetToken',
template: '<confirm-page></confirm-page>',
data: { pageTitle: 'Confirm Reset', specialClass: 'gray-bg' },
resolve: {
validated: function($q, $stateParams, AuthService, toastr) {
//$log.log('auth.confirm resolve $stateParams',$stateParams);
return AuthService.validateResetToken($stateParams.resetToken).catch(function(validateErr) {
toastr.error(validateErr, 'Blocked', {closeButton: true});
return $q.reject(validateErr);
});
}
}
})
You can create a rule as in https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-create-rules-to-prevent-access-to-a-state
Adapt their example:
app.config(function($stateProvider) {
$stateProvider.state('privatePage', {
data: {
rule: function(token) {
return validateResetToken(token)
}
});
});
app.run(function($rootScope, $state, $stateParams) {
$rootScope.$on('$stateChangeStart', function(e, to) {
if (!angular.isFunction(to.data.rule)) return;
var result = to.data.rule($stateParams.resetToken);
if (result && result.to) {
e.preventDefault();
// Optionally set option.notify to false if you don't want
// to retrigger another $stateChangeStart event
$state.go(result.to, result.params, {notify: false});
}
});
});
I am trying to implement role based authorization in beeman's loopback-angular-admin repo by using this https://github.com/Narzerus/angular-permission
so I am trying to change the users.config.js to like this -
(function () {
'use strict';
angular.module('com.module.users')
.run(function ($rootScope, gettextCatalog, Permission, AppAuth, User) {
$rootScope.addMenu(gettextCatalog.getString('Users'), 'app.users.list', 'fa-user');
Permission.defineRole('anonymous', function (stateParams) {
// If the returned value is *truthy* then the user has the role, otherwise they don't
return !AppAuth.hasOwnProperty('currentUser');
});
Permission.defineRole('student', function (stateParams) {
// If the returned value is *truthy* then the user has the role, otherwise they don't
AppAuth.ensureHasCurrentUser(function () {
//This call also serves to redirect a user to the login screen, via the interceptor in users.auth.js, if they are not authenticated.
console.log(User.getCurrent());
return !!(User.getCurrent().hasOwnProperty('role') && User.getCurrent().role == 'student');
});
}).defineRole('tutor', function (stateParams) {
// If the returned value is *truthy* then the user has the role, otherwise they don't
AppAuth.ensureHasCurrentUser(function () {
//This call also serves to redirect a user to the login screen, via the interceptor in users.auth.js, if they are not authenticated.
console.log(User.getCurrent());
return !!(User.getCurrent().hasOwnProperty('role') && User.getCurrent().role == 'tutor');
});
});
});
})();
So basically I am defining my roles with the help of AppAuth module that has the following code -
(function () {
'use strict';
/*jshint sub:true*/
/*jshint camelcase: false */
angular
.module('com.module.users')
.factory('AppAuth', function ($cookies, User, LoopBackAuth, $http) {
var self = {
login: function (data, cb) {
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId = null;
$http.post('/api/users/login?include=user', {
email: data.email,
password: data.password
})
.then(function (response) {
if (response.data && response.data.id) {
LoopBackAuth.currentUserId = response.data.userId;
LoopBackAuth.accessTokenId = response.data.id;
}
if (LoopBackAuth.currentUserId === null) {
delete $cookies['accessToken'];
LoopBackAuth.accessTokenId = null;
}
LoopBackAuth.save();
if (LoopBackAuth.currentUserId && response.data && response.data
.user) {
self.currentUser = response.data.user;
cb(self.currentUser);
} else {
cb({});
}
}, function () {
console.log('User.login() err', arguments);
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId =
null;
LoopBackAuth.save();
cb({});
});
},
logout: function (cb) {
//Destroy the access token.
User.logout({"access_token": LoopBackAuth.accessTokenId}, function () {
//Destory both cookies that get created.
delete $cookies["access_token"];
delete $cookies["accessToken"];
//Perform the Passport Logout
$http.post('/auth/logout');
});
self.currentUser = null;
cb();
},
ensureHasCurrentUser: function (cb) {
if ((!this.currentUser || this.currentUser.id === 'social') && $cookies.accessToken) {
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId = null;
$http.get('/auth/current')
.then(function (response) {
if (response.data.id) {
LoopBackAuth.currentUserId = response.data.id;
LoopBackAuth.accessTokenId = $cookies.accessToken.substring(
2, 66);
}
if (LoopBackAuth.currentUserId === null) {
delete $cookies['accessToken'];
LoopBackAuth.accessTokenId = null;
}
LoopBackAuth.save();
self.currentUser = response.data;
var profile = self.currentUser && self.currentUser.profiles &&
self.currentUser.profiles.length && self.currentUser.profiles[
0];
if (profile) {
self.currentUser.name = profile.profile.name;
}
cb(self.currentUser);
}, function () {
console.log('User.getCurrent() err', arguments);
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId =
null;
LoopBackAuth.save();
cb({});
});
} else {
if(self.currentUser){
console.log('Using cached current user.');
}
cb(self.currentUser);
}
}
};
return self;
});
})();
and then in my routes file I am doing -
$stateProvider
.state('router', {
url: '/router',
template: '<div class="lockscreen" style="height: 100%"></div>',
controller: 'RouteCtrl'
})
.state('error', {
url: '/error',
template: '<div class="text-center alert alert-danger" style="margin: 100px">An error occurred.</div>'
})
.state('app', {
abstract: true,
url: '/app',
templateUrl: 'modules/core/views/app.html',
controller: 'MainCtrl',
data: {
permissions: {
only: ['admin', 'student']
}
}
})
.state('app.home', {
url: '',
templateUrl: 'modules/core/views/home.html',
controller: 'HomeCtrl',
data: {
permissions: {
only: ['admin', 'student']
}
}
})
.state('tutor', {
abstract: true,
url: '/tutor',
templateUrl: 'modules/core/views/app.html',
controller: 'MainCtrl',
data: {
permissions: {
only: ['admin', 'tutor']
}
}
})
.state('tutor.home', {
url: '',
templateUrl: 'modules/core/views/home.html',
controller: 'HomeCtrl',
data: {
permissions: {
only: ['admin', 'tutor']
}
}
});
$urlRouterProvider.otherwise('/router');
but now I am getting the following error in the browser console -
Error: undefined role or invalid role validation
at Object.Permission._findMatchingRole (angular-permission.js:170)
at Object.Permission.resolveIfMatch (angular-permission.js:211)
at Object.Permission.authorize (angular-permission.js:239)
at angular-permission.js:45
at Scope.$broadcast (angular-scenario.js:24081)
at Object.transitionTo (angular-ui-router.js:3229)
at Array.<anonymous> (angular-ui-router.js:2346)
at Object.invoke (angular.js:4185)
at handleIfMatch (angular-ui-router.js:1836)
at angular-ui-router.js:1891
so how can I debug this error or implement client side role based authorization in my loopback app
[..] implement client side role based authorization in my loopback app
This doesn't mean anything, loopback is your server application.
Client-side authorization should be prohibited, since clients can be tampered with.
Authorization should take place server-side exclusively. Client-side you should just adapt the content in function of the role. That way if someone tampers with your client code at least its http requests to the API will fail and your data is safe
I have an AngularJS app which communicates with a Laravel PHP backend. Sending a GET request to /api/checkLogin will return { logged: false, username: undefined, id: undefined } if the user is not logged in, otherwise, it will return something like { logged: true, username: 'John', id: 123 }.
I am not too familiar with using AngularJS services, but I would like to set up a service called AuthService that can, well, perform my app's authentication services.
I would like to implement the following functions: AuthService.loggedIn, AuthService.isAdmin, AuthService.username, and AuthService.id.
I want these functions implemented in such a way that calling one will set the values for all the rest. For example, let's say I call AuthService.isAdmin. This function will check if isAdmin is set, if so, it will return the value of isAdmin. If isAdmin is not set, it will make an HTTP request to /api/checkLogin, set the values for loggedIn, isAdmin, username, and id, and then return the value of isAdmin. How can I accomplish this?
Here is the service I have tried putting together:
angular.module('myApp').factory('AuthService', ['$http', function($http) {
var loggedIn;
var isAdmin;
var username;
var id;
var checkLogin = function() {
if(loggedIn != undefined) {
return loggedIn
} else {
setUserData(checkLogin);
}
}
var checkAdmin = function() {
if(isAdmin != undefined) {
return isAdmin
} else {
setUserData(checkLogin);
}
}
var returnUsername = function() {
if(username != undefined) {
return username
} else {
setUserData(checkLogin);
}
}
var returnId = function() {
if(id != undefined) {
return id
} else {
setUserData(checkLogin);
}
}
// Our function call which will set our loggedIn, isAdmin, username, and id values
var setUserData = function(callback) {
$http.get(baseURL+'/api/checkLogin').success(function(data) {
loggedIn = data.logged;
if(loggedIn) {
isAdmin = data.is_admin;
username = data.username;
id = data.id;
}
callback();
});
}
return {
loggedIn: function() { return checkLogin(); },
isAdmin: function() { return checkAdmin(); },
username: function() { return returnUsername(); },
id: function() { return returnId(); },
}
}]);
It looks you want to use checkLogin as a callback, but instead of doing it the way you have it, return the promise back to checkLogin from setUserData. Then in checkLogin, create your own deferred to handle the results.
You are acting on asynchronous logic by introducing the $http call, so checkLogin is going to need to return a promise in all cases:
var checkLogin = function() {
// Create a custom deferred
var defer = $q.defer();
if(loggedIn != undefined) {
// Resolve your deferred with the value of logged in
defer.resolve(loggedIn);
} else {
setUserData().then(function (data) {
console.log('Set user data returned successfully');
loggedIn = data.logged;
if(loggedIn) {
isAdmin = data.is_admin;
username = data.username;
id = data.id;
defer.resolve(loggedIn);
} else {
defer.reject();
}
}, function () {
console.log('setUserData failed');
defer.reject();
});
}
return defer.promise;
}
var setUserData = function() {
return $http.get(baseURL+'/api/checkLogin');
}
AuthService.loggedIn() will now return a promise. You have to resolve the promise to get the value out of it:
AuthService.loggedIn().then(function (data) {
console.log(data);
});
The function passed to then above will be called when the promise is resolved with the value that the promise was resolved with. In this case, its the value of data.logged because thats what was passed to defer.resolve in your checkLogin function.
Here's some reading on Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
http://www.html5rocks.com/en/tutorials/es6/promises/
https://docs.angularjs.org/api/ng/service/$q
use this
loginService this
var app=angular.module('myApp');
app.factory('loginService', function ($http, $location) {
return {
login: function (data, scope) {
var $promise = $http.post('api.php/site/login', data);
$promise.then(function (msg) {
var uId = msg.data.key;
if (msg.data.key) {
$location.path('/abAdmin/home');
} else {
$location.path('/abAdmin');
}
});
},
logout: function () {
$http.post('api.php/site/logout');
$location.path('/abAdmin');
},
isLogged: function () {
var $check = $http.post('api.php/site/checkSession');
return $check;
}
}
});
and your app.js
var app=angular.module('myApp');
app.run(function ($rootScope, $location, loginService) {
var routPermission = [
'/abAdmin/home',
'/abAdmin/category',
'/abAdmin/category/:id'];
$rootScope.$on('$routeChangeStart', function (e, current) {
if ( routPermission.indexOf(current.$$route.originalPath) != -1) {
var connected = loginService.isLogged();
connected.then(function (data) {
if (!data.data.isLogged) {
logged=true;
$location.path('abAdmin');
}
})
}
});
});