From 81f95389539dcd74629a6cbbbd3a4aa2e5e7f059 Mon Sep 17 00:00:00 2001 From: Volodymyr Lavrynovych Date: Thu, 24 Aug 2017 01:18:17 +0300 Subject: [PATCH 1/3] #30: Fixed and covered by tests --- src/angular-spa-auth.js | 4 +++- test/public-methods/test.run.js | 1 - test/test.app.js | 32 ++++++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/angular-spa-auth.js b/src/angular-spa-auth.js index 3afa505..a6e4c25 100644 --- a/src/angular-spa-auth.js +++ b/src/angular-spa-auth.js @@ -187,7 +187,9 @@ config.handlers.success(user); }) .catch(function (err) { - openLogin(); + if(!service.isPublic($location.path())) { + openLogin(); + } return onError(err); }); } diff --git a/test/public-methods/test.run.js b/test/public-methods/test.run.js index 61f4dae..04149e9 100644 --- a/test/public-methods/test.run.js +++ b/test/public-methods/test.run.js @@ -109,7 +109,6 @@ describe('Public methods:', function () { //then: expect(success).not.toBeNull(); expect(success).toEqual(false); - expect($location.path()).toEqual(loginPage); }); it('No config provided', function () { diff --git a/test/test.app.js b/test/test.app.js index 159ae22..ac35f23 100644 --- a/test/test.app.js +++ b/test/test.app.js @@ -48,20 +48,22 @@ describe('angular-spa-auth', function () { expect($rootScope.currentUser).toBeUndefined(); watch($location); - //when: isAuthenticated method is triggered but we still do not know status of user + //when: isAuthenticated method is triggered $location.path('/public'); + $httpBackend.flush(1); //then: public page is always available expect($location.path()).toEqual('/public'); - expect($rootScope.currentUser).toBeUndefined(); + expect($rootScope.currentUser).toEqual(false); checkEvent(false, '/public'); - //when: isAuthenticated method is triggered but we still do not know status of user + //when: isAuthenticated method is triggered $location.path('/private'); //then: private page is rejected checkEvent(true, '/private'); - expect($rootScope.currentUser).toBeUndefined(); + expect($rootScope.currentUser).toEqual(false); + expect($location.path()).not.toEqual('/private'); //when: trigger after init $httpBackend.flush(); @@ -188,6 +190,28 @@ describe('angular-spa-auth', function () { expect($rootScope.currentUser).toEqual(USER); }); + it('User is not logged in on start, check home page', function () { + //given: + expect($rootScope.currentUser).toBeUndefined(); + watch($location); + + //when: isAuthenticated method is triggered but we still do not know status of user + $location.path(AuthService.config.uiRoutes.home); + + //then: public page is always available + expect($location.path()).toEqual(AuthService.config.uiRoutes.home); + expect($rootScope.currentUser).toBeUndefined(); + checkEvent(false, AuthService.config.uiRoutes.home); + + //when: trigger after init + $httpBackend.flush(); + + //then: we are on login page and user is set to false, because we checked his authentication status + expect($location.path()).toEqual(AuthService.config.uiRoutes.home); + expect($rootScope.currentUser).toEqual(false); + }); + + function watch($location) { events = []; $rootScope.$on("$routeChangeStart", function(e, next) { From 2a08e32b2fd7b2b40d8420d5bbc17dbc10f7b5ed Mon Sep 17 00:00:00 2001 From: Volodymyr Lavrynovych Date: Thu, 24 Aug 2017 01:21:14 +0300 Subject: [PATCH 2/3] Build dist --- dist/angular-spa-auth.js | 4 +++- dist/angular-spa-auth.min.js | 2 +- dist/angular-spa-auth.min.js.map | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dist/angular-spa-auth.js b/dist/angular-spa-auth.js index f9972ef..5fe8d37 100644 --- a/dist/angular-spa-auth.js +++ b/dist/angular-spa-auth.js @@ -187,7 +187,9 @@ config.handlers.success(user); }) .catch(function (err) { - openLogin(); + if(!service.isPublic($location.path())) { + openLogin(); + } return onError(err); }); } diff --git a/dist/angular-spa-auth.min.js b/dist/angular-spa-auth.min.js index daeba2c..711f39d 100644 --- a/dist/angular-spa-auth.min.js +++ b/dist/angular-spa-auth.min.js @@ -1,2 +1,2 @@ -"use strict";!function(){var t={UNAUTHORIZED_REDIRECT_TO_LOGIN:"Unauthorized: redirecting to the login page",MISSING_CURRENT_USER_ENDPOINT:"Endpoint for current user is not specified",MISSING_LOGIN_ENDPOINT:"Login endpoint is not specified",MISSING_LOGOUT_ENDPOINT:"Logout endpoint is not specified",SUCCESS_AUTH:"Successfully authenticated"};angular.module("angular-spa-auth",["ngRoute"]).run(["$rootScope","$location","AuthService",function(e,n,r){function o(){return void 0!==e.currentUser}r.saveTarget(),e.$on("$routeChangeStart",function(e,i){function u(){return i.$$route&&!r.isPublic(i.$$route.originalPath)}function c(){r._info("Stop loading: "+n.path()),e.preventDefault()}r._info("Start loading: "+n.path()),o()?r.isAuthenticated()?n.path()==r.config.uiRoutes.login&&(c(),r.openHome()):u()&&(c(),r._info(t.UNAUTHORIZED_REDIRECT_TO_LOGIN),r.openLogin()):u()&&(r._info("Unknown user status"),c())}),e.$on("$routeChangeSuccess",function(t,e){r._info("Loaded: "+n.path())})}]).service("AuthService",["$rootScope","$q","$http","$location","$route",function(e,n,r,o,i){function u(t){s(console.info,"AuthService: "+t)}function c(t){s(console.error,t)}function s(t,e){d.verbose&&t&&t(e)}function a(t){u((o.path()||"unknown")+" -----> "+t),t==d.uiRoutes.login&&U.isAuthenticated()?(u("User is already authenticated and cannot open login page: "+t),t=p(),o.path(t),u("Redirected to the "+t)):t==o.path()?(u("Reload: "+t),i.reload()):(o.path(t),u("Redirected to the "+t))}function l(){return d.endpoints.isAuthenticated?r.get(d.endpoints.isAuthenticated).then(function(t){var r=JSON.parse(t.data);return u("isAuthenticated: "+r),e.currentUser=t.data,r||n.reject(t.data)}):n.resolve(!0)}function g(){u("init"),l().then(U.refreshCurrentUser).then(function(t){d.handlers.success(t)})["catch"](function(t){return h(),f(t)})}function h(){a(d.uiRoutes.login)}function f(t){return c(t),d.handlers.error(t),n.reject(t)}function p(){return d.handlers.getHomePage(e.currentUser)}var d={verbose:!1,publicUrls:["/login","/home"],endpoints:{isAuthenticated:null,currentUser:null,logout:"/logout",login:"/login"},uiRoutes:{login:"/login",home:"/home",target:null},handlers:{getHomePage:function(t){return d.uiRoutes.home},getUser:function(){if(!d.endpoints.currentUser)throw new Error(t.MISSING_CURRENT_USER_ENDPOINT);return r.get(d.endpoints.currentUser).then(function(t){return u("Current user: "+JSON.stringify(t.data)),t.data})},login:function(e){if(!d.endpoints.login)throw new Error(t.MISSING_LOGIN_ENDPOINT);return r.post(d.endpoints.login,e)},logout:function(){if(!d.endpoints.logout)throw new Error(t.MISSING_LOGOUT_ENDPOINT);return r.get(d.endpoints.logout).then(function(){e.currentUser=null,h()})},success:function(e){u(t.SUCCESS_AUTH)},error:c}},U={config:d,_info:u,isPublic:function(t){return t?d.publicUrls.some(function(e){return e instanceof RegExp?t.match(e):"string"==typeof e?t&&e.startsWith(t):!1}):!1},saveTarget:function(){d.uiRoutes.target=o.path()||null,u("Target route is saved: "+d.uiRoutes.target)},openTarget:function(){var t=d.uiRoutes.target||p();u("Open target: "+t),a(t),U.clearTarget()},clearTarget:function(){d.uiRoutes.target=null},openLogin:h,openHome:function(){a(p())},getCurrentUser:function(){return e.currentUser?n.resolve(e.currentUser):U.refreshCurrentUser()},refreshCurrentUser:function(){return d.handlers.getUser().then(function(t){return e.currentUser=t,U.openTarget(),e.currentUser})},isAuthenticated:function(){return!!e.currentUser},logout:function(){return d.handlers.logout()},run:function(t){t&&(t.publicUrls&&(d.publicUrls=t.publicUrls),d=angular.merge(d,t)),g()},login:function(t){return d.handlers.login(t).then(U.refreshCurrentUser).then(d.handlers.success)["catch"](f)}};return U}])}(),String.prototype.startsWith||!function(){var t=function(){try{var t={},e=Object.defineProperty,n=e(t,t,t)&&e}catch(r){}return n}(),e={}.toString,n=function(t){if(null==this)throw TypeError();var n=String(this);if(t&&"[object RegExp]"==e.call(t))throw TypeError();var r=n.length,o=String(t),i=o.length,u=arguments.length>1?arguments[1]:void 0,c=u?Number(u):0;c!=c&&(c=0);var s=Math.min(Math.max(c,0),r);if(i+s>r)return!1;for(var a=-1;++a "+t),t==d.uiRoutes.login&&U.isAuthenticated()?(u("User is already authenticated and cannot open login page: "+t),t=p(),o.path(t),u("Redirected to the "+t)):t==o.path()?(u("Reload: "+t),i.reload()):(o.path(t),u("Redirected to the "+t))}function l(){return d.endpoints.isAuthenticated?r.get(d.endpoints.isAuthenticated).then(function(t){var r=JSON.parse(t.data);return u("isAuthenticated: "+r),e.currentUser=t.data,r||n.reject(t.data)}):n.resolve(!0)}function g(){u("init"),l().then(U.refreshCurrentUser).then(function(t){d.handlers.success(t)})["catch"](function(t){return U.isPublic(o.path())||h(),f(t)})}function h(){a(d.uiRoutes.login)}function f(t){return c(t),d.handlers.error(t),n.reject(t)}function p(){return d.handlers.getHomePage(e.currentUser)}var d={verbose:!1,publicUrls:["/login","/home"],endpoints:{isAuthenticated:null,currentUser:null,logout:"/logout",login:"/login"},uiRoutes:{login:"/login",home:"/home",target:null},handlers:{getHomePage:function(t){return d.uiRoutes.home},getUser:function(){if(!d.endpoints.currentUser)throw new Error(t.MISSING_CURRENT_USER_ENDPOINT);return r.get(d.endpoints.currentUser).then(function(t){return u("Current user: "+JSON.stringify(t.data)),t.data})},login:function(e){if(!d.endpoints.login)throw new Error(t.MISSING_LOGIN_ENDPOINT);return r.post(d.endpoints.login,e)},logout:function(){if(!d.endpoints.logout)throw new Error(t.MISSING_LOGOUT_ENDPOINT);return r.get(d.endpoints.logout).then(function(){e.currentUser=null,h()})},success:function(e){u(t.SUCCESS_AUTH)},error:c}},U={config:d,_info:u,isPublic:function(t){return t?d.publicUrls.some(function(e){return e instanceof RegExp?t.match(e):"string"==typeof e?t&&e.startsWith(t):!1}):!1},saveTarget:function(){d.uiRoutes.target=o.path()||null,u("Target route is saved: "+d.uiRoutes.target)},openTarget:function(){var t=d.uiRoutes.target||p();u("Open target: "+t),a(t),U.clearTarget()},clearTarget:function(){d.uiRoutes.target=null},openLogin:h,openHome:function(){a(p())},getCurrentUser:function(){return e.currentUser?n.resolve(e.currentUser):U.refreshCurrentUser()},refreshCurrentUser:function(){return d.handlers.getUser().then(function(t){return e.currentUser=t,U.openTarget(),e.currentUser})},isAuthenticated:function(){return!!e.currentUser},logout:function(){return d.handlers.logout()},run:function(t){t&&(t.publicUrls&&(d.publicUrls=t.publicUrls),d=angular.merge(d,t)),g()},login:function(t){return d.handlers.login(t).then(U.refreshCurrentUser).then(d.handlers.success)["catch"](f)}};return U}])}(),String.prototype.startsWith||!function(){var t=function(){try{var t={},e=Object.defineProperty,n=e(t,t,t)&&e}catch(r){}return n}(),e={}.toString,n=function(t){if(null==this)throw TypeError();var n=String(this);if(t&&"[object RegExp]"==e.call(t))throw TypeError();var r=n.length,o=String(t),i=o.length,u=arguments.length>1?arguments[1]:void 0,c=u?Number(u):0;c!=c&&(c=0);var s=Math.min(Math.max(c,0),r);if(i+s>r)return!1;for(var a=-1;++a ' + route);\n if(route == config.uiRoutes.login && service.isAuthenticated()) {\n info('User is already authenticated and cannot open login page: ' + route);\n route = getHome();\n $location.path(route);\n info('Redirected to the ' + route);\n } else if(route == $location.path()) {\n info('Reload: ' + route);\n $route.reload()\n } else {\n $location.path(route);\n info('Redirected to the ' + route);\n }\n }\n\n function isAuthenticated() {\n if (!config.endpoints.isAuthenticated) {\n return $q.resolve(true);\n }\n\n return $http.get(config.endpoints.isAuthenticated).then(function (response) {\n var isAuth = JSON.parse(response.data);\n info('isAuthenticated: ' + isAuth);\n $rootScope.currentUser = response.data;\n return isAuth || $q.reject(response.data);\n });\n }\n\n function init() {\n info('init');\n isAuthenticated()\n .then(service.refreshCurrentUser)\n .then(function (user) {\n config.handlers.success(user);\n })\n .catch(function (err) {\n openLogin();\n return onError(err);\n });\n }\n\n function openLogin() {\n goTo(config.uiRoutes.login);\n }\n\n function onError(err) {\n error(err);\n config.handlers.error(err);\n return $q.reject(err);\n }\n\n function getHome() {\n return config.handlers.getHomePage($rootScope.currentUser);\n }\n\n // ------------------------------------------------------------------------/// Public\n var service = {\n config: config,\n _info: info,\n\n /**\n * Returns true if provide route url is in the list of public urls\n * @param {String} path route path that should be checked\n * @returns {boolean} true if url is in the list of public urls\n */\n isPublic: function (path) {\n if(!path) {\n return false;\n }\n\n return config.publicUrls.some(function (pattern) {\n if(pattern instanceof RegExp) {\n return path.match(pattern);\n } else if(typeof pattern == \"string\") {\n return path && pattern.startsWith(path);\n } else {\n return false;\n }\n });\n },\n /**\n * Saves current route as a target route\n */\n saveTarget: function () {\n config.uiRoutes.target = $location.path() || null;\n info('Target route is saved: ' + config.uiRoutes.target);\n },\n /**\n * Redirects user to the saved target route if exists or to the home page\n */\n openTarget: function () {\n var target = config.uiRoutes.target || getHome();\n info('Open target: ' + target);\n goTo(target);\n service.clearTarget()\n },\n /**\n * Clears saved target route\n */\n clearTarget: function () {\n config.uiRoutes.target = null;\n },\n /**\n * Redirects user to the login page\n */\n openLogin: openLogin,\n /**\n * Redirects user to the home page\n */\n openHome: function () {\n goTo(getHome());\n },\n /**\n * Returns saved current user or load it from backed\n * Always returns {Promise}\n * @returns {Promise}\n */\n getCurrentUser: function () {\n return $rootScope.currentUser ? $q.resolve($rootScope.currentUser) : service.refreshCurrentUser();\n },\n /**\n * Loads user from backed using currentUser endpoint or getUser handler\n * Always returns {Promise}\n * @returns {Promise}\n */\n refreshCurrentUser: function() {\n return config.handlers.getUser().then(function (user) {\n $rootScope.currentUser = user;\n service.openTarget();\n return $rootScope.currentUser;\n })\n },\n /**\n * Returns true if user is authenticated\n * Warning! It does not check backend.\n * @returns {boolean} true if user is authenticated\n */\n isAuthenticated: function () {\n return !!$rootScope.currentUser;\n },\n /**\n * Logs user out from the system and redirects it to the login page\n */\n logout: function () {\n return config.handlers.logout();\n },\n /**\n * Allows you to configure angular-spa-auth module and start the init process.\n * Should be called in the #run method of you module\n * @param {Object} configuration contains all the configs\n * @param {String=} configuration.verbose activates console.info output if true\n * @param {String[]=} configuration.publicUrls list url that are available for unauthorized users\n * @param {Object=} configuration.endpoints gives you ability to setup all the backed endpoints that will own roles in the authentication process\n * @param {Object=} configuration.uiRoutes helps you automatically redirect user to the specified UI routes such as home and login\n * @param {String=} configuration.uiRoutes.home home route\n * @param {String=} configuration.uiRoutes.login login route\n * @param {Object=} configuration.handlers allows you to provide you implementation for key methods of authentication process\n */\n run: function (configuration) {\n if (configuration) {\n if(configuration.publicUrls) {\n //publicUrls should be completely replaced by new value if provided\n //to provide ability to set new array with only one route\n config.publicUrls = configuration.publicUrls;\n }\n\n config = angular.merge(config, configuration);\n }\n init()\n },\n /**\n * Login user using provided credentials\n * @param {Object} credentials object with any type of information that is needed to compelete authentication process\n */\n login: function (credentials) {\n return config.handlers.login(credentials)\n .then(service.refreshCurrentUser)\n .then(config.handlers.success)\n .catch(onError);\n }\n };\n\n return service\n }]);\n})();","/*! http://mths.be/startswith v0.2.0 by @mathias */\nif (!String.prototype.startsWith) {\n\t(function() {\n\t\t'use strict'; // needed to support `apply`/`call` with `undefined`/`null`\n\t\tvar defineProperty = (function() {\n\t\t\t// IE 8 only supports `Object.defineProperty` on DOM elements\n\t\t\ttry {\n\t\t\t\tvar object = {};\n\t\t\t\tvar $defineProperty = Object.defineProperty;\n\t\t\t\tvar result = $defineProperty(object, object, object) && $defineProperty;\n\t\t\t} catch(error) {}\n\t\t\treturn result;\n\t\t}());\n\t\tvar toString = {}.toString;\n\t\tvar startsWith = function(search) {\n\t\t\tif (this == null) {\n\t\t\t\tthrow TypeError();\n\t\t\t}\n\t\t\tvar string = String(this);\n\t\t\tif (search && toString.call(search) == '[object RegExp]') {\n\t\t\t\tthrow TypeError();\n\t\t\t}\n\t\t\tvar stringLength = string.length;\n\t\t\tvar searchString = String(search);\n\t\t\tvar searchLength = searchString.length;\n\t\t\tvar position = arguments.length > 1 ? arguments[1] : undefined;\n\t\t\t// `ToInteger`\n\t\t\tvar pos = position ? Number(position) : 0;\n\t\t\tif (pos != pos) { // better `isNaN`\n\t\t\t\tpos = 0;\n\t\t\t}\n\t\t\tvar start = Math.min(Math.max(pos, 0), stringLength);\n\t\t\t// Avoid the `indexOf` call if no match is possible\n\t\t\tif (searchLength + start > stringLength) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar index = -1;\n\t\t\twhile (++index < searchLength) {\n\t\t\t\tif (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\t\tif (defineProperty) {\n\t\t\tdefineProperty(String.prototype, 'startsWith', {\n\t\t\t\t'value': startsWith,\n\t\t\t\t'configurable': true,\n\t\t\t\t'writable': true\n\t\t\t});\n\t\t} else {\n\t\t\tString.prototype.startsWith = startsWith;\n\t\t}\n\t}());\n}\n"]} \ No newline at end of file +{"version":3,"sources":["../src/angular-spa-auth.js","../node_modules/string.prototype.startswith/startswith.js"],"names":["MESSAGES","UNAUTHORIZED_REDIRECT_TO_LOGIN","MISSING_CURRENT_USER_ENDPOINT","MISSING_LOGIN_ENDPOINT","MISSING_LOGOUT_ENDPOINT","SUCCESS_AUTH","angular","module","run","$rootScope","$location","AuthService","isKnownStatus","undefined","currentUser","saveTarget","$on","event","next","isPrivate","$$route","isPublic","originalPath","stop","_info","path","preventDefault","isAuthenticated","config","uiRoutes","login","openHome","openLogin","service","$q","$http","$route","info","message","_log","console","error","err","fn","msg","verbose","goTo","route","getHome","reload","endpoints","get","then","response","isAuth","JSON","parse","data","reject","resolve","init","refreshCurrentUser","user","handlers","success","onError","getHomePage","publicUrls","logout","home","target","getUser","Error","stringify","credentials","post","some","pattern","RegExp","match","startsWith","openTarget","clearTarget","getCurrentUser","configuration","merge","String","prototype","defineProperty","object","$defineProperty","Object","result","toString","search","this","TypeError","string","call","stringLength","length","searchString","searchLength","position","arguments","pos","Number","start","Math","min","max","index","charCodeAt","value","configurable","writable"],"mappings":"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","file":"angular-spa-auth.min.js","sourcesContent":["'use strict';\n(function () {\n\n var MESSAGES = {\n UNAUTHORIZED_REDIRECT_TO_LOGIN: 'Unauthorized: redirecting to the login page',\n MISSING_CURRENT_USER_ENDPOINT: 'Endpoint for current user is not specified',\n MISSING_LOGIN_ENDPOINT: 'Login endpoint is not specified',\n MISSING_LOGOUT_ENDPOINT: 'Logout endpoint is not specified',\n SUCCESS_AUTH: 'Successfully authenticated'\n };\n\n angular.module('angular-spa-auth', ['ngRoute'])\n .run(['$rootScope', '$location', 'AuthService', function ($rootScope, $location, AuthService) {\n AuthService.saveTarget();\n $rootScope.$on('$routeChangeStart', function (event, next) {\n AuthService._info('Start loading: ' + $location.path());\n\n if(isKnownStatus()) {\n if (AuthService.isAuthenticated()) {\n if($location.path() == AuthService.config.uiRoutes.login) {\n stop();\n AuthService.openHome();\n }\n } else if (isPrivate()) {\n stop();\n AuthService._info(MESSAGES.UNAUTHORIZED_REDIRECT_TO_LOGIN);\n AuthService.openLogin();\n }\n } else if (isPrivate()) {\n AuthService._info('Unknown user status');\n stop();\n }\n\n function isPrivate() {\n return next.$$route && !AuthService.isPublic(next.$$route.originalPath)\n }\n\n function stop() {\n AuthService._info('Stop loading: ' + $location.path());\n event.preventDefault();\n }\n });\n\n $rootScope.$on('$routeChangeSuccess', function (event, next) {\n AuthService._info('Loaded: ' + $location.path());\n });\n\n function isKnownStatus() {\n return $rootScope.currentUser !== undefined;\n }\n }])\n .service('AuthService', ['$rootScope', '$q', '$http', '$location', '$route', function ($rootScope, $q, $http, $location, $route) {\n\n // ------------------------------------------------------------------------/// Config\n var config = {\n verbose: false,\n publicUrls: ['/login', '/home'],\n endpoints: {\n isAuthenticated: null,\n currentUser: null,\n logout: '/logout',\n login: '/login'\n },\n uiRoutes: {\n login: '/login',\n home: '/home',\n target: null\n },\n handlers: {\n /**\n * Returns url of home page as a string\n * @param {Object} user authenticated user\n * @returns {string} url to the default/home page\n */\n getHomePage: function(user) {\n return config.uiRoutes.home;\n },\n\n /**\n * Returns promise of GET request which should get current user from backend\n * @returns {Promise}\n */\n getUser: function () {\n if(!config.endpoints.currentUser) {\n throw new Error(MESSAGES.MISSING_CURRENT_USER_ENDPOINT)\n }\n\n return $http.get(config.endpoints.currentUser).then(function (response) {\n info('Current user: ' + JSON.stringify(response.data));\n return response.data\n })\n },\n\n /**\n * Tries to login user using provided credentials.\n * Sends GET request\n *\n * @param {Object} credentials object with user credentials\n * @param {String} [credentials.login]\n * @param {String} [credentials.password]\n * @returns {Promise}\n */\n login: function (credentials) {\n if(!config.endpoints.login) {\n throw new Error(MESSAGES.MISSING_LOGIN_ENDPOINT)\n }\n\n return $http.post(config.endpoints.login, credentials);\n },\n\n logout: function () {\n if(!config.endpoints.logout) {\n throw new Error(MESSAGES.MISSING_LOGOUT_ENDPOINT)\n }\n\n return $http.get(config.endpoints.logout).then(function () {\n $rootScope.currentUser = null;\n openLogin();\n });\n },\n\n /**\n * Success handler\n * @param {*} data received from backend\n */\n success: function (data) {\n info(MESSAGES.SUCCESS_AUTH)\n },\n\n /**\n * Error handler\n * @param {*} err backend error object\n */\n error: error\n }\n };\n\n // ------------------------------------------------------------------------/// Private\n function info(message) {\n _log(console.info, 'AuthService: ' + message)\n }\n\n function error(err) {\n _log(console.error, err);\n }\n \n function _log(fn, msg) {\n if(config.verbose) {\n fn && fn(msg)\n }\n }\n\n function goTo(route) {\n info(($location.path() || 'unknown') + ' -----> ' + route);\n if(route == config.uiRoutes.login && service.isAuthenticated()) {\n info('User is already authenticated and cannot open login page: ' + route);\n route = getHome();\n $location.path(route);\n info('Redirected to the ' + route);\n } else if(route == $location.path()) {\n info('Reload: ' + route);\n $route.reload()\n } else {\n $location.path(route);\n info('Redirected to the ' + route);\n }\n }\n\n function isAuthenticated() {\n if (!config.endpoints.isAuthenticated) {\n return $q.resolve(true);\n }\n\n return $http.get(config.endpoints.isAuthenticated).then(function (response) {\n var isAuth = JSON.parse(response.data);\n info('isAuthenticated: ' + isAuth);\n $rootScope.currentUser = response.data;\n return isAuth || $q.reject(response.data);\n });\n }\n\n function init() {\n info('init');\n isAuthenticated()\n .then(service.refreshCurrentUser)\n .then(function (user) {\n config.handlers.success(user);\n })\n .catch(function (err) {\n if(!service.isPublic($location.path())) {\n openLogin();\n }\n return onError(err);\n });\n }\n\n function openLogin() {\n goTo(config.uiRoutes.login);\n }\n\n function onError(err) {\n error(err);\n config.handlers.error(err);\n return $q.reject(err);\n }\n\n function getHome() {\n return config.handlers.getHomePage($rootScope.currentUser);\n }\n\n // ------------------------------------------------------------------------/// Public\n var service = {\n config: config,\n _info: info,\n\n /**\n * Returns true if provide route url is in the list of public urls\n * @param {String} path route path that should be checked\n * @returns {boolean} true if url is in the list of public urls\n */\n isPublic: function (path) {\n if(!path) {\n return false;\n }\n\n return config.publicUrls.some(function (pattern) {\n if(pattern instanceof RegExp) {\n return path.match(pattern);\n } else if(typeof pattern == \"string\") {\n return path && pattern.startsWith(path);\n } else {\n return false;\n }\n });\n },\n /**\n * Saves current route as a target route\n */\n saveTarget: function () {\n config.uiRoutes.target = $location.path() || null;\n info('Target route is saved: ' + config.uiRoutes.target);\n },\n /**\n * Redirects user to the saved target route if exists or to the home page\n */\n openTarget: function () {\n var target = config.uiRoutes.target || getHome();\n info('Open target: ' + target);\n goTo(target);\n service.clearTarget()\n },\n /**\n * Clears saved target route\n */\n clearTarget: function () {\n config.uiRoutes.target = null;\n },\n /**\n * Redirects user to the login page\n */\n openLogin: openLogin,\n /**\n * Redirects user to the home page\n */\n openHome: function () {\n goTo(getHome());\n },\n /**\n * Returns saved current user or load it from backed\n * Always returns {Promise}\n * @returns {Promise}\n */\n getCurrentUser: function () {\n return $rootScope.currentUser ? $q.resolve($rootScope.currentUser) : service.refreshCurrentUser();\n },\n /**\n * Loads user from backed using currentUser endpoint or getUser handler\n * Always returns {Promise}\n * @returns {Promise}\n */\n refreshCurrentUser: function() {\n return config.handlers.getUser().then(function (user) {\n $rootScope.currentUser = user;\n service.openTarget();\n return $rootScope.currentUser;\n })\n },\n /**\n * Returns true if user is authenticated\n * Warning! It does not check backend.\n * @returns {boolean} true if user is authenticated\n */\n isAuthenticated: function () {\n return !!$rootScope.currentUser;\n },\n /**\n * Logs user out from the system and redirects it to the login page\n */\n logout: function () {\n return config.handlers.logout();\n },\n /**\n * Allows you to configure angular-spa-auth module and start the init process.\n * Should be called in the #run method of you module\n * @param {Object} configuration contains all the configs\n * @param {String=} configuration.verbose activates console.info output if true\n * @param {String[]=} configuration.publicUrls list url that are available for unauthorized users\n * @param {Object=} configuration.endpoints gives you ability to setup all the backed endpoints that will own roles in the authentication process\n * @param {Object=} configuration.uiRoutes helps you automatically redirect user to the specified UI routes such as home and login\n * @param {String=} configuration.uiRoutes.home home route\n * @param {String=} configuration.uiRoutes.login login route\n * @param {Object=} configuration.handlers allows you to provide you implementation for key methods of authentication process\n */\n run: function (configuration) {\n if (configuration) {\n if(configuration.publicUrls) {\n //publicUrls should be completely replaced by new value if provided\n //to provide ability to set new array with only one route\n config.publicUrls = configuration.publicUrls;\n }\n\n config = angular.merge(config, configuration);\n }\n init()\n },\n /**\n * Login user using provided credentials\n * @param {Object} credentials object with any type of information that is needed to compelete authentication process\n */\n login: function (credentials) {\n return config.handlers.login(credentials)\n .then(service.refreshCurrentUser)\n .then(config.handlers.success)\n .catch(onError);\n }\n };\n\n return service\n }]);\n})();","/*! http://mths.be/startswith v0.2.0 by @mathias */\nif (!String.prototype.startsWith) {\n\t(function() {\n\t\t'use strict'; // needed to support `apply`/`call` with `undefined`/`null`\n\t\tvar defineProperty = (function() {\n\t\t\t// IE 8 only supports `Object.defineProperty` on DOM elements\n\t\t\ttry {\n\t\t\t\tvar object = {};\n\t\t\t\tvar $defineProperty = Object.defineProperty;\n\t\t\t\tvar result = $defineProperty(object, object, object) && $defineProperty;\n\t\t\t} catch(error) {}\n\t\t\treturn result;\n\t\t}());\n\t\tvar toString = {}.toString;\n\t\tvar startsWith = function(search) {\n\t\t\tif (this == null) {\n\t\t\t\tthrow TypeError();\n\t\t\t}\n\t\t\tvar string = String(this);\n\t\t\tif (search && toString.call(search) == '[object RegExp]') {\n\t\t\t\tthrow TypeError();\n\t\t\t}\n\t\t\tvar stringLength = string.length;\n\t\t\tvar searchString = String(search);\n\t\t\tvar searchLength = searchString.length;\n\t\t\tvar position = arguments.length > 1 ? arguments[1] : undefined;\n\t\t\t// `ToInteger`\n\t\t\tvar pos = position ? Number(position) : 0;\n\t\t\tif (pos != pos) { // better `isNaN`\n\t\t\t\tpos = 0;\n\t\t\t}\n\t\t\tvar start = Math.min(Math.max(pos, 0), stringLength);\n\t\t\t// Avoid the `indexOf` call if no match is possible\n\t\t\tif (searchLength + start > stringLength) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar index = -1;\n\t\t\twhile (++index < searchLength) {\n\t\t\t\tif (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\t\tif (defineProperty) {\n\t\t\tdefineProperty(String.prototype, 'startsWith', {\n\t\t\t\t'value': startsWith,\n\t\t\t\t'configurable': true,\n\t\t\t\t'writable': true\n\t\t\t});\n\t\t} else {\n\t\t\tString.prototype.startsWith = startsWith;\n\t\t}\n\t}());\n}\n"]} \ No newline at end of file From fedb4a12d2539cbd6d6d5901bc1adb5801ca6409 Mon Sep 17 00:00:00 2001 From: Volodymyr Lavrynovych Date: Thu, 24 Aug 2017 01:22:57 +0300 Subject: [PATCH 3/3] v1.0.1 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 7c95492..ee8d1b3 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "authors": [ "Volodymyr Lavrynovych " ], - "version": "1.0.0", + "version": "1.0.1", "description": "Provides ability to easily handle most of the logic related to the authentication process and page load for the AngularJS SPA", "main": "dist/angular-spa-auth.min.js", "keywords": [ diff --git a/package.json b/package.json index a7e91ab..8d95706 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "angular-spa-auth", "title": "Angular SPA Auth", - "version": "1.0.0", + "version": "1.0.1", "author": "Volodymyr Lavrynovych ", "contributors": [ "Volodymyr Lavrynovych "