From e080802445df77f434b68e40bb74eb27bc47d237 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Wed, 23 Jan 2019 13:11:44 +0100 Subject: [PATCH 1/4] #981 Login unified without passing tests --- back/methods/account/acl.js | 45 ++ back/methods/account/login.js | 31 +- back/methods/account/specs/login.spec.js | 45 ++ back/methods/account/specs/logout.spec.js | 42 ++ back/methods/account/validate-token.js | 31 ++ back/models/account.js | 2 + back/models/account.json | 9 + front/auth/auth.ejs | 11 - front/auth/index.js | 2 - front/auth/login/login.html | 29 - front/auth/login/login.js | 86 --- front/auth/login/style.scss | 51 -- front/auth/module.js | 14 - front/core/directives/acl.js | 14 +- front/core/directives/specs/acl.spec.js | 2 +- front/core/directives/visible-by.js | 3 +- front/core/lib/acl-service.js | 61 +-- front/core/lib/index.js | 1 - front/core/lib/module-loader.js | 18 +- front/core/lib/specs/acl-service.spec.js | 12 +- front/salix/components/app/app.html | 20 +- front/salix/components/app/app.js | 31 +- front/salix/components/app/style.scss | 99 +++- front/salix/components/home/home.html | 36 +- front/salix/components/home/home.js | 22 +- front/salix/components/home/style.scss | 3 +- front/salix/components/index.js | 3 +- .../components}/login/locale/en.yml | 0 .../components}/login/locale/es.yml | 0 front/salix/components/login/login.html | 29 + front/salix/components/login/login.js | 38 ++ .../{auth => salix/components}/login/logo.svg | 0 front/salix/components/login/style.scss | 61 +++ .../salix/components/main-menu/main-menu.html | 14 +- front/salix/components/main-menu/main-menu.js | 9 +- .../components/main-menu/main-menu.spec.js | 4 +- front/salix/components/main-menu/style.scss | 51 +- front/salix/components/topbar/style.scss | 18 +- front/salix/components/topbar/topbar.html | 2 +- front/salix/config-routes.js | 78 --- front/salix/index.js | 4 +- ...global-keybindings.yml => keybindings.yml} | 0 front/salix/lib/auth.js | 105 ++++ front/salix/lib/index.js | 4 + front/{core => salix}/lib/interceptor.js | 12 +- front/salix/lib/modules.js | 46 ++ front/salix/lib/token.js | 51 ++ front/salix/module.js | 21 +- front/salix/modules-factory.js | 36 -- front/salix/routes.js | 85 +++ loopback/client/auth.ejs | 13 - loopback/client/index.ejs | 1 - loopback/server/boot/auth.js | 94 ---- loopback/server/boot/salix.js | 142 +---- loopback/server/boot/specs/auth.spec.js | 103 ---- modules/client/front/risk/index/index.js | 6 +- package-lock.json | 509 ++++++++++++++++-- package.json | 1 + webpack.config.js | 5 - webpack.config.yml | 3 +- 60 files changed, 1344 insertions(+), 924 deletions(-) create mode 100644 back/methods/account/acl.js create mode 100644 back/methods/account/specs/login.spec.js create mode 100644 back/methods/account/specs/logout.spec.js create mode 100644 back/methods/account/validate-token.js delete mode 100644 front/auth/auth.ejs delete mode 100644 front/auth/index.js delete mode 100644 front/auth/login/login.html delete mode 100644 front/auth/login/login.js delete mode 100644 front/auth/login/style.scss delete mode 100644 front/auth/module.js rename front/{auth => salix/components}/login/locale/en.yml (100%) rename front/{auth => salix/components}/login/locale/es.yml (100%) create mode 100644 front/salix/components/login/login.html create mode 100644 front/salix/components/login/login.js rename front/{auth => salix/components}/login/logo.svg (100%) create mode 100644 front/salix/components/login/style.scss delete mode 100644 front/salix/config-routes.js rename front/salix/{global-keybindings.yml => keybindings.yml} (100%) create mode 100644 front/salix/lib/auth.js create mode 100644 front/salix/lib/index.js rename front/{core => salix}/lib/interceptor.js (72%) create mode 100644 front/salix/lib/modules.js create mode 100644 front/salix/lib/token.js delete mode 100644 front/salix/modules-factory.js create mode 100644 front/salix/routes.js delete mode 100644 loopback/client/auth.ejs delete mode 100644 loopback/server/boot/auth.js delete mode 100644 loopback/server/boot/specs/auth.spec.js diff --git a/back/methods/account/acl.js b/back/methods/account/acl.js new file mode 100644 index 000000000..c1dcdc11b --- /dev/null +++ b/back/methods/account/acl.js @@ -0,0 +1,45 @@ +module.exports = Self => { + Self.remoteMethod('acl', { + description: 'Get the user information and permissions', + accepts: [ + { + arg: 'ctx', + type: 'Object', + http: {source: 'context'} + } + ], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/acl`, + verb: 'GET' + } + }); + + Self.acl = async function(ctx) { + let userId = ctx.req.accessToken.userId; + let models = Self.app.models; + + let user = await models.Account.findById(userId, { + fields: ['id', 'name', 'nickname', 'email'] + }); + + let roles = await models.RoleMapping.find({ + fields: ['roleId'], + where: { + principalId: userId, + principalType: 'USER' + }, + include: [{ + relation: 'role', + scope: { + fields: ['name'] + } + }] + }); + + return {roles, user}; + }; +}; diff --git a/back/methods/account/login.js b/back/methods/account/login.js index 59ce78690..cad85f035 100644 --- a/back/methods/account/login.js +++ b/back/methods/account/login.js @@ -1,4 +1,3 @@ -const url = require('url'); const md5 = require('md5'); module.exports = Self => { @@ -13,12 +12,7 @@ module.exports = Self => { }, { arg: 'password', type: 'String', - description: 'The user name or email', - required: true - }, { - arg: 'location', - type: 'String', - description: 'Location to redirect after login' + description: 'The user name or email' } ], returns: { @@ -31,7 +25,7 @@ module.exports = Self => { } }); - Self.login = async function(user, password, location) { + Self.login = async function(user, password) { let token; let usesEmail = user.indexOf('@') !== -1; let User = Self.app.models.User; @@ -68,25 +62,6 @@ module.exports = Self => { token = await User.login(loginInfo, 'user'); } - let apiKey; - let continueUrl; - - try { - let query = url.parse(location, true).query; - apiKey = query.apiKey; - continueUrl = query.continue; - } catch (e) { - continueUrl = null; - } - - let applications = Self.app.get('applications'); - if (!apiKey) apiKey = 'default'; - let loginUrl = applications[apiKey] || '/login'; - - return { - token: token.id, - continue: continueUrl, - loginUrl: loginUrl - }; + return {token: token.id}; }; }; diff --git a/back/methods/account/specs/login.spec.js b/back/methods/account/specs/login.spec.js new file mode 100644 index 000000000..1768557ff --- /dev/null +++ b/back/methods/account/specs/login.spec.js @@ -0,0 +1,45 @@ +const app = require(`${serviceRoot}/server/server`); + +describe('account login()', () => { + it('when the user doesnt exist but the client does and the password is correct', async() => { + let response = await app.models.Account.login('PetterParker', 'nightmare'); + + expect(response.token).toBeDefined(); + }); + + describe('when the user exists and the password is correct', () => { + it('should login and return the token', async() => { + let response = await app.models.Account.login('employee', 'nightmare'); + + expect(response.token).toBeDefined(); + }); + + it('should define the url to continue upon login', async() => { + let location = 'http://localhost/auth/?apiKey=salix&continue=continueURL'; + let response = await app.models.Account.login('employee', 'nightmare', location); + + expect(response.continue).toBe('continueURL'); + }); + + it('should define the loginUrl upon login', async() => { + let location = 'http://localhost/auth/?apiKey=salix'; + let response = await app.models.Account.login('employee', 'nightmare', location); + + expect(response.loginUrl).toBeDefined(); + }); + }); + + it('should throw a 401 error when credentials are incorrect', async() => { + let error; + + try { + await app.models.Account.login('IDontExist', 'TotallyWrongPassword'); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.statusCode).toBe(401); + expect(error.code).toBe('LOGIN_FAILED'); + }); +}); diff --git a/back/methods/account/specs/logout.spec.js b/back/methods/account/specs/logout.spec.js new file mode 100644 index 000000000..b77c6fd2e --- /dev/null +++ b/back/methods/account/specs/logout.spec.js @@ -0,0 +1,42 @@ +const app = require(`${serviceRoot}/server/server`); + +describe('account logout()', () => { + it('should logout and remove token after valid login', async() => { + let loginResponse = await app.models.Account.login('employee', 'nightmare'); + let accessToken = await app.models.AccessToken.findById(loginResponse.token); + let ctx = {req: {accessToken: accessToken}}; + + let response = await app.models.Account.logout(ctx); + let afterToken = await app.models.AccessToken.findById(loginResponse.token); + + expect(response).toBeTruthy(); + expect(afterToken).toBeNull(); + }); + + it('should throw a 401 error when token is invalid', async() => { + let error; + let ctx = {req: {accessToken: {id: 'invalidToken'}}}; + + try { + response = await app.models.Account.logout(ctx); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.statusCode).toBe(401); + }); + + it('should throw an error when no token is passed', async() => { + let error; + let ctx = {req: {accessToken: null}}; + + try { + response = await app.models.Account.logout(ctx); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + }); +}); diff --git a/back/methods/account/validate-token.js b/back/methods/account/validate-token.js new file mode 100644 index 000000000..f1264855d --- /dev/null +++ b/back/methods/account/validate-token.js @@ -0,0 +1,31 @@ +module.exports = Self => { + Self.remoteMethod('validateToken', { + description: 'Get the user information and permissions', + accepts: [ + { + arg: 'token', + type: 'String', + description: 'The token to validate', + required: true + } + ], + returns: { + type: 'Boolean', + root: true + }, + http: { + path: `/validateToken`, + verb: 'GET' + } + }); + + Self.validateToken = function(tokenId, cb) { + Self.app.models.AccessToken.findById(tokenId, (err, token) => { + if (err) return cb(err); + if (token) + token.validate((_, isValid) => cb(null, isValid === true)); + else + cb(null, false); + }); + }; +}; diff --git a/back/models/account.js b/back/models/account.js index dbf3cdabe..4ccd0e99a 100644 --- a/back/models/account.js +++ b/back/models/account.js @@ -3,6 +3,8 @@ const md5 = require('md5'); module.exports = Self => { require('../methods/account/login')(Self); require('../methods/account/logout')(Self); + require('../methods/account/acl')(Self); + require('../methods/account/validate-token')(Self); // Validations diff --git a/back/models/account.json b/back/models/account.json index 00d031f9e..5e1398587 100644 --- a/back/models/account.json +++ b/back/models/account.json @@ -15,6 +15,9 @@ "type": "string", "required": true }, + "nickname": { + "type": "string" + }, "password": { "type": "string", "required": true @@ -45,6 +48,12 @@ "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW" + }, { + "property": "validateToken", + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" } ] } diff --git a/front/auth/auth.ejs b/front/auth/auth.ejs deleted file mode 100644 index 2ed12bc08..000000000 --- a/front/auth/auth.ejs +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Salix - - - - - - diff --git a/front/auth/index.js b/front/auth/index.js deleted file mode 100644 index d15aa496a..000000000 --- a/front/auth/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import './module'; -import './login/login'; diff --git a/front/auth/login/login.html b/front/auth/login/login.html deleted file mode 100644 index 0f0bcec35..000000000 --- a/front/auth/login/login.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
-
- -
- - - - - -
-
-
- -
diff --git a/front/auth/login/login.js b/front/auth/login/login.js deleted file mode 100644 index b92dd9ffa..000000000 --- a/front/auth/login/login.js +++ /dev/null @@ -1,86 +0,0 @@ -import ngModule from '../module'; -import './style.scss'; - -/** - * A simple login form. - */ -export default class Controller { - constructor($element, $scope, $window, $http) { - this.$element = $element; - this.$ = $scope; - this.$window = $window; - this.$http = $http; - } - submit() { - if (!this.user) { - this.focusUser(); - this.showError('Please insert your user and password'); - return; - } - - this.loading = true; - let params = { - user: this.user, - password: this.password, - location: this.$window.location.href - }; - this.$http.post('/auth/login', params).then( - json => this.onLoginOk(json), - json => this.onLoginErr(json) - ); - } - onLoginOk(json) { - this.loading = false; - let data = json.data; - let params = { - token: data.token, - continue: data.continue - }; - let loginUrl = data.loginUrl || ''; - this.$window.location = `${loginUrl}?${this.encodeUri(params)}`; - } - encodeUri(object) { - let uri = ''; - for (let key in object) { - if (object[key] !== undefined) { - if (uri.length > 0) - uri += '&'; - uri += encodeURIComponent(key) + '=' + encodeURIComponent(object[key]); - } - } - return uri; - } - onLoginErr(json) { - this.loading = false; - this.password = ''; - - let message; - - switch (json.status) { - case 401: - message = 'Invalid credentials'; - break; - case -1: - message = 'Can\'t contact with server'; - break; - default: - message = 'Something went wrong'; - } - - this.showError(message); - this.focusUser(); - } - focusUser() { - this.$.userField.select(); - this.$.userField.focus(); - } - showError(message) { - this.$.snackbar.showError({message: message}); - } -} -Controller.$inject = ['$element', '$scope', '$window', '$http']; - -ngModule.component('vnLogin', { - template: require('./login.html'), - controller: Controller -}); diff --git a/front/auth/login/style.scss b/front/auth/login/style.scss deleted file mode 100644 index 61e5a3e02..000000000 --- a/front/auth/login/style.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import "colors"; - -vn-login > div { - position: absolute; - height: 100%; - width: 100%; - margin: 0; - padding: 0; - color: $main-font-color; - font-size: 1.1em; - font-weight: normal; - background-color: #3c393b; - - .box-wrapper { - position: relative; - max-width: 19em; - margin: auto; - height: inherit; - } - - .box { - box-sizing: border-box; - position: absolute; - top: 50%; - width: 100%; - margin-top: -13.5em; - padding: 3em; - background-color: white; - box-shadow: 0 0 1em 0 rgba(1,1,1,.6); - border-radius: .5em; - } - - img { - width: 100%; - padding-bottom: 1em; - } - - .footer { - margin-top: 1em; - text-align: center; - position: relative; - } - - .spinner-wrapper { - position: absolute; - width: 0; - top: .3em; - right: 3em; - overflow: visible; - } -} diff --git a/front/auth/module.js b/front/auth/module.js deleted file mode 100644 index ff38fa5f1..000000000 --- a/front/auth/module.js +++ /dev/null @@ -1,14 +0,0 @@ -import {ng} from 'core/vendor'; -import 'core'; - -let ngModule = ng.module('vnAuth', ['vnCore']); -export default ngModule; - -config.$inject = ['$translatePartialLoaderProvider', '$httpProvider']; -export function config($translatePartialLoaderProvider, $httpProvider) { - $translatePartialLoaderProvider.addPart('auth'); - - $httpProvider.defaults.useXDomain = true; - delete $httpProvider.defaults.headers.common['X-Requested-With']; -} -ngModule.config(config); diff --git a/front/core/directives/acl.js b/front/core/directives/acl.js index 2ef4a0d7b..81acbbe85 100644 --- a/front/core/directives/acl.js +++ b/front/core/directives/acl.js @@ -16,9 +16,8 @@ function vnAcl(aclService, $timeout) { let find = input.className.match(/mdl-[\w]+input/g); if (find && find.length && find[0]) { let type = getMaterialType(find[0]); - if (type && input.parentNode[`Material${type}`] && input.parentNode[`Material${type}`].updateClasses_) { + if (type && input.parentNode[`Material${type}`] && input.parentNode[`Material${type}`].updateClasses_) input.parentNode[`Material${type}`].updateClasses_(); - } } } } @@ -38,7 +37,7 @@ function vnAcl(aclService, $timeout) { } function permissionElement($element, action) { - if (!aclService.aclPermission(acls)) { + if (!aclService.hasAny(acls)) { if (action === 'disabled') { let input = $element[0]; let selector = 'input, textarea, button, submit'; @@ -48,25 +47,24 @@ function vnAcl(aclService, $timeout) { if (input) { $timeout(() => { - input.setAttribute("disabled", "true"); + input.setAttribute('disabled', 'true'); updateMaterial(input); }); $element[0].querySelectorAll('vn-drop-down').forEach(element => { element.parentNode.removeChild(element); }); } - } else { + } else $element.remove(); - } } } function updateAcls(role, toAdd) { let position = acls.indexOf(role); - if (!toAdd && position > -1) { + if (!toAdd && position > -1) acls.splice(position, 1); - } // todo: add acl and enabled element if previusly was disabled + // todo: add acl and enabled element if previusly was disabled } return { diff --git a/front/core/directives/specs/acl.spec.js b/front/core/directives/specs/acl.spec.js index 2ceb56568..4cd19072f 100644 --- a/front/core/directives/specs/acl.spec.js +++ b/front/core/directives/specs/acl.spec.js @@ -8,7 +8,7 @@ describe('Directive acl', () => { compile = (hasPermissions, _element) => { inject(($compile, $rootScope, aclService, _$timeout_) => { - spyOn(aclService, 'aclPermission').and.returnValue(hasPermissions); + spyOn(aclService, 'hasAny').and.returnValue(hasPermissions); scope = $rootScope.$new(); $timeout = _$timeout_; element = angular.element(_element); diff --git a/front/core/directives/visible-by.js b/front/core/directives/visible-by.js index e05079fb5..8be5cfc90 100644 --- a/front/core/directives/visible-by.js +++ b/front/core/directives/visible-by.js @@ -6,9 +6,8 @@ function vnVisibleBy(aclService) { priority: -1, link: function($scope, $element, $attrs) { let acls = $attrs.vnVisibleBy.split(','); - if (!aclService.aclPermission(acls)) { + if (!aclService.hasAny(acls)) $element[0].style.visibility = 'hidden'; - } } }; } diff --git a/front/core/lib/acl-service.js b/front/core/lib/acl-service.js index ae4c865bf..d286ec85f 100644 --- a/front/core/lib/acl-service.js +++ b/front/core/lib/acl-service.js @@ -1,43 +1,34 @@ import ngModule from '../module'; -var acl = window.salix ? window.salix.acl : {}; -ngModule.constant('aclConstant', acl); - -aclService.$inject = ['aclConstant']; -function aclService(aclConstant) { - if (aclConstant.roles) { - this.roles = {}; - Object.keys(aclConstant.roles).forEach(role => { - this.roles[role.toLowerCase()] = aclConstant.roles[role]; - }); - } else { - this.roles = undefined; +class AclService { + constructor($http) { + this.$http = $http; } + reset() { + this.user = null; + this.roles = null; + } + load() { + return this.$http.get('/api/Accounts/acl').then(res => { + this.user = res.data.user; + this.roles = {}; - this.routeHasPermission = function(route) { - let hasPermission; - if (!this.roles) - hasPermission = false; - else if (!route.acl) - hasPermission = true; - else if (!this.roles || !Object.keys(this.roles).length) - hasPermission = false; - else - hasPermission = this.aclPermission(route.acl); - return hasPermission; - }; - this.aclPermission = function(aclCollection) { - let hasPermission = false; - let total = aclCollection.length; - for (let i = 0; i < total; i++) { - let role = aclCollection[i].trim().toLowerCase(); - if (this.roles[role]) { - hasPermission = true; - break; + for (let role of res.data.roles) { + if (role.role) + this.roles[role.role.name] = true; + } + }); + } + hasAny(roles) { + if (this.roles) { + for (let role of roles) { + if (this.roles[role]) + return true; } } - return hasPermission; - }; + return false; + } } +AclService.$inject = ['$http']; -ngModule.service('aclService', aclService); +ngModule.service('aclService', AclService); diff --git a/front/core/lib/index.js b/front/core/lib/index.js index ca135e67f..9ea493c64 100644 --- a/front/core/lib/index.js +++ b/front/core/lib/index.js @@ -1,7 +1,6 @@ import './module-loader'; import './crud'; import './app'; -import './interceptor'; import './acl-service'; import './storage-services'; import './template'; diff --git a/front/core/lib/module-loader.js b/front/core/lib/module-loader.js index 358d3863b..b4f393d77 100644 --- a/front/core/lib/module-loader.js +++ b/front/core/lib/module-loader.js @@ -22,13 +22,13 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t let deps = splitingRegister.getDependencies(moduleName); let depPromises = []; - if (deps) + if (deps) { for (let dep of deps) depPromises.push(this.load(dep, validations)); + } loaded[moduleName] = new Promise((resolve, reject) => { - Promise.all(depPromises) - .then(() => { + Promise.all(depPromises).then(() => { let promises = []; $translatePartialLoader.addPart(moduleName); @@ -39,26 +39,24 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t ); })); - if (validations) + if (validations) { promises.push(new Promise(resolve => { $http.get(`/${moduleName}/validations`).then( json => this.onValidationsReady(json, resolve), () => resolve() ); })); + } promises.push(new Promise(resolve => { splitingRegister.modules[moduleName](resolve); })); - Promise.all(promises) - .then(() => { + Promise.all(promises).then(() => { loaded[moduleName] = true; resolve($ocLazyLoad.load({name: moduleName})); - }) - .catch(reject); - }) - .catch(reject); + }).catch(reject); + }).catch(reject); }); return loaded[moduleName]; diff --git a/front/core/lib/specs/acl-service.spec.js b/front/core/lib/specs/acl-service.spec.js index fb9c4a40e..f29f4bf2d 100644 --- a/front/core/lib/specs/acl-service.spec.js +++ b/front/core/lib/specs/acl-service.spec.js @@ -28,21 +28,21 @@ describe('Service acl', () => { expect(aclService.routeHasPermission(route)).toBeFalsy(); }); - it('should call the service aclPermission() function and return false as the service has roles and the rote has acl without length', () => { + it('should call the service hasAny() function and return false as the service has roles and the rote has acl without length', () => { aclService.roles = {customer: true, employee: true}; let route = {url: 'http://www.verdnatura.es', acl: []}; - spyOn(aclService, 'aclPermission').and.callThrough(); + spyOn(aclService, 'hasAny').and.callThrough(); expect(aclService.routeHasPermission(route)).toBeFalsy(); - expect(aclService.aclPermission).toHaveBeenCalledWith(route.acl); + expect(aclService.hasAny).toHaveBeenCalledWith(route.acl); }); - it('should call the service aclPermission() function to return true as the service has roles matching with the ones in acl', () => { + it('should call the service hasAny() function to return true as the service has roles matching with the ones in acl', () => { aclService.roles = {customer: true, employee: true}; let route = {url: 'http://www.verdnatura.es', acl: ['customer']}; - spyOn(aclService, 'aclPermission').and.callThrough(); + spyOn(aclService, 'hasAny').and.callThrough(); expect(aclService.routeHasPermission(route)).toBeTruthy(); - expect(aclService.aclPermission).toHaveBeenCalledWith(route.acl); + expect(aclService.hasAny).toHaveBeenCalledWith(route.acl); }); }); diff --git a/front/salix/components/app/app.html b/front/salix/components/app/app.html index 35546d113..fe9c45937 100644 --- a/front/salix/components/app/app.html +++ b/front/salix/components/app/app.html @@ -1,11 +1,23 @@ - - - + + + + -
+
+
+
\ No newline at end of file diff --git a/front/salix/components/app/app.js b/front/salix/components/app/app.js index 2368fdef2..17e90dc11 100644 --- a/front/salix/components/app/app.js +++ b/front/salix/components/app/app.js @@ -2,15 +2,42 @@ import ngModule from '../../module'; import './style.scss'; export default class App { - constructor($scope, vnApp) { + constructor($element, $scope, vnApp, $state, $transitions) { + this.$element = $element; this.$ = $scope; this.vnApp = vnApp; + this.$state = $state; + + $transitions.onStart({}, () => { + if (this.menuShown) this.hideMenu(); + }); } $postLink() { + this.background = this.$element[0].querySelector('.background'); this.vnApp.snackbar = this.$.snackbar; } + // TODO: Temporary fix to hide the topbar when login is displayed + get inApp() { + let state = this.$state.current.name; + return state && state != 'login'; + } + get leftBlock() { + return this.$element[0].querySelector('.left-block'); + } + showMenu() { + let leftBlock = this.leftBlock; + if (!leftBlock) return; + leftBlock.classList.add('shown'); + this.menuShown = true; + } + hideMenu() { + this.menuShown = false; + let leftBlock = this.leftBlock; + if (!leftBlock) return; + leftBlock.classList.remove('shown'); + } } -App.$inject = ['$scope', 'vnApp']; +App.$inject = ['$element', '$scope', 'vnApp', '$state', '$transitions']; ngModule.component('vnApp', { template: require('./app.html'), diff --git a/front/salix/components/app/style.scss b/front/salix/components/app/style.scss index e66b46634..7893a96a9 100644 --- a/front/salix/components/app/style.scss +++ b/front/salix/components/app/style.scss @@ -1,5 +1,9 @@ @import "background"; +$menu-width: 16em; +$topbar-height: 4em; +$mobile-width: 800px; + body { @extend .bg-content; overflow: auto; @@ -7,49 +11,104 @@ body { vn-app { display: block; - vn-topbar { + & > vn-topbar { position: fixed; top: 0; left: 0; width: 100%; - z-index: 5; - box-shadow: 0 .1em .2em rgba(1, 1, 1, .2); + z-index: 2; + box-shadow: 0 .1em .2em rgba(1, 1, 1, .2); + height: $topbar-height; + padding: .6em; - .logo { - float: left; - height: 1.8em; - padding: 1em; - } - vn-spinner { - float: left; - padding: 1em .4em; + & > header { + & > .logo > img { + height: 1.8em; + display: block; + } + & > .show-menu { + display: none; + font-size: 2.3em; + cursor: pointer; + + &:hover { + color: $main-01; + } + } + & > vn-spinner { + padding: 0 .4em; + } + & > vn-main-menu { + flex: 1; + } } } - .main-view { - padding-top: 4em; - + & > .main-view { + &.padding { + padding-top: $topbar-height; + } vn-main-block { display: block; margin: 0 auto; - padding-left: 16em; + padding-left: $menu-width; .left-block { position: fixed; z-index: 5; - top: 4em; + top: $topbar-height; left: 0; bottom: 0; - width: 16em; - min-width: 16em; + width: $menu-width; + min-width: $menu-width; background-color: white; box-shadow: 0 .1em .2em rgba(1, 1, 1, .2); overflow: auto; } .right-block { - width: 16em; - min-width: 16em; + width: $menu-width; + min-width: $menu-width; padding-left: 1em; } + + } + } + & > .background { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: black; + z-index: 4; + opacity: 0; + transition: opacity 200ms ease-out; + } + @media screen and (max-width: $mobile-width) { + & > vn-topbar > header { + & > .logo { + display: none; + } + & > .show-menu { + display: block; + } + } + & > .main-view vn-main-block { + padding-left: 0; + + .left-block { + top: 0; + transform: translateZ(0) translateX(-$menu-width); + transition: transform 200ms ease-out; + + &.shown { + transform: translateZ(0) translateX(0); + } + } + } + & > .background.shown { + display: block; + opacity: .3; } } } diff --git a/front/salix/components/home/home.html b/front/salix/components/home/home.html index 48546f4cd..8187cf18b 100644 --- a/front/salix/components/home/home.html +++ b/front/salix/components/home/home.html @@ -1,22 +1,22 @@
-
diff --git a/front/salix/components/home/home.js b/front/salix/components/home/home.js index dd8765286..8b71f1f39 100644 --- a/front/salix/components/home/home.js +++ b/front/salix/components/home/home.js @@ -1,31 +1,19 @@ import ngModule from '../../module'; import './style.scss'; -import keybindings from '../../global-keybindings.yml'; export default class Controller { - constructor(modulesFactory, $state, $translate, $sce) { - this.modules = modulesFactory.getModules(); + constructor(vnModules, $state, $translate, $sce) { + this.modules = vnModules.get(); this.$state = $state; this._ = $translate; this.$sce = $sce; - this.keybindings = keybindings; - } - $onInit() { - this.modules.map(mod => { - let keyBind = this.keybindings.find(keyBind => { - return keyBind.sref == mod.route.state; - }); - - if (keyBind) - mod.keyBind = keyBind.key.toUpperCase(); - }); } getModuleName(mod) { let getName = mod => { let name = this._.instant(mod.name); - let lower = name.toUpperCase(); + let upper = name.toUpperCase(); if (!mod.keyBind) return name; - let index = lower.indexOf(mod.keyBind); + let index = upper.indexOf(mod.keyBind); if (index === -1) return name; let newName = name.substr(0, index); @@ -38,7 +26,7 @@ export default class Controller { } } -Controller.$inject = ['modulesFactory', '$state', '$translate', '$sce']; +Controller.$inject = ['vnModules', '$state', '$translate', '$sce']; ngModule.component('vnHome', { template: require('./home.html'), diff --git a/front/salix/components/home/style.scss b/front/salix/components/home/style.scss index f9ba0732b..63aabe639 100644 --- a/front/salix/components/home/style.scss +++ b/front/salix/components/home/style.scss @@ -1,7 +1,8 @@ @import "effects"; vn-home { - padding: 2em; + display: block; + padding: .5em; & > div { & > h6 { diff --git a/front/salix/components/index.js b/front/salix/components/index.js index 9b0bc46ff..b90b221a4 100644 --- a/front/salix/components/index.js +++ b/front/salix/components/index.js @@ -1,6 +1,7 @@ import './app/app'; +import './login/login'; import './home/home'; import './main-menu/main-menu'; import './left-menu/left-menu'; import './topbar/topbar'; -import './user-configuration-popover' +import './user-configuration-popover'; diff --git a/front/auth/login/locale/en.yml b/front/salix/components/login/locale/en.yml similarity index 100% rename from front/auth/login/locale/en.yml rename to front/salix/components/login/locale/en.yml diff --git a/front/auth/login/locale/es.yml b/front/salix/components/login/locale/es.yml similarity index 100% rename from front/auth/login/locale/es.yml rename to front/salix/components/login/locale/es.yml diff --git a/front/salix/components/login/login.html b/front/salix/components/login/login.html new file mode 100644 index 000000000..95a0044d8 --- /dev/null +++ b/front/salix/components/login/login.html @@ -0,0 +1,29 @@ +
+ +
+ + + + + + + +
+
diff --git a/front/salix/components/login/login.js b/front/salix/components/login/login.js new file mode 100644 index 000000000..1b823e44d --- /dev/null +++ b/front/salix/components/login/login.js @@ -0,0 +1,38 @@ +import ngModule from '../../module'; +import './style.scss'; + +/** + * A simple login form. + */ +export default class Controller { + constructor($element, $scope, vnAuth) { + this.$element = $element; + this.$ = $scope; + this.vnAuth = vnAuth; + this.user = localStorage.getItem('lastUser'); + } + submit() { + this.loading = true; + this.vnAuth.login(this.user, this.password, this.remember) + .then(() => { + localStorage.setItem('lastUser', this.user); + this.loading = false; + }) + .catch(error => { + this.loading = false; + this.password = ''; + this.focusUser(); + throw error; + }); + } + focusUser() { + this.$.userField.select(); + this.$.userField.focus(); + } +} +Controller.$inject = ['$element', '$scope', 'vnAuth']; + +ngModule.component('vnLogin', { + template: require('./login.html'), + controller: Controller +}); diff --git a/front/auth/login/logo.svg b/front/salix/components/login/logo.svg similarity index 100% rename from front/auth/login/logo.svg rename to front/salix/components/login/logo.svg diff --git a/front/salix/components/login/style.scss b/front/salix/components/login/style.scss new file mode 100644 index 000000000..205a1ee27 --- /dev/null +++ b/front/salix/components/login/style.scss @@ -0,0 +1,61 @@ +@import "colors"; + +vn-login { + position: absolute; + height: 100%; + width: 100%; + margin: 0; + padding: 0; + color: $main-font-color; + font-size: 1.1em; + font-weight: normal; + background-color: #3c393b; + display: flex; + justify-content: center; + align-items: center; + overflow: auto; + + & > .box { + box-sizing: border-box; + position: absolute; + max-width: 19em; + min-width: 15em; + padding: 3em; + background-color: white; + box-shadow: 0 0 1em 0 rgba(1,1,1,.6); + border-radius: .5em; + + & > img { + width: 100%; + padding-bottom: 1em; + } + & > form { + & > vn-textfield { + width: 100%; + margin: 1em 0; + } + & > .footer { + margin-top: 1em; + text-align: center; + position: relative; + + & > .spinner-wrapper { + position: absolute; + width: 0; + top: .3em; + right: 3em; + overflow: visible; + } + } + } + } + + @media screen and (max-width: 600px) { + background-color: white; + + & > .box { + box-shadow: none; + background-color: white; + } + } +} diff --git a/front/salix/components/main-menu/main-menu.html b/front/salix/components/main-menu/main-menu.html index 5f771d363..cebe1e0ce 100644 --- a/front/salix/components/main-menu/main-menu.html +++ b/front/salix/components/main-menu/main-menu.html @@ -1,11 +1,11 @@ -
+
-
{{currentUserName}}
+ {{currentUserName}}
- - -
- \ No newline at end of file diff --git a/front/salix/components/main-menu/main-menu.js b/front/salix/components/main-menu/main-menu.js index 62926a64d..bd6b53678 100644 --- a/front/salix/components/main-menu/main-menu.js +++ b/front/salix/components/main-menu/main-menu.js @@ -12,13 +12,14 @@ let languages = { }; export default class MainMenu { - constructor($translate, $scope, $http, $window, modulesFactory) { + constructor($translate, $scope, $http, $window, vnModules, vnAuth) { this.$ = $scope; this.$http = $http; this.$translate = $translate; this.$window = $window; - this.modules = modulesFactory.getModules(); + this.modules = vnModules.get(); this.langs = []; + this.vnAuth = vnAuth; for (let code of $translate.getAvailableLanguageKeys()) { this.langs.push({ @@ -41,7 +42,7 @@ export default class MainMenu { } onLogoutClick() { - this.$window.location = '/logout'; + this.vnAuth.logout(); } onChangeLangClick(lang) { @@ -52,7 +53,7 @@ export default class MainMenu { this.getCurrentUserName(); } } -MainMenu.$inject = ['$translate', '$scope', '$http', '$window', 'modulesFactory']; +MainMenu.$inject = ['$translate', '$scope', '$http', '$window', 'vnModules', 'vnAuth']; ngModule.component('vnMainMenu', { template: require('./main-menu.html'), diff --git a/front/salix/components/main-menu/main-menu.spec.js b/front/salix/components/main-menu/main-menu.spec.js index 51817106c..8d3e44abf 100644 --- a/front/salix/components/main-menu/main-menu.spec.js +++ b/front/salix/components/main-menu/main-menu.spec.js @@ -7,9 +7,9 @@ describe('Component vnMainMenu', () => { beforeEach(ngModule('salix')); beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => { - let modulesFactory = {getModules: () => {}}; + let vnModules = {get: () => {}}; $httpBackend = _$httpBackend_; - controller = $componentController('vnMainMenu', {modulesFactory}); + controller = $componentController('vnMainMenu', {vnModules}); })); describe('getCurrentUserName()', () => { diff --git a/front/salix/components/main-menu/style.scss b/front/salix/components/main-menu/style.scss index 2719f7927..6c4d4e5fa 100644 --- a/front/salix/components/main-menu/style.scss +++ b/front/salix/components/main-menu/style.scss @@ -1,31 +1,40 @@ @import "effects"; vn-main-menu { - #user { - display: inline-block; - padding-right: 0.2em; - margin-bottom: 8px; - height: 2.5em; - vertical-align: middle; - } - #user h6{ - color: white; - } - & > div > vn-icon, & > div > a > vn-icon { - font-size: 2.2em; - cursor: pointer; + display: flex; + align-items: center; + justify-content: flex-end; - &:hover { - color: $main-01; + & > div { + display: flex; + align-items: center; + box-sizing: border-box; + + & > * { + cursor: pointer; + padding-left: .1em; + + &:hover { + color: $main-01; + } } - } - + & > #user { + vertical-align: middle; + font-weight: bold; + padding-right: .6em; + } + & > vn-icon, + & > a > vn-icon { + display: block; + font-size: 2.2em; + } + } vn-menu.vn-popover > div > div.content > ul { list-style-type: none; margin: 0; color: white; - li { + & > li { @extend %clickable-light; background-color: $main-01; margin-bottom: .6em; @@ -33,13 +42,13 @@ vn-main-menu { border-radius: .1em; min-width: 8em; + &:last-child { + margin-bottom: 0; + } & > vn-icon { padding-right: .3em; vertical-align: middle; } - &:last-child { - margin-bottom: 0; - } } } } \ No newline at end of file diff --git a/front/salix/components/topbar/style.scss b/front/salix/components/topbar/style.scss index 360018e01..7804f7d69 100644 --- a/front/salix/components/topbar/style.scss +++ b/front/salix/components/topbar/style.scss @@ -1,6 +1,16 @@ -header { +@import "background"; + +vn-topbar { display: flex; - flex-direction: row; - flex: 1; - color: #fff; + color: white; + box-sizing: border-box; + background-color: $bg-dark-bar; + align-items: center; + + & > header { + height: inherit; + width: inherit; + display: flex; + align-items: center; + } } diff --git a/front/salix/components/topbar/topbar.html b/front/salix/components/topbar/topbar.html index 0a0498ca6..faeb0c65e 100644 --- a/front/salix/components/topbar/topbar.html +++ b/front/salix/components/topbar/topbar.html @@ -1,2 +1,2 @@ -
+
\ No newline at end of file diff --git a/front/salix/config-routes.js b/front/salix/config-routes.js deleted file mode 100644 index 40f2cb58f..000000000 --- a/front/salix/config-routes.js +++ /dev/null @@ -1,78 +0,0 @@ -import ngModule from './module'; -import deps from 'modules.yml'; -import modules from 'spliting'; -import splitingRegister from 'core/lib/spliting-register'; - -function loader(moduleName, validations) { - load.$inject = ['vnModuleLoader']; - function load(moduleLoader) { - return moduleLoader.load(moduleName, validations); - } - return load; -} - -config.$inject = ['$stateProvider', '$urlRouterProvider', 'aclServiceProvider', 'modulesFactoryProvider']; -function config($stateProvider, $urlRouterProvider, aclServiceProvider, modulesFactory) { - splitingRegister.graph = deps; - splitingRegister.modules = modules; - - let aclService = aclServiceProvider.$get(); - - function getParams(route) { - let params = ''; - let temporalParams = []; - - if (!route.params) - return params; - - - Object.keys(route.params).forEach(key => { - temporalParams.push(`${key} = "${route.params[key]}"`); - }); - return temporalParams.join(' '); - } - - $urlRouterProvider.otherwise('/'); - - $stateProvider.state('home', { - url: '/', - template: '', - description: 'Salix' - }); - - for (let file in window.routes) { - let fileRoutes = window.routes[file].routes; - let moduleName = window.routes[file].module; - let validations = window.routes[file].validations || false; - let mainModule = modulesFactory.$get().getMainRoute(fileRoutes); - - if (mainModule) { - let count = fileRoutes.length; - for (let i = 0; i < count; i++) { - let route = fileRoutes[i]; - if (aclService.routeHasPermission(route)) { - let configRoute = { - url: route.url, - template: `<${route.component} ${getParams(route)}>`, - description: route.description, - reloadOnSearch: false, - resolve: { - loader: loader(moduleName, validations) - }, - data: { - moduleIndex: file - } - }; - if (route.abstract) - configRoute.abstract = true; - if (route.routeParams) - configRoute.params = route.routeParams; - - $stateProvider.state(route.state, configRoute); - } else if (route.state === mainModule.state) - break; - } - } - } -} -ngModule.config(config); diff --git a/front/salix/index.js b/front/salix/index.js index 88ff6e001..ca19b465d 100644 --- a/front/salix/index.js +++ b/front/salix/index.js @@ -1,5 +1,5 @@ import './module'; -import './config-routes'; +import './routes'; import './components/index'; +import './lib/index'; import './styles/index'; -import './modules-factory'; diff --git a/front/salix/global-keybindings.yml b/front/salix/keybindings.yml similarity index 100% rename from front/salix/global-keybindings.yml rename to front/salix/keybindings.yml diff --git a/front/salix/lib/auth.js b/front/salix/lib/auth.js new file mode 100644 index 000000000..f3e5d76ff --- /dev/null +++ b/front/salix/lib/auth.js @@ -0,0 +1,105 @@ +import ngModule from '../module'; +import UserError from 'core/lib/user-error'; + +/** + * Authentication service. + * + * @property {Boolean} loggedIn Whether the user is currently logged + */ +export default class Auth { + constructor($http, $state, $transitions, $window, vnToken, vnModules, aclService) { + Object.assign(this, { + $http, + $state, + $transitions, + $window, + vnToken, + vnModules, + aclService, + token: null, + loggedIn: false, + dataLoaded: false + }); + } + initialize() { + this.loggedIn = this.vnToken.token != null; + + let criteria = { + to: state => state.name != 'login' + }; + this.$transitions.onStart(criteria, transition => { + if (!this.loggedIn) { + let params = {continue: this.$window.location.hash}; + return transition.router.stateService.target('login', params); + } + if (!this.dataLoaded) { + this.resetData(); + this.dataLoaded = true; + return this.aclService.load(); + } + + return true; + }); + } + login(user, password, remember) { + if (!user) + throw new UserError('Please insert your user and password'); + + let params = { + user, + password: password || undefined + }; + return this.$http.post('/api/Accounts/login', params).then( + json => this.onLoginOk(json, remember), + json => this.onLoginErr(json) + ); + } + onLoginOk(json, remember) { + this.resetData(); + this.vnToken.set(json.data.token, remember); + this.loggedIn = true; + + let continueHash = this.$state.params.continue; + + if (continueHash) + this.$window.location = continueHash; + else + this.$state.go('home'); + } + onLoginErr(json) { + let message; + + switch (json.status) { + case 401: + message = 'Invalid credentials'; + break; + case -1: + message = 'Can\'t contact with server'; + break; + default: + message = 'Something went wrong'; + } + + throw new UserError(message); + } + logout() { + let promise = this.$http.post('/api/Accounts/logout', null, { + headers: {Authorization: this.vnToken.token} + }); + + this.vnToken.unset(); + this.loggedIn = false; + this.resetData(); + this.$state.go('login'); + + return promise; + } + resetData() { + this.aclService.reset(); + this.vnModules.reset(); + this.dataLoaded = false; + } +} +Auth.$inject = ['$http', '$state', '$transitions', '$window', 'vnToken', 'vnModules', 'aclService']; + +ngModule.service('vnAuth', Auth); diff --git a/front/salix/lib/index.js b/front/salix/lib/index.js new file mode 100644 index 000000000..f9ca51b3f --- /dev/null +++ b/front/salix/lib/index.js @@ -0,0 +1,4 @@ +import './auth'; +import './token'; +import './modules'; +import './interceptor'; diff --git a/front/core/lib/interceptor.js b/front/salix/lib/interceptor.js similarity index 72% rename from front/core/lib/interceptor.js rename to front/salix/lib/interceptor.js index 9ccb26191..23e065392 100644 --- a/front/core/lib/interceptor.js +++ b/front/salix/lib/interceptor.js @@ -1,16 +1,14 @@ import ngModule from '../module'; -import HttpError from './http-error'; +import HttpError from 'core/lib/http-error'; -interceptor.$inject = ['$q', 'vnApp', '$cookies', '$translate']; -function interceptor($q, vnApp, $cookies, $translate) { +interceptor.$inject = ['$q', 'vnApp', 'vnToken', '$translate']; +function interceptor($q, vnApp, vnToken, $translate) { return { request: function(config) { vnApp.pushLoader(); - let token = $cookies.get('vnToken'); - - if (token) - config.headers.Authorization = token; + if (vnToken.token) + config.headers.Authorization = vnToken.token; if ($translate.use()) config.headers['Accept-Language'] = $translate.use(); diff --git a/front/salix/lib/modules.js b/front/salix/lib/modules.js new file mode 100644 index 000000000..f44859d5d --- /dev/null +++ b/front/salix/lib/modules.js @@ -0,0 +1,46 @@ +import ngModule from '../module'; +import {getMainRoute} from '../routes'; +import keybindings from '../keybindings.yml'; + +export default class Modules { + constructor(aclService, $window) { + Object.assign(this, { + aclService, + $window + }); + } + reset() { + this.modules = null; + } + get() { + if (this.modules) + return this.modules; + + this.modules = []; + for (let mod of this.$window.routes) { + if (!mod || !mod.routes) continue; + + let route = getMainRoute(mod.routes); + if (!route || (route.acl && !this.aclService.hasAny(route.acl))) + continue; + + let keyBind; + if (keybindings) { + let res = keybindings.find(i => i.sref == route.state); + if (res) keyBind = res.key.toUpperCase(); + } + + this.modules.push({ + name: mod.name || mod.module, + icon: mod.icon || null, + route, + keyBind + }); + } + + return this.modules; + } +} +Modules.$inject = ['aclService', '$window']; + +ngModule.service('vnModules', Modules); diff --git a/front/salix/lib/token.js b/front/salix/lib/token.js new file mode 100644 index 000000000..15585a5fc --- /dev/null +++ b/front/salix/lib/token.js @@ -0,0 +1,51 @@ +import ngModule from '../module'; + +/** + * Saves and loads the token for the current logged in user. + * + * @property {String} token The current login token or %null + */ +export default class Token { + constructor($cookies) { + this.$cookies = $cookies; + + try { + this.token = sessionStorage.getItem('vnToken'); + if (!this.token) + this.token = localStorage.getItem('vnToken'); + } catch (e) {} + + if (!this.token) + this.token = this.$cookies.get('vnToken'); + } + set(value, remember) { + this.unset(); + try { + if (remember) + localStorage.setItem('vnToken', value); + else + sessionStorage.setItem('vnToken', value); + } catch (e) { + let options = {}; + + if (location.protocol == 'https:') + options.secure = true; + if (remember) { + let now = new Date().getTime(); + options.expires = new Date(now + 7 * 86400000); + } + + this.$cookies.put('vnToken', value, options); + } + this.token = value; + } + unset() { + localStorage.removeItem('vnToken'); + sessionStorage.removeItem('vnToken'); + this.$cookies.remove('vnToken'); + this.token = null; + } +} +Token.$inject = ['$cookies']; + +ngModule.service('vnToken', Token); diff --git a/front/salix/module.js b/front/salix/module.js index 1f153d2d0..50b49849e 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -1,17 +1,19 @@ import {ng} from 'core/vendor'; import 'core'; -import keybindings from './global-keybindings.yml'; +import keybindings from './keybindings.yml'; export const appName = 'salix'; const ngModule = ng.module('salix', ['vnCore']); export default ngModule; -run.$inject = ['$window', '$rootScope', 'vnApp', '$state', '$document']; -export function run($window, $rootScope, vnApp, $state, $document) { +run.$inject = ['$window', '$rootScope', 'vnAuth', 'vnApp', '$state', '$document']; +export function run($window, $rootScope, vnAuth, vnApp, $state, $document) { $window.validations = {}; vnApp.name = appName; + vnAuth.initialize(); + $rootScope.$on('$viewContentLoaded', () => {}); window.myAppErrorLog = []; $state.defaultErrorHandler(function(error) { @@ -40,7 +42,6 @@ export function run($window, $rootScope, vnApp, $state, $document) { if (correctShortcut) { $state.go(keybindings[binding].sref); e.preventDefault(); - e.stopImmediatePropagation(); } }); } @@ -56,8 +57,8 @@ ngModule.config(config); // Unhandled exceptions -$exceptionHandler.$inject = ['vnApp', '$window']; -function $exceptionHandler(vnApp, $window) { +$exceptionHandler.$inject = ['vnApp', '$window', '$state']; +function $exceptionHandler(vnApp, $window, $state) { return function(exception, cause) { let message; @@ -77,11 +78,9 @@ function $exceptionHandler(vnApp, $window) { else message = `${exception.status}: ${exception.statusText}`; - if (exception.status === 401) { - let location = $window.location; - let continueUrl = location.pathname + location.search + location.hash; - continueUrl = encodeURIComponent(continueUrl); - $window.location = `/auth/?apiKey=${vnApp.name}&continue=${continueUrl}`; + if (exception.status === 401 && $state.current.name != 'login') { + let params = {continue: $window.location.hash}; + $state.go('login', params); } } else if (exception.name == 'UserError') message = exception.message; diff --git a/front/salix/modules-factory.js b/front/salix/modules-factory.js deleted file mode 100644 index 1c199c4c4..000000000 --- a/front/salix/modules-factory.js +++ /dev/null @@ -1,36 +0,0 @@ -import ngModule from './module'; - -function modulesFactory(aclService) { - function getMainRoute(routeCollection) { - let cant = routeCollection.length; - for (let i = 0; i < cant; i++) { - if (!routeCollection[i].abstract) - return routeCollection[i]; - } - return null; - } - - function getModules() { - let modules = []; - for (let file in window.routes) { - let card = { - name: routes[file].name || routes[file].module, - icon: routes[file].icon || null - }; - let mainRoute = getMainRoute(window.routes[file].routes); - if (mainRoute && aclService.routeHasPermission(mainRoute)) { - card.route = mainRoute; - modules.push(card); - } - } - return modules; - } - - return { - getModules: getModules, - getMainRoute: getMainRoute - }; -} -modulesFactory.$inject = ['aclService']; - -ngModule.factory('modulesFactory', modulesFactory); diff --git a/front/salix/routes.js b/front/salix/routes.js new file mode 100644 index 000000000..41e669e81 --- /dev/null +++ b/front/salix/routes.js @@ -0,0 +1,85 @@ +import ngModule from './module'; +import deps from 'modules.yml'; +import modules from 'spliting'; +import splitingRegister from 'core/lib/spliting-register'; + +export function getMainRoute(routes) { + for (let route of routes) { + if (!route.abstract) + return route; + } + return null; +} + +function loader(moduleName, validations) { + load.$inject = ['vnModuleLoader']; + function load(moduleLoader) { + return moduleLoader.load(moduleName, validations); + } + return load; +} + +config.$inject = ['$stateProvider', '$urlRouterProvider']; +function config($stateProvider, $urlRouterProvider) { + splitingRegister.graph = deps; + splitingRegister.modules = modules; + + $urlRouterProvider.otherwise('/'); + + $stateProvider.state('home', { + url: '/', + template: '', + description: 'Salix' + }); + $stateProvider.state('login', { + url: '/login?continue', + template: '', + description: 'Login' + }); + + function getParams(route) { + let params = ''; + let temporalParams = []; + + if (!route.params) + return params; + + + Object.keys(route.params).forEach(key => { + temporalParams.push(`${key} = "${route.params[key]}"`); + }); + return temporalParams.join(' '); + } + + for (let file in window.routes) { + let routeFile = window.routes[file]; + let fileRoutes = routeFile.routes; + let moduleName = routeFile.module; + let validations = routeFile.validations || false; + let mainModule = getMainRoute(fileRoutes); + + if (!mainModule) continue; + + for (let route of fileRoutes) { + let configRoute = { + url: route.url, + template: `<${route.component} ${getParams(route)}>`, + description: route.description, + reloadOnSearch: false, + resolve: { + loader: loader(moduleName, validations) + }, + data: { + moduleIndex: file + } + }; + if (route.abstract) + configRoute.abstract = true; + if (route.routeParams) + configRoute.params = route.routeParams; + + $stateProvider.state(route.state, configRoute); + } + } +} +ngModule.config(config); diff --git a/loopback/client/auth.ejs b/loopback/client/auth.ejs deleted file mode 100644 index 1fd6988ad..000000000 --- a/loopback/client/auth.ejs +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Salix - - - - <% for (let jsFile of assets) { %> - - <% } %> - - diff --git a/loopback/client/index.ejs b/loopback/client/index.ejs index 841de0de6..316179c17 100644 --- a/loopback/client/index.ejs +++ b/loopback/client/index.ejs @@ -3,7 +3,6 @@ - diff --git a/loopback/server/boot/auth.js b/loopback/server/boot/auth.js deleted file mode 100644 index 9e6032869..000000000 --- a/loopback/server/boot/auth.js +++ /dev/null @@ -1,94 +0,0 @@ -let url = require('url'); -let md5 = require('md5'); - -module.exports = function(app) { - let User = app.models.User; - let applications = app.get('applications'); - - app.get('/auth/', function(req, res) { - res.render('auth.ejs', { - assets: app.getWpAssets('auth') - }); - }); - - app.post('/auth/login', function(req, res) { - let body = req.body; - let user = body.user; - let password = body.password; - let syncOnFail = true; - let usesEmail = user && user.indexOf('@') !== -1; - - login(); - - function login() { - let loginInfo = {password: password}; - - if (usesEmail) - loginInfo.email = user; - else - loginInfo.username = user; - - User.login(loginInfo, 'user', loginCb); - } - function loginCb(err, token) { - if (err) { - if (syncOnFail && !usesEmail) { - syncOnFail = false; - let filter = {where: {name: user}}; - app.models.Account.findOne(filter, findCb); - } else - badLogin(); - return; - } - - let apiKey; - let continueUrl; - - try { - let query = url.parse(req.body.location, true).query; - apiKey = query.apiKey; - continueUrl = query.continue; - } catch (e) { - continueUrl = null; - } - - if (!apiKey) apiKey = 'default'; - let loginUrl = applications[apiKey] || '/login'; - - res.json({ - token: token.id, - continue: continueUrl, - loginUrl: loginUrl - }); - } - function findCb(err, instance) { - if (err || !instance || instance.password !== md5(password)) { - badLogin(); - return; - } - - let where = {id: instance.id}; - let userData = { - id: instance.id, - username: user, - password: password, - email: instance.email, - created: instance.created, - updated: instance.updated - }; - User.upsertWithWhere(where, userData, login); - } - function badLogin() { - res.status(401); - res.json({ - message: 'Login failed' - }); - } - }); - - app.get('/auth/logout', function(req, res) { - User.logout(req.accessToken.id, () => { - res.redirect('/'); - }); - }); -}; diff --git a/loopback/server/boot/salix.js b/loopback/server/boot/salix.js index c6c3e1e82..e9ee7f9a0 100644 --- a/loopback/server/boot/salix.js +++ b/loopback/server/boot/salix.js @@ -1,146 +1,10 @@ module.exports = function(app) { - let models = app.models; let bootTimestamp = new Date().getTime(); app.get('/', function(req, res) { - let token = req.cookies.vnToken; - validateToken(token, function(isValid) { - if (!isValid) { - redirectToAuth(res, req.get('origin')); - return; - } - - res.render('index.ejs', { - assets: app.getWpAssets('salix'), - version: bootTimestamp - }); + res.render('index.ejs', { + assets: app.getWpAssets('salix'), + version: bootTimestamp }); }); - - app.get('/acl', function(req, res) { - let token = req.cookies.vnToken; - validateToken(token, function(isValid, token) { - if (isValid) - sendUserRole(res, token); - else - sendACL(res, {}); - }); - }); - - app.get('/login', function(req, res) { - let token = req.query.token; - let continueUrl = req.query.continue; - - validateToken(token, function(isValid) { - if (isValid) { - res.cookie('vnToken', token /* , {httpOnly: true} */); - res.redirect(continueUrl ? continueUrl : '/'); - } else - redirectToAuth(res); - }); - }); - - app.get('/logout', function(req, res) { - let token = req.cookies.vnToken; - models.User.logout(token, function() { - redirectToAuth(res); - }); - }); - - app.get('/validateToken', function(req, res) { - let token = req.headers.authorization; - - validateToken(token, function(isValid) { - if (isValid) - res.json(null); - else { - res.status(401).json({ - message: 'Invalid token' - }); - } - }); - }); - - function validateToken(tokenId, cb) { - models.AccessToken.findById(tokenId, function(err, token) { - if (token) { - token.validate(function(err, isValid) { - cb(isValid === true, token); - }); - } else - cb(false); - }); - } - - function sendUserRole(res, token) { - if (token.userId) { - let query = { - where: { - principalId: token.userId, - principalType: 'USER' - }, - include: [{ - relation: 'role', - scope: { - fields: ['name'] - } - }] - }; - models.RoleMapping.find(query, function(_, roles) { - if (roles) { - let acl = { - userProfile: {}, - roles: {} - }; - Object.keys(roles).forEach(function(_, i) { - if (roles[i].roleId) { - let rol = roles[i].role(); - acl.roles[rol.name] = true; - } - }); - models.User.findById(token.userId, function(_, userProfile) { - // acl.userProfile = userProfile; - if (userProfile && userProfile.id) { - acl.userProfile.id = userProfile.id; - acl.userProfile.username = userProfile.username; - acl.userProfile.warehouseId = 1; - sendACL(res, acl); - } else - sendACL(res, {}); - }); - } else - sendACL(res, {}); - }); - } else - sendACL(res, {}); - } - - function redirectToAuth(res, continueUrl) { - let authUrl = app.get('url auth'); - let params = { - apiKey: app.get('api key'), - continue: continueUrl - }; - res.clearCookie('vnToken'); - res.redirect(`${authUrl}/?${encodeUri(params)}`); - } - - function sendACL(res, acl) { - let aclStr = JSON.stringify(acl); - res.header('Content-Type', 'application/javascript; charset=UTF-8'); - res.send(`(function(window){window.salix = window.salix || {}; window.salix.acl = window.salix.acl || {}; window.salix.acl = ${aclStr}; })(window)`); - } }; - -function encodeUri(object) { - let uri = ''; - for (let key in object) { - if (object[key]) { - if (uri.length > 0) - uri += '&'; - uri += encodeURIComponent(key) + '='; - uri += encodeURIComponent(object[key]); - } - } - return uri; -} diff --git a/loopback/server/boot/specs/auth.spec.js b/loopback/server/boot/specs/auth.spec.js deleted file mode 100644 index 95018d837..000000000 --- a/loopback/server/boot/specs/auth.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -const app = require('../../../server/server'); -const routes = require('../auth'); - -describe('Auth routes', () => { - beforeEach(async () => { - await app.models.User.destroyById(102); - }); - - afterAll(async () => { - await app.models.User.destroyById(102); - }); - - let User = app.models.User; - let loginFunction; - let logoutFunction; - let res; - let req; - - beforeEach(() => { - spyOn(app, 'post'); - spyOn(app, 'get').and.callThrough(); - routes(app); - loginFunction = app.post.calls.mostRecent().args[1]; - logoutFunction = app.get.calls.argsFor(2)[1]; - res = {}; - req = {body: {}}; - }); - - describe('when the user doesnt exist but the client does and the password is correct', () => { - it('should create the user login and return the token', done => { - spyOn(User, 'upsertWithWhere').and.callThrough(); - req.body.user = 'PetterParker'; - req.body.password = 'nightmare'; - res.json = response => { - expect(User.upsertWithWhere).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Object), jasmine.any(Function)); - expect(response.token).toBeDefined(); - done(); - }; - loginFunction(req, res); - }); - }); - - describe('when the user exists and the password is correct', () => { - it('should login and return the token', done => { - req.body.user = 'employee'; - req.body.password = 'nightmare'; - res.json = response => { - expect(response.token).toBeDefined(); - done(); - }; - loginFunction(req, res); - }); - - it('should define the url to continue upon login', done => { - req.body.user = 'employee'; - req.body.password = 'nightmare'; - req.body.location = 'http://localhost:5000/auth/?apiKey=salix&continue="continueURL"'; - res.json = response => { - expect(response.continue).toBeDefined(); - done(); - }; - loginFunction(req, res); - }); - - it('should define the loginUrl upon login', done => { - req.body.user = 'employee'; - req.body.password = 'nightmare'; - req.body.location = 'http://localhost:5000/auth/?apiKey=salix'; - res.json = response => { - expect(response.loginUrl).toBeDefined(); - done(); - }; - loginFunction(req, res); - }); - - it('should logout after login', done => { - spyOn(User, 'logout').and.callThrough(); - req.accessToken = {id: 'testingTokenId'}; - logoutFunction(req, res); - res.redirect = url => { - expect(User.logout).toHaveBeenCalledWith('testingTokenId', jasmine.any(Function)); - expect(url).toBe('/'); - done(); - }; - }); - }); - - describe('when the user is incorrect', () => { - it('should return a 401 unauthorized', done => { - req.body.user = 'IDontExist'; - req.body.password = 'TotallyWrongPassword'; - res.status = status => { - expect(status).toBe(401); - }; - - res.json = response => { - expect(response.message).toBe('Login failed'); - done(); - }; - loginFunction(req, res); - }); - }); -}); diff --git a/modules/client/front/risk/index/index.js b/modules/client/front/risk/index/index.js index 9f95ddcf0..5744c6fb1 100644 --- a/modules/client/front/risk/index/index.js +++ b/modules/client/front/risk/index/index.js @@ -2,12 +2,12 @@ import ngModule from '../../module'; import './style.scss'; class Controller { - constructor($stateParams, $translate, $scope, $cookies) { + constructor($stateParams, $translate, $scope, vnToken) { this.$ = $scope; this.$stateParams = $stateParams; this.$translate = $translate; - this.accessToken = $cookies.get('vnToken'); + this.accessToken = vnToken.token; this.companyFk = window.localStorage.defaultCompanyFk; this.filter = { include: { @@ -57,7 +57,7 @@ class Controller { } } -Controller.$inject = ['$stateParams', '$translate', '$scope', '$cookies']; +Controller.$inject = ['$stateParams', '$translate', '$scope', 'vnToken']; ngModule.component('vnClientRiskIndex', { template: require('./index.html'), diff --git a/package-lock.json b/package-lock.json index 467dfffe8..e142ac1b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1271,7 +1271,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1564,7 +1564,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1883,7 +1883,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -1963,6 +1963,436 @@ "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=" }, + "bcrypt": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.3.tgz", + "integrity": "sha512-4EuzUo6K790QC3uq/ogzy9w2Hc7XDIBoEndU5y7l7YaEAwQF8vyFqv6tC30+gOBZvyxk3F632xzKBQoLNz2pjg==", + "requires": { + "nan": "2.12.1", + "node-pre-gyp": "0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.4", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "minizlib": { + "version": "1.1.1", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true + }, + "npm-packlist": { + "version": "1.1.12", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.5", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "sax": { + "version": "1.2.4", + "bundled": true + }, + "semver": { + "version": "5.6.0", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -2480,7 +2910,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -2727,7 +3157,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -2887,7 +3317,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -4019,7 +4449,7 @@ "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha1-HxngwuGqDjJ5fEl5nyg3rGr2nFc=", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { "is-obj": "^1.0.0" @@ -4179,7 +4609,7 @@ "dependencies": { "fs-extra": { "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", "dev": true, "requires": { @@ -4192,7 +4622,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -5224,7 +5654,7 @@ }, "file-loader": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -6224,7 +6654,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -6489,7 +6919,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -6654,7 +7084,7 @@ "dependencies": { "es6-promise": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", "dev": true }, @@ -7436,7 +7866,7 @@ "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -7675,7 +8105,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -7711,7 +8141,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -7754,7 +8184,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -7855,14 +8285,14 @@ }, "jasmine-core": { "version": "2.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "resolved": "http://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", "dev": true }, "jasmine-spec-reporter": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", - "integrity": "sha1-HWMq7ANBZwrTJPkrqEtLMrNeniI=", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", "dev": true, "requires": { "colors": "1.1.2" @@ -8150,7 +8580,7 @@ "karma-chrome-launcher": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha1-zxudBxNswY/iOTJ9JGVMPbw2is8=", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", "dev": true, "requires": { "fs-access": "^1.0.0", @@ -9982,7 +10412,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "minstache": { @@ -10364,7 +10794,7 @@ }, "named-placeholders": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz", "integrity": "sha1-O3oNJiA910s6nfTJz7gnsvuQfmQ=", "dev": true, "requires": { @@ -10373,7 +10803,7 @@ "dependencies": { "lru-cache": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz", + "resolved": "http://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz", "integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus=", "dev": true } @@ -10382,8 +10812,7 @@ "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "dev": true + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" }, "nanoid": { "version": "2.0.0", @@ -11354,7 +11783,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -13405,7 +13834,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -13529,7 +13958,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -13669,7 +14098,7 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -13856,7 +14285,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { "define-property": "^1.0.0", @@ -13907,7 +14336,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -14252,7 +14681,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -14261,7 +14690,7 @@ "split2": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", - "integrity": "sha1-GGsldbz4PoW30YRldWI47k7kJJM=", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", "dev": true, "requires": { "through2": "^2.0.2" @@ -14442,7 +14871,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -14794,7 +15223,7 @@ }, "style-loader": { "version": "0.20.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", + "resolved": "http://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", "dev": true, "requires": { @@ -15293,7 +15722,7 @@ "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha1-/jZfX3XsntTlaCXgu3bSSrdK+Ds=", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { "nopt": "~1.0.10" @@ -15792,7 +16221,7 @@ "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -16514,7 +16943,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -16527,7 +16956,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -16915,7 +17344,7 @@ "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha1-H/YVdcLipOjlENb6TiQ8zhg5mas=", + "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { "graceful-fs": "^4.1.11", diff --git a/package.json b/package.json index 4db2d3e19..3c028788c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "description": "Salix application", "license": "GPL-3.0", "dependencies": { + "bcrypt": "^3.0.3", "compression": "^1.7.3", "cookie-parser": "^1.4.3", "cors": "^2.8.5", diff --git a/webpack.config.js b/webpack.config.js index 864748031..b8c3521c0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -81,11 +81,6 @@ let baseConfig = { template: 'front/salix/index.ejs', filename: 'index.html', chunks: ['salix'] - }), - new HtmlWebpackPlugin({ - template: 'front/auth/auth.ejs', - filename: 'auth.html', - chunks: ['auth'] }) ], devtool: 'source-map', diff --git a/webpack.config.yml b/webpack.config.yml index c2d98af0d..31764fbaa 100644 --- a/webpack.config.yml +++ b/webpack.config.yml @@ -2,6 +2,5 @@ buildDir: dist devServerPort: 3500 publicPath: '/static' entry: { - salix: salix, - auth: auth + salix: salix } \ No newline at end of file From 30749f0f4d8f3118541aa333db13921eac88552b Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Wed, 23 Jan 2019 17:49:28 +0100 Subject: [PATCH 2/4] #981 Back & front tests passed, keybinds on it's own module routes.json --- back/methods/account/specs/login.spec.js | 46 +++++------- front/core/index.js | 1 + front/core/lib/get-main-route.js | 8 +++ front/core/lib/index.js | 1 + front/core/lib/specs/acl-service.spec.js | 72 ++++++++++--------- front/{salix/lib => core/services}/auth.js | 0 front/{salix/lib => core/services}/index.js | 0 .../lib => core/services}/interceptor.js | 0 front/{salix/lib => core/services}/modules.js | 7 +- front/{salix/lib => core/services}/token.js | 0 front/salix/index.js | 5 +- front/salix/keybindings.yml | 6 -- front/salix/module.js | 42 ++++++----- front/salix/routes.js | 9 +-- modules/claim/front/routes.json | 3 + modules/client/front/risk/index/index.spec.js | 2 +- modules/client/front/routes.json | 3 + modules/item/front/routes.json | 3 + modules/ticket/front/routes.json | 3 + 19 files changed, 110 insertions(+), 101 deletions(-) create mode 100644 front/core/lib/get-main-route.js rename front/{salix/lib => core/services}/auth.js (100%) rename front/{salix/lib => core/services}/index.js (100%) rename front/{salix/lib => core/services}/interceptor.js (100%) rename front/{salix/lib => core/services}/modules.js (84%) rename front/{salix/lib => core/services}/token.js (100%) delete mode 100644 front/salix/keybindings.yml diff --git a/back/methods/account/specs/login.spec.js b/back/methods/account/specs/login.spec.js index 1768557ff..6a9143790 100644 --- a/back/methods/account/specs/login.spec.js +++ b/back/methods/account/specs/login.spec.js @@ -1,45 +1,33 @@ const app = require(`${serviceRoot}/server/server`); describe('account login()', () => { - it('when the user doesnt exist but the client does and the password is correct', async() => { - let response = await app.models.Account.login('PetterParker', 'nightmare'); - - expect(response.token).toBeDefined(); - }); - - describe('when the user exists and the password is correct', () => { - it('should login and return the token', async() => { + describe('when credentials are correct', () => { + it('should return the token', async() => { let response = await app.models.Account.login('employee', 'nightmare'); expect(response.token).toBeDefined(); }); - it('should define the url to continue upon login', async() => { - let location = 'http://localhost/auth/?apiKey=salix&continue=continueURL'; - let response = await app.models.Account.login('employee', 'nightmare', location); + it('should return the token if the user doesnt exist but the client does', async() => { + let response = await app.models.Account.login('PetterParker', 'nightmare'); - expect(response.continue).toBe('continueURL'); - }); - - it('should define the loginUrl upon login', async() => { - let location = 'http://localhost/auth/?apiKey=salix'; - let response = await app.models.Account.login('employee', 'nightmare', location); - - expect(response.loginUrl).toBeDefined(); + expect(response.token).toBeDefined(); }); }); - it('should throw a 401 error when credentials are incorrect', async() => { - let error; + describe('when credentials are incorrect', () => { + it('should throw a 401 error', async() => { + let error; - try { - await app.models.Account.login('IDontExist', 'TotallyWrongPassword'); - } catch (e) { - error = e; - } + try { + await app.models.Account.login('IDontExist', 'TotallyWrongPassword'); + } catch (e) { + error = e; + } - expect(error).toBeDefined(); - expect(error.statusCode).toBe(401); - expect(error.code).toBe('LOGIN_FAILED'); + expect(error).toBeDefined(); + expect(error.statusCode).toBe(401); + expect(error.code).toBe('LOGIN_FAILED'); + }); }); }); diff --git a/front/core/index.js b/front/core/index.js index d3601a8e1..fdcfaff05 100644 --- a/front/core/index.js +++ b/front/core/index.js @@ -4,4 +4,5 @@ export * from './module'; export * from './directives'; export * from './filters'; export * from './lib'; +export * from './services'; export * from './components'; diff --git a/front/core/lib/get-main-route.js b/front/core/lib/get-main-route.js new file mode 100644 index 000000000..52a86ada1 --- /dev/null +++ b/front/core/lib/get-main-route.js @@ -0,0 +1,8 @@ + +export default function getMainRoute(routes) { + for (let route of routes) { + if (!route.abstract) + return route; + } + return null; +} diff --git a/front/core/lib/index.js b/front/core/lib/index.js index 9ea493c64..a50603d4c 100644 --- a/front/core/lib/index.js +++ b/front/core/lib/index.js @@ -12,3 +12,4 @@ import './modified'; import './key-codes'; import './http-error'; import './user-error'; +import './get-main-route'; diff --git a/front/core/lib/specs/acl-service.spec.js b/front/core/lib/specs/acl-service.spec.js index f29f4bf2d..63fe30f93 100644 --- a/front/core/lib/specs/acl-service.spec.js +++ b/front/core/lib/specs/acl-service.spec.js @@ -3,46 +3,54 @@ describe('Service acl', () => { beforeEach(ngModule('vnCore')); - beforeEach(ngModule($provide => { - $provide.value('aclConstant', {}); - })); - - beforeEach(inject(_aclService_ => { + beforeEach(inject((_aclService_, $httpBackend) => { + $httpBackend.when('GET', `/api/Accounts/acl`).respond({ + roles: [ + {role: {name: 'foo'}}, + {role: {name: 'bar'}}, + {role: {name: 'baz'}} + ] + }); aclService = _aclService_; + aclService.load(); + $httpBackend.flush(); })); - it('should return false as the service doesn\'t have roles', () => { - expect(aclService.routeHasPermission('http://www.verdnatura.es')).toBeFalsy(); + describe('load()', () => { + it('should load roles from backend', () => { + expect(aclService.roles).toEqual({ + foo: true, + bar: true, + baz: true + }); + }); }); - it('should return true as the service has roles but the route has no acl', () => { - aclService.roles = {customer: true}; + describe('hasAny()', () => { + it('should return true when user has any of the passed roles', () => { + let hasAny = aclService.hasAny(['foo', 'nonExistent']); - expect(aclService.routeHasPermission('http://www.verdnatura.es')).toBeTruthy(); + expect(hasAny).toBeTruthy(); + }); + + it('should return true when user has all the passed roles', () => { + let hasAny = aclService.hasAny(['bar', 'baz']); + + expect(hasAny).toBeTruthy(); + }); + + it('should return true when user has not any of the passed roles', () => { + let hasAny = aclService.hasAny(['inventedRole', 'nonExistent']); + + expect(hasAny).toBeFalsy(); + }); }); - it('should return false as the service roles have no length', () => { - aclService.roles = {}; - let route = {url: 'http://www.verdnatura.es', acl: []}; + describe('reset()', () => { + it('should reset the roles', () => { + aclService.reset(); - expect(aclService.routeHasPermission(route)).toBeFalsy(); - }); - - it('should call the service hasAny() function and return false as the service has roles and the rote has acl without length', () => { - aclService.roles = {customer: true, employee: true}; - let route = {url: 'http://www.verdnatura.es', acl: []}; - spyOn(aclService, 'hasAny').and.callThrough(); - - expect(aclService.routeHasPermission(route)).toBeFalsy(); - expect(aclService.hasAny).toHaveBeenCalledWith(route.acl); - }); - - it('should call the service hasAny() function to return true as the service has roles matching with the ones in acl', () => { - aclService.roles = {customer: true, employee: true}; - let route = {url: 'http://www.verdnatura.es', acl: ['customer']}; - spyOn(aclService, 'hasAny').and.callThrough(); - - expect(aclService.routeHasPermission(route)).toBeTruthy(); - expect(aclService.hasAny).toHaveBeenCalledWith(route.acl); + expect(aclService.roles).toBeNull(); + }); }); }); diff --git a/front/salix/lib/auth.js b/front/core/services/auth.js similarity index 100% rename from front/salix/lib/auth.js rename to front/core/services/auth.js diff --git a/front/salix/lib/index.js b/front/core/services/index.js similarity index 100% rename from front/salix/lib/index.js rename to front/core/services/index.js diff --git a/front/salix/lib/interceptor.js b/front/core/services/interceptor.js similarity index 100% rename from front/salix/lib/interceptor.js rename to front/core/services/interceptor.js diff --git a/front/salix/lib/modules.js b/front/core/services/modules.js similarity index 84% rename from front/salix/lib/modules.js rename to front/core/services/modules.js index f44859d5d..b997df08c 100644 --- a/front/salix/lib/modules.js +++ b/front/core/services/modules.js @@ -1,6 +1,5 @@ import ngModule from '../module'; -import {getMainRoute} from '../routes'; -import keybindings from '../keybindings.yml'; +import getMainRoute from '../lib/get-main-route'; export default class Modules { constructor(aclService, $window) { @@ -25,8 +24,8 @@ export default class Modules { continue; let keyBind; - if (keybindings) { - let res = keybindings.find(i => i.sref == route.state); + if (mod.keybindings) { + let res = mod.keybindings.find(i => i.sref == route.state); if (res) keyBind = res.key.toUpperCase(); } diff --git a/front/salix/lib/token.js b/front/core/services/token.js similarity index 100% rename from front/salix/lib/token.js rename to front/core/services/token.js diff --git a/front/salix/index.js b/front/salix/index.js index ca19b465d..6788bae07 100644 --- a/front/salix/index.js +++ b/front/salix/index.js @@ -1,5 +1,4 @@ import './module'; import './routes'; -import './components/index'; -import './lib/index'; -import './styles/index'; +import './components'; +import './styles'; diff --git a/front/salix/keybindings.yml b/front/salix/keybindings.yml deleted file mode 100644 index dd625ea2f..000000000 --- a/front/salix/keybindings.yml +++ /dev/null @@ -1,6 +0,0 @@ -[ - {key: r, sref: claim.index}, - {key: c, sref: client.index}, - {key: a, sref: item.index}, - {key: t, sref: ticket.index}, -] \ No newline at end of file diff --git a/front/salix/module.js b/front/salix/module.js index 50b49849e..75bc18f2d 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -1,6 +1,5 @@ import {ng} from 'core/vendor'; import 'core'; -import keybindings from './keybindings.yml'; export const appName = 'salix'; @@ -21,29 +20,36 @@ export function run($window, $rootScope, vnAuth, vnApp, $state, $document) { window.myAppErrorLog.push(error); }); - for (const binding in keybindings) { - if (!keybindings[binding].key || !keybindings[binding].sref) - throw new Error('Binding not formed correctly'); + if ($window.routes) { + for (const mod of $window.routes) { + if (!mod || !mod.keybindings) + continue; - $document.on('keyup', function(e) { - if (e.defaultPrevented) return; + for (const binding of mod.keybindings) { + if (!binding.key || !binding.sref) + throw new Error('Binding not formed correctly'); - let shortcut = { - altKey: true, - ctrlKey: true, - key: keybindings[binding].key - }; + $document.on('keyup', function(e) { + if (e.defaultPrevented) return; - let correctShortcut = true; + let shortcut = { + altKey: true, + ctrlKey: true, + key: binding.key + }; - for (const key in shortcut) - correctShortcut = correctShortcut && shortcut[key] == e[key]; + let correctShortcut = true; - if (correctShortcut) { - $state.go(keybindings[binding].sref); - e.preventDefault(); + for (const key in shortcut) + correctShortcut = correctShortcut && shortcut[key] == e[key]; + + if (correctShortcut) { + $state.go(binding.sref); + e.preventDefault(); + } + }); } - }); + } } } ngModule.run(run); diff --git a/front/salix/routes.js b/front/salix/routes.js index 41e669e81..661e59a27 100644 --- a/front/salix/routes.js +++ b/front/salix/routes.js @@ -2,14 +2,7 @@ import ngModule from './module'; import deps from 'modules.yml'; import modules from 'spliting'; import splitingRegister from 'core/lib/spliting-register'; - -export function getMainRoute(routes) { - for (let route of routes) { - if (!route.abstract) - return route; - } - return null; -} +import getMainRoute from 'core/lib/get-main-route'; function loader(moduleName, validations) { load.$inject = ['vnModuleLoader']; diff --git a/modules/claim/front/routes.json b/modules/claim/front/routes.json index a418ef8aa..a8ae49f1f 100644 --- a/modules/claim/front/routes.json +++ b/modules/claim/front/routes.json @@ -78,5 +78,8 @@ {"state": "claim.card.detail", "icon": "icon-details"}, {"state": "claim.card.development", "icon": "icon-traceability"}, {"state": "claim.card.action", "icon": "icon-actions"} + ], + "keybindings": [ + {"key": "r", "sref": "claim.index"} ] } \ No newline at end of file diff --git a/modules/client/front/risk/index/index.spec.js b/modules/client/front/risk/index/index.spec.js index 925bcafe4..77f00c5e8 100644 --- a/modules/client/front/risk/index/index.spec.js +++ b/modules/client/front/risk/index/index.spec.js @@ -11,7 +11,7 @@ describe('Client', () => { beforeEach(angular.mock.inject((_$componentController_, $rootScope) => { $componentController = _$componentController_; $scope = $rootScope.$new(); - controller = $componentController('vnClientRiskIndex', {$scope: $scope}); + controller = $componentController('vnClientRiskIndex', {$scope}); })); describe('risks() setter', () => { diff --git a/modules/client/front/routes.json b/modules/client/front/routes.json index 808e5af41..c35c48b78 100644 --- a/modules/client/front/routes.json +++ b/modules/client/front/routes.json @@ -347,5 +347,8 @@ {"state": "client.card.webPayment", "icon": "icon-onlinepayment"} ] } + ], + "keybindings": [ + {"key": "c", "sref": "client.index"} ] } diff --git a/modules/item/front/routes.json b/modules/item/front/routes.json index f7ac8ebb7..bd83276b8 100644 --- a/modules/item/front/routes.json +++ b/modules/item/front/routes.json @@ -125,5 +125,8 @@ {"state": "item.card.itemBarcode", "icon": "icon-barcode"}, {"state": "item.card.diary", "icon": "icon-transaction"}, {"state": "item.card.last-entries", "icon": "icon-regentry"} + ], + "keybindings": [ + {"key": "a", "sref": "item.index"} ] } \ No newline at end of file diff --git a/modules/ticket/front/routes.json b/modules/ticket/front/routes.json index 59fd490bf..cce565342 100644 --- a/modules/ticket/front/routes.json +++ b/modules/ticket/front/routes.json @@ -240,5 +240,8 @@ {"state": "ticket.card.picture", "icon": "image"}, {"state": "ticket.card.log", "icon": "history"}, {"state": "ticket.card.request.index", "icon": "icon-100"} + ], + "keybindings": [ + {"key": "t", "sref": "ticket.index"} ] } \ No newline at end of file From 498ae5d4fe289918bc35e54a5198ae26764f69e3 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Thu, 24 Jan 2019 09:25:51 +0100 Subject: [PATCH 3/4] Keybindings bugs fixed --- front/core/services/modules.js | 2 +- front/salix/module.js | 49 +++++++++++++++++--------------- modules/claim/front/routes.json | 2 +- modules/client/front/routes.json | 2 +- modules/item/front/routes.json | 2 +- modules/ticket/front/routes.json | 2 +- 6 files changed, 31 insertions(+), 28 deletions(-) diff --git a/front/core/services/modules.js b/front/core/services/modules.js index b997df08c..2c1862fa8 100644 --- a/front/core/services/modules.js +++ b/front/core/services/modules.js @@ -25,7 +25,7 @@ export default class Modules { let keyBind; if (mod.keybindings) { - let res = mod.keybindings.find(i => i.sref == route.state); + let res = mod.keybindings.find(i => i.state == route.state); if (res) keyBind = res.key.toUpperCase(); } diff --git a/front/salix/module.js b/front/salix/module.js index 75bc18f2d..8f3a022da 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -6,8 +6,8 @@ export const appName = 'salix'; const ngModule = ng.module('salix', ['vnCore']); export default ngModule; -run.$inject = ['$window', '$rootScope', 'vnAuth', 'vnApp', '$state', '$document']; -export function run($window, $rootScope, vnAuth, vnApp, $state, $document) { +run.$inject = ['$window', '$rootScope', 'vnAuth', 'vnApp', '$state']; +export function run($window, $rootScope, vnAuth, vnApp, $state) { $window.validations = {}; vnApp.name = appName; @@ -21,35 +21,38 @@ export function run($window, $rootScope, vnAuth, vnApp, $state, $document) { }); if ($window.routes) { + let keybindings = {}; + for (const mod of $window.routes) { if (!mod || !mod.keybindings) continue; for (const binding of mod.keybindings) { - if (!binding.key || !binding.sref) - throw new Error('Binding not formed correctly'); + let err; + if (!binding.key) + err = `Missing attribute 'key' in binding`; + else if (!binding.state) + err = `Missing attribute 'state' in binding`; + else if (keybindings[binding.key]) + err = `Binding key redeclared`; - $document.on('keyup', function(e) { - if (e.defaultPrevented) return; - - let shortcut = { - altKey: true, - ctrlKey: true, - key: binding.key - }; - - let correctShortcut = true; - - for (const key in shortcut) - correctShortcut = correctShortcut && shortcut[key] == e[key]; - - if (correctShortcut) { - $state.go(binding.sref); - e.preventDefault(); - } - }); + if (err) + console.warn(`${err}: ${mod.module}: ${JSON.stringify(binding)}`); + else + keybindings[binding.key] = binding.state; } } + + $window.addEventListener('keyup', function(event) { + if (event.defaultPrevented || !event.altKey || !event.ctrlKey) + return; + + let state = keybindings[event.key]; + if (state) { + $state.go(state); + event.preventDefault(); + } + }); } } ngModule.run(run); diff --git a/modules/claim/front/routes.json b/modules/claim/front/routes.json index a8ae49f1f..3b7bbef3e 100644 --- a/modules/claim/front/routes.json +++ b/modules/claim/front/routes.json @@ -80,6 +80,6 @@ {"state": "claim.card.action", "icon": "icon-actions"} ], "keybindings": [ - {"key": "r", "sref": "claim.index"} + {"key": "r", "state": "claim.index"} ] } \ No newline at end of file diff --git a/modules/client/front/routes.json b/modules/client/front/routes.json index c35c48b78..cdaa6b7c1 100644 --- a/modules/client/front/routes.json +++ b/modules/client/front/routes.json @@ -349,6 +349,6 @@ } ], "keybindings": [ - {"key": "c", "sref": "client.index"} + {"key": "c", "state": "client.index"} ] } diff --git a/modules/item/front/routes.json b/modules/item/front/routes.json index bd83276b8..d2514107d 100644 --- a/modules/item/front/routes.json +++ b/modules/item/front/routes.json @@ -127,6 +127,6 @@ {"state": "item.card.last-entries", "icon": "icon-regentry"} ], "keybindings": [ - {"key": "a", "sref": "item.index"} + {"key": "a", "state": "item.index"} ] } \ No newline at end of file diff --git a/modules/ticket/front/routes.json b/modules/ticket/front/routes.json index cce565342..8c78de7d3 100644 --- a/modules/ticket/front/routes.json +++ b/modules/ticket/front/routes.json @@ -242,6 +242,6 @@ {"state": "ticket.card.request.index", "icon": "icon-100"} ], "keybindings": [ - {"key": "t", "sref": "ticket.index"} + {"key": "t", "state": "ticket.index"} ] } \ No newline at end of file From adfd69b39384291ec6bca48445a3e8eda35575f2 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Thu, 24 Jan 2019 10:57:43 +0100 Subject: [PATCH 4/4] #981 e2e passed --- e2e/helpers/config.js | 2 +- e2e/helpers/extensions.js | 6 +- e2e/helpers/selectors.js | 1 - .../client-module/02_edit_basic_data.spec.js | 1 - .../12_lock_of_verified_data.spec.js | 4 - e2e/paths/ticket-module/07_edit_sale.spec.js | 2 - front/core/directives/acl.js | 2 +- package-lock.json | 105 +++++++----------- 8 files changed, 48 insertions(+), 75 deletions(-) diff --git a/e2e/helpers/config.js b/e2e/helpers/config.js index d52f08b39..e5862d52c 100644 --- a/e2e/helpers/config.js +++ b/e2e/helpers/config.js @@ -1,3 +1,3 @@ export default { - url: 'http://localhost:5000/' + url: 'http://localhost:5000' }; diff --git a/e2e/helpers/extensions.js b/e2e/helpers/extensions.js index d5807ba0b..42318485c 100644 --- a/e2e/helpers/extensions.js +++ b/e2e/helpers/extensions.js @@ -25,13 +25,13 @@ Nightmare.asyncAction('clearInput', async function(selector) { let actions = { login: function(userName, done) { - this.goto(`${config.url}auth/?apiKey=salix`) + this.goto(`${config.url}#!/login`) .wait(`vn-login input[name=user]`) .write(`vn-login input[name=user]`, userName) .write(`vn-login input[name=password]`, 'nightmare') .click(`vn-login input[type=submit]`) - // FIXME: Wait for dom to be ready: https://github.com/segmentio/nightmare/issues/481 - .wait(1000) + // FIXME: Wait for dom to be ready: https://github.com/segmentio/nightmare/issues/481 + // .wait(1000) .then(() => { currentUser = userName; done(); diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index a7487acc0..daf92d000 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -2,7 +2,6 @@ import components from './components_selectors.js'; export default { globalItems: { - logOutButton: `#logout`, applicationsMenuButton: `#apps`, applicationsMenuVisible: `vn-main-menu [vn-id="apps-menu"] ul`, clientsButton: `vn-main-menu [vn-id="apps-menu"] ul > li[ui-sref="client.index"]`, diff --git a/e2e/paths/client-module/02_edit_basic_data.spec.js b/e2e/paths/client-module/02_edit_basic_data.spec.js index 220c0d186..c76846d8c 100644 --- a/e2e/paths/client-module/02_edit_basic_data.spec.js +++ b/e2e/paths/client-module/02_edit_basic_data.spec.js @@ -89,7 +89,6 @@ describe('Client Edit basicData path', () => { describe('as salesAssistant', () => { beforeAll(() => { nightmare - .waitToClick(selectors.globalItems.logOutButton) .loginAndModule('salesASsistant', 'client') .accessToSearchResult('Ptonomy Wallace') .accessToSection('client.card.basicData'); diff --git a/e2e/paths/client-module/12_lock_of_verified_data.spec.js b/e2e/paths/client-module/12_lock_of_verified_data.spec.js index 367753586..f7a62770a 100644 --- a/e2e/paths/client-module/12_lock_of_verified_data.spec.js +++ b/e2e/paths/client-module/12_lock_of_verified_data.spec.js @@ -48,7 +48,6 @@ describe('Client lock verified data path', () => { describe('as administrative', () => { beforeAll(() => { nightmare - .waitToClick(selectors.globalItems.logOutButton) .waitForLogin('administrative'); }); @@ -150,7 +149,6 @@ describe('Client lock verified data path', () => { describe('as salesPerson second run', () => { beforeAll(() => { nightmare - .waitToClick(selectors.globalItems.logOutButton) .waitForLogin('salesPerson'); }); @@ -219,7 +217,6 @@ describe('Client lock verified data path', () => { describe('as salesAssistant', () => { beforeAll(() => { nightmare - .waitToClick(selectors.globalItems.logOutButton) .waitForLogin('salesAssistant'); }); @@ -298,7 +295,6 @@ describe('Client lock verified data path', () => { describe('as salesPerson third run', () => { beforeAll(() => { nightmare - .waitToClick(selectors.globalItems.logOutButton) .waitForLogin('salesPerson'); }); diff --git a/e2e/paths/ticket-module/07_edit_sale.spec.js b/e2e/paths/ticket-module/07_edit_sale.spec.js index 9f6c8a748..3a5feff15 100644 --- a/e2e/paths/ticket-module/07_edit_sale.spec.js +++ b/e2e/paths/ticket-module/07_edit_sale.spec.js @@ -475,7 +475,6 @@ describe('Ticket Edit sale path', () => { it('should log in as Production role and go to the ticket index', async() => { const url = await nightmare - .waitToClick(selectors.globalItems.logOutButton) .waitForLogin('production') .waitToClick(selectors.globalItems.applicationsMenuButton) .wait(selectors.globalItems.applicationsMenuVisible) @@ -562,7 +561,6 @@ describe('Ticket Edit sale path', () => { it('should log in as salesPerson role and go to the ticket index', async() => { const url = await nightmare - .waitToClick(selectors.globalItems.logOutButton) .waitForLogin('salesPerson') .waitToClick(selectors.globalItems.applicationsMenuButton) .wait(selectors.globalItems.applicationsMenuVisible) diff --git a/front/core/directives/acl.js b/front/core/directives/acl.js index 81acbbe85..51ceb3b6d 100644 --- a/front/core/directives/acl.js +++ b/front/core/directives/acl.js @@ -71,7 +71,7 @@ function vnAcl(aclService, $timeout) { restrict: 'A', priority: -1, link: function($scope, $element, $attrs) { - acls = $attrs.vnAcl.split(',').map(element => element.trim().toLowerCase()); + acls = $attrs.vnAcl.split(',').map(i => i.trim()); let action = $attrs.vnAclAction || 'disabled'; let conditions = getDynamicConditions($attrs); permissionElement($element, action); diff --git a/package-lock.json b/package-lock.json index 78e7f1f0a..f676e6e7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2685,7 +2685,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2735,7 +2735,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2954,7 +2954,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -3431,7 +3431,7 @@ }, "colors": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, @@ -3624,7 +3624,7 @@ }, "content-disposition": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "resolved": "http://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" }, "content-security-policy-builder": { @@ -3825,7 +3825,7 @@ }, "css-loader": { "version": "0.25.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.25.0.tgz", + "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-0.25.0.tgz", "integrity": "sha1-w/68jOKPTINXa2sTcH9H+Qw5AiM=", "dev": true, "requires": { @@ -4482,7 +4482,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -5151,7 +5151,7 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -5874,7 +5874,7 @@ }, "fs-access": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, "requires": { @@ -5947,8 +5947,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5969,14 +5968,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5991,20 +5988,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6121,8 +6115,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6134,7 +6127,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6149,7 +6141,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6157,14 +6148,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6183,7 +6172,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6264,8 +6252,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6277,7 +6264,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6363,8 +6349,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6400,7 +6385,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6420,7 +6404,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6464,14 +6447,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6716,7 +6697,7 @@ }, "globby": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { @@ -6761,7 +6742,7 @@ }, "got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -7940,7 +7921,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -8087,7 +8068,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -8787,7 +8768,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -10295,7 +10276,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -10318,7 +10299,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -10534,7 +10515,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -10542,7 +10523,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -10694,7 +10675,7 @@ }, "multipipe": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "resolved": "http://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", "dev": true, "requires": { @@ -10919,7 +10900,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -11874,7 +11855,7 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, @@ -11890,7 +11871,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -12078,7 +12059,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -12829,7 +12810,7 @@ }, "pretty-bytes": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "resolved": "http://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", "dev": true, "requires": { @@ -12918,7 +12899,7 @@ }, "through2": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz", "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", "dev": true, "requires": { @@ -13457,7 +13438,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -14821,7 +14802,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -14992,7 +14973,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -15398,7 +15379,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -15568,7 +15549,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -15840,7 +15821,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -16484,7 +16465,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": {