From aa4d1edd32006096791ffa9e8a78cc7c7be7dc34 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Thu, 5 May 2022 15:56:17 +0200 Subject: [PATCH] #3806 Salix backend --- app.js | 75 +++---- forms/account/conf/conf.js | 13 +- forms/admin/connections/connections.js | 44 ++-- forms/admin/users/users.js | 25 +-- forms/ecomerce/ticket/ticket.js | 12 +- js/db/result-set.js | 52 +++-- js/hedera/app.js | 15 +- js/hedera/gui.js | 6 +- js/hedera/locale/ca.yml | 3 +- js/hedera/locale/en.yml | 3 +- js/hedera/locale/es.yml | 3 +- js/hedera/locale/fr.yml | 3 +- js/hedera/locale/mn.yml | 1 - js/hedera/locale/pt.yml | 3 +- js/hedera/login.js | 99 ++++----- js/vn/json-connection.js | 286 ++++++++++++------------- rest/client/supplant.php | 30 +++ rest/core/change-password.php | 21 -- rest/core/login.php | 30 --- rest/core/logout.php | 9 - rest/core/supplant.php | 1 - rest/core/sync-user.php | 17 -- rest/dms/add.php | 105 --------- rest/misc/sms.php | 52 ----- web/rest-service.php | 13 +- web/service.php | 81 +++---- webpack.config.js | 22 +- 27 files changed, 389 insertions(+), 635 deletions(-) create mode 100644 rest/client/supplant.php delete mode 100644 rest/core/change-password.php delete mode 100644 rest/core/login.php delete mode 100644 rest/core/logout.php delete mode 100644 rest/core/sync-user.php delete mode 100644 rest/dms/add.php delete mode 100644 rest/misc/sms.php diff --git a/app.js b/app.js index 0481dfe5..aa8a23f4 100644 --- a/app.js +++ b/app.js @@ -1,71 +1,60 @@ +__webpack_public_path__ = _PUBLIC_PATH; -var assetsPath; +require('hedera/hedera'); -if (_DEV_MODE) -{ - var host = window.location.host.split(':')[0]; - assetsPath = 'http://'+ host +':'+ _DEV_SERVER_PORT +'/'+ _PUBLIC_PATH; -} -else - assetsPath = _PUBLIC_PATH; - -__webpack_public_path__ = assetsPath; - -require ('hedera/hedera'); - -window.onload = function () -{ - loadLocale (main); +window.onload = function() { + loadLocale(main); } -function main (req) -{ +function main(req) { if (req) - onLocaleLoad (req); + onLocaleLoad(req); - hederaWeb = new Hedera.App (); - hederaWeb.run (); + hederaWeb = new Hedera.App(); + hederaWeb.run(); } -function loadLocale (cb) -{ - Vn.Locale.init (); +function loadLocale(cb) { + Vn.Locale.init(); var lang = Vn.Locale.language; - var req = require.context ('js', true, /locale\/en.yml$/); - onLocaleLoad (req); + var req = require.context('js', true, /locale\/en.yml$/); + onLocaleLoad(req); - switch (lang) - { + switch (lang) { case 'ca': - require ([], function () { - cb (require.context ('js', true, /locale\/ca.yml$/)); }); + require([], function() { + cb(require.context('js', true, /locale\/ca.yml$/)); + }); break; case 'es': - require ([], function () { - cb (require.context ('js', true, /locale\/es.yml$/)); }); + require([], function() { + cb(require.context('js', true, /locale\/es.yml$/)); + }); break; case 'fr': - require ([], function () { - cb (require.context ('js', true, /locale\/fr.yml$/)); }); + require([], function() { + cb(require.context('js', true, /locale\/fr.yml$/)); + }); break; case 'mn': - require ([], function () { - cb (require.context ('js', true, /locale\/mn.yml$/)); }); + require([], function() { + cb(require.context('js', true, /locale\/mn.yml$/)); + }); break; case 'pt': - require ([], function () { - cb (require.context ('js', true, /locale\/pt.yml$/)); }); + require([], function() { + cb(require.context('js', true, /locale\/pt.yml$/)); + }); break; default: - cb (); + cb(); } } -function onLocaleLoad (req) -{ - var keys = req.keys (); +function onLocaleLoad(req) { + var keys = req.keys(); for (var i = 0; i < keys.length; i++) - Vn.Locale.add (req (keys[i])); + Vn.Locale.add(req(keys[i])); } diff --git a/forms/account/conf/conf.js b/forms/account/conf/conf.js index 68188877..4e82f0d4 100644 --- a/forms/account/conf/conf.js +++ b/forms/account/conf/conf.js @@ -5,8 +5,6 @@ Hedera.Conf = new Class({ ,activate: function() { this.$('user-model').setInfo('c', 'myClient', 'hedera'); - console.log(this.hash.get('verificationToken')); - if (this.hash.get('verificationToken')) this.onPassChangeClick(); } @@ -45,17 +43,20 @@ Hedera.Conf = new Class({ this.conn.send('core/restore-password', params, this._onPassChange.bind(this)); } else { + let userId = this.gui.user.id; params.oldPassword = oldPassword; - this.conn.send('core/change-password', params, - this._onPassChange.bind(this)); + this.conn.lbSend('PATCH', + `Accounts/${userId}/changePassword`, params, + this._onPassChange.bind(this) + ); } } catch (e) { Htk.Toast.showError(e.message); } } - + ,_onPassChange: function(json, error) { - if (json) { + if (!error) { this.$('change-password').hide(); this.hash.unset('verificationToken'); Htk.Toast.showMessage(_('Password changed!')); diff --git a/forms/admin/connections/connections.js b/forms/admin/connections/connections.js index bb5c2993..ff058c12 100644 --- a/forms/admin/connections/connections.js +++ b/forms/admin/connections/connections.js @@ -1,52 +1,44 @@ -Hedera.Connections = new Class -({ +Hedera.Connections = new Class({ Extends: Hedera.Form ,_timeoutId: null - ,onModelStatusChange: function (model, status) - { + ,onModelStatusChange: function(model) { if (!model.ready) return; if (this._timeoutId) - clearTimeout (this._timeoutId); + clearTimeout(this._timeoutId); - this._timeoutId = setTimeout (this.onRefreshClick.bind (this), 60000); + this._timeoutId = setTimeout(this.onRefreshClick.bind(this), 60000); } - ,deactivate: function () - { - clearTimeout (this._timeoutId); + ,deactivate: function() { + clearTimeout(this._timeoutId); } - ,onRefreshClick: function () - { - this.$('sessions').refresh (); + ,onRefreshClick: function() { + this.$('sessions').refresh(); } - ,onAccessLogClick: function (button, form) - { - this.hash.set ({ - 'form': 'admin/access-log' - ,'user': form.get ('userId') + ,onAccessLogClick: function(button, form) { + this.hash.set({ + form: 'admin/access-log' + ,user: form.get('userId') }); } - ,onChangeUserClick: function (button, form) - { - this.gui.supplantUser (form.get ('user'), - this._onUserSupplant.bind (this)); + ,onChangeUserClick: function(button, form) { + this.gui.supplantUser(form.get('user'), + this._onUserSupplant.bind(this)); } - ,_onUserSupplant: function (userName) - { - this.hash.set ({'form': 'ecomerce/orders'}); + ,_onUserSupplant: function() { + this.hash.set({form: 'ecomerce/orders'}); } - ,sessionsFunc: function () - { + ,sessionsFunc: function() { return 1; } }); diff --git a/forms/admin/users/users.js b/forms/admin/users/users.js index 52a1de8b..ccca530c 100644 --- a/forms/admin/users/users.js +++ b/forms/admin/users/users.js @@ -1,18 +1,15 @@ -Hedera.Users = new Class -({ +Hedera.Users = new Class({ Extends: Hedera.Form - ,onAccessLogClick: function (button, form) - { - this.hash.set ({ + ,onAccessLogClick: function(button, form) { + this.hash.set({ 'form': 'admin/access-log' - ,'user': form.get ('id') + ,'user': form.get('id') }); } - ,rendererFunc: function (scope, form) - { + ,rendererFunc: function(scope, form) { var isEnabled = form.get('active') scope.$('disabled').style.display = isEnabled ? 'none' : 'block'; @@ -20,15 +17,13 @@ Hedera.Users = new Class 'block' : 'none'; } - ,onChangeUserClick: function (button, form) - { - this.gui.supplantUser (form.get ('name'), - this.onUserSupplant.bind (this)); + ,onChangeUserClick: function(button, form) { + this.gui.supplantUser(form.get('name'), + this.onUserSupplant.bind(this)); } - ,onUserSupplant: function () - { - this.hash.set ({form: 'ecomerce/orders'}); + ,onUserSupplant: function() { + this.hash.set({form: 'ecomerce/orders'}); } }); diff --git a/forms/ecomerce/ticket/ticket.js b/forms/ecomerce/ticket/ticket.js index 59a6f408..2204818d 100644 --- a/forms/ecomerce/ticket/ticket.js +++ b/forms/ecomerce/ticket/ticket.js @@ -19,11 +19,15 @@ Hedera.Ticket = new Class({ }, onPrintClick: function() { - var batch = new Sql.Batch(); - batch.addValue('ticket', this.$('ticket-id').value); - this.gui.openReport('delivery-note', batch); + let params = Vn.Url.makeUri({ + authorization: this.conn.token, + ticketId: this.$('ticket-id').value, + recipientId: this.gui.user.id, + type: 'deliveryNote' + }); + window.open(`/api/report/delivery-note?${params}`); }, - + repeaterFunc: function(res, form) { var discount = res.$('discount'); discount.style.display = form.get('discount') ? 'inline' : 'none'; diff --git a/js/db/result-set.js b/js/db/result-set.js index 23d528d5..241a219b 100644 --- a/js/db/result-set.js +++ b/js/db/result-set.js @@ -1,19 +1,17 @@ -var Result = require ('./result'); +var Result = require('./result'); /** * This class stores the database results. **/ -module.exports = new Class -({ +module.exports = new Class({ results: null ,error: null /** * Initilizes the resultset object. **/ - ,initialize: function (results, error) - { + ,initialize: function(results, error) { this.results = results; this.error = error; } @@ -23,19 +21,17 @@ module.exports = new Class * * @return {Db.Err} the error or null if no errors hapened **/ - ,getError: function () - { + ,getError: function() { return this.error; } - ,fetch: function () - { + ,fetch: function() { if (this.error) throw this.error; if (this.results !== null && this.results.length > 0) - return this.results.shift (); + return this.results.shift(); return null; } @@ -45,14 +41,12 @@ module.exports = new Class * * @return {Db.Result} the result or %null if error or there are no more results **/ - ,fetchResult: function () - { - var result = this.fetch (); + ,fetchResult: function() { + var result = this.fetch(); - if (result !== null) - { + if (result !== null) { if (result.data instanceof Array) - return new Result (result); + return new Result(result); else return true; } @@ -65,9 +59,8 @@ module.exports = new Class * * @return {Array} the row if success, %null otherwise **/ - ,fetchRow: function () - { - var result = this.fetch (); + ,fetchRow: function() { + var result = this.fetch(); if (result !== null && result.data instanceof Array @@ -77,14 +70,29 @@ module.exports = new Class return null; } + ,fetchObject: function() { + var result = this.fetch(); + + if (result !== null + && result.data instanceof Array + && result.data.length > 0) { + var row = result.data[0]; + var object = {}; + for(var i = 0; i < row.length; i++) + object[result.columns[i].name] = row[i]; + return object; + } + + return null; + } + /** * Fetchs the first row and column value from the next resultset. * * @return {Object} the value if success, %null otherwise **/ - ,fetchValue: function () - { - var row = this.fetchRow (); + ,fetchValue: function() { + var row = this.fetchRow(); if (row instanceof Array && row.length > 0) return row[0]; diff --git a/js/hedera/app.js b/js/hedera/app.js index 8a3bc433..a27fa926 100644 --- a/js/hedera/app.js +++ b/js/hedera/app.js @@ -54,7 +54,7 @@ module.exports = new Class({ this.unref(); } - ,_onWindowError: function(message, file, line, col, err) { + ,_onWindowError: function(message, file, line) { var error = new Error(message); error.fileName = file; error.lineNumber = line; @@ -68,12 +68,15 @@ module.exports = new Class({ Htk.Toast.showError(_('Invalid login')); this._logout(); break; + case 'Forbidden': + Htk.Toast.showError(_('You don\'t have enough privileges')); + break; case 'UserDisabled': Htk.Toast.showError(_('User disabled')); this._logout(); break; case 'SessionExpired': - Htk.Toast.showError(_('You\'ve been too idle')); + Htk.Toast.showError(_('Session expired')); this._logout(); break; case 'OutdatedVersion': @@ -81,6 +84,14 @@ module.exports = new Class({ break; default: Htk.Toast.showError(error.message); + } else if (error.statusCode) + switch (error.statusCode) { + case 401: + Htk.Toast.showError(_('Invalid login')); + this._logout(); + break; + default: + Htk.Toast.showError(error.message); } else { console.error(error); diff --git a/js/hedera/gui.js b/js/hedera/gui.js index 04177602..1409d1f2 100644 --- a/js/hedera/gui.js +++ b/js/hedera/gui.js @@ -48,7 +48,7 @@ module.exports = new Class({ this.$('social-bar').conn = this._conn; - var sql = 'SELECT nickname FROM account.myUser;' + var sql = 'SELECT id, name, nickname FROM account.myUser;' +'SELECT defaultForm FROM config;' +'SELECT url FROM imageConfig;' +'SELECT dbproduccion FROM vn2008.tblContadores;' @@ -121,8 +121,8 @@ module.exports = new Class({ ,onMainQueryDone: function(resultSet) { // Retrieving the user name - var userName = resultSet.fetchValue(); - Vn.Node.setText(this.$('user-name'), userName); + this.user = resultSet.fetchObject(); + Vn.Node.setText(this.$('user-name'), this.user.nickname); // Retrieving configuration parameters diff --git a/js/hedera/locale/ca.yml b/js/hedera/locale/ca.yml index e26b24f7..7ad360ee 100644 --- a/js/hedera/locale/ca.yml +++ b/js/hedera/locale/ca.yml @@ -9,7 +9,8 @@ Login phone: +34 607 562 391 Password forgotten? Push here: ¿Has oblidat la teva contrasenya? Yet you are not a customer?: Encara no ets client? Sign up: Registrarme -You've been too idle: Has estat massa temps inactiu i la sessió ha expirat +You don't have enough privileges: No tens prou privilegis +Session expired: La sessió ha expirat Invalid login: >- Usuari o contrasenya incorrectes, recorda que s'hi distingeix entre majúscula i minúscula diff --git a/js/hedera/locale/en.yml b/js/hedera/locale/en.yml index ff22a8f9..efe920dd 100644 --- a/js/hedera/locale/en.yml +++ b/js/hedera/locale/en.yml @@ -10,7 +10,8 @@ Password forgotten? Push here: Password forgotten? Push here Yet you are not a customer?: Yet you are not a customer? Sign up: Sign up Sign up link: http://bit.ly/2wLntMl -You've been too idle: You have been idle too long and your session has expired +You don't have enough privileges: You don't have enough privileges +Session expired: Your session has expired Invalid login: 'Username or password incorrect, remember that it is case-sensitive' User disabled: >- Authentication is correct but the user account has been disabled, please diff --git a/js/hedera/locale/es.yml b/js/hedera/locale/es.yml index 1c994999..e769c5c5 100644 --- a/js/hedera/locale/es.yml +++ b/js/hedera/locale/es.yml @@ -10,7 +10,8 @@ Password forgotten? Push here: ¿Has olvidado tu contraseña? Yet you are not a customer?: ¿Todavía no eres cliente? Sign up: Registrarme Sign up link: http://bit.ly/2wLntMl -You've been too idle: Has estado demasiado tiempo inactivo y la sesión ha expirado +You don't have enough privileges: No tienes suficientes privilegios +Session expired: La sesión ha expirado Invalid login: >- Usuario o contraseña incorrectos, recuerda que se hace distinción entre mayúsculas y minúsculas diff --git a/js/hedera/locale/fr.yml b/js/hedera/locale/fr.yml index 45d4fa56..db5d4fcc 100644 --- a/js/hedera/locale/fr.yml +++ b/js/hedera/locale/fr.yml @@ -10,7 +10,8 @@ Password forgotten? Push here: as tu oublié ton mot de passe? Yet you are not a customer?: Êtes-vous Pas encore client? Sign up: Inscription Sign up link: http://bit.ly/2msCil1 -You've been too idle: Il a eu le temps de trop paresseux et votre session a expiré +You don't have enough privileges: Vous n'avez pas assez de privilèges +Session expired: Et votre session a expiré Invalid login: >- Utilisateur ou mot de passe incorrect, n'oubliez pas de distinction entre majuscules et minuscules diff --git a/js/hedera/locale/mn.yml b/js/hedera/locale/mn.yml index 06ee6aac..87c8f0e1 100644 --- a/js/hedera/locale/mn.yml +++ b/js/hedera/locale/mn.yml @@ -9,7 +9,6 @@ Login phone: +34 607 562 391 Password forgotten? Push here: Нууц үг мартсан? энд түлхэх Yet you are not a customer?: Гэсэн хэдий ч та хэрэглэгчийн биш гэж үү? Sign up: бүртгүүлэх -You've been too idle: 'Та нар ч бас зогссон байсан, чуулган хугацаа дууссан байна' Invalid login: 'Хэрэглэгчийн нэр эсвэл нууц үг буруу, Тэр хэргийг мэдрэмтгий гэдгийг санаарай' Please write your user name: Хэрэглэгчийн нэрээ бичнэ үү A mail has been sent wich you can recover your password: Мэйл та нууц үгээ сэргээх боломжтой А байна илгээсэн diff --git a/js/hedera/locale/pt.yml b/js/hedera/locale/pt.yml index 326d88be..05f68099 100644 --- a/js/hedera/locale/pt.yml +++ b/js/hedera/locale/pt.yml @@ -9,7 +9,8 @@ Login phone: +34 963 242 100 Password forgotten? Push here: Não lembro minha palavra-passe Yet you are not a customer?: Ainda não és cliente? Sign up: Cadastrar-se -You've been too idle: 'Muito tempo de inatividade, a sessão foi finalizada' +You don't have enough privileges: Você não tem privilégios suficientes +Session expired: 'A sessão foi finalizada' Invalid login: >- Usuário ou Palavra-Passe incorreto, lembre-se de diferenciar maiusculas e minusculas diff --git a/js/hedera/login.js b/js/hedera/login.js index 3bfc4368..71dbef8f 100644 --- a/js/hedera/login.js +++ b/js/hedera/login.js @@ -1,129 +1,112 @@ -var Css = require ('./login.css'); -var Tpl = require ('./login.xml'); +require('./login.css'); +var Tpl = require('./login.xml'); -module.exports = new Class -({ +module.exports = new Class({ Extends: Htk.Component, Properties: { conn: { type: Db.Connection - ,set: function (x) - { - this.link ({_conn: x}, {'loading-changed': this._onConnLoadChange}); + ,set: function(x) { + this.link({_conn: x}, {'loading-changed': this._onConnLoadChange}); } - ,get: function () - { + ,get: function() { return this._conn; } } } - ,initialize: function (props) - { - this.parent (props); - this.builderInitString (Tpl); + ,initialize: function(props) { + this.parent(props); + this.builderInitString(Tpl); this.$('social-bar').conn = this._conn; var self = this; - this.$('form').onsubmit = function () - { - self._onSubmit (); + this.$('form').onsubmit = function() { + self._onSubmit(); return false; }; } - ,_onConnLoadChange: function (conn, isLoading) - { + ,_onConnLoadChange: function(conn, isLoading) { if (isLoading) - this.$('spinner').start (); + this.$('spinner').start(); else - this.$('spinner').stop (); + this.$('spinner').stop(); } - ,show: function () - { - document.body.appendChild (this.node); + ,show: function() { + document.body.appendChild(this.node); - var lastUser = localStorage.getItem ('hederaLastUser'); + var lastUser = localStorage.getItem('hederaLastUser'); if (lastUser) this.$('user').value = lastUser; - this._focusUserInput (); + this._focusUserInput(); } - ,_onSubmit: function () - { - this._conn.open ( + ,_onSubmit: function() { + this._conn.open( this.$('user').value, this.$('pass').value, this.$('remember').checked, - this._onConnOpen.bind (this) + this._onConnOpen.bind(this) ); - this._disableUi (true); + this._disableUi(true); } - ,_onConnOpen: function (conn, success, error) - { + ,_onConnOpen: function(conn, success, error) { this.$('pass').value = ''; - this._disableUi (false); + this._disableUi(false); - if (success) - { + if (success) { var user = this.$('user').value; if (user) - localStorage.setItem ('hederaLastUser', user); + localStorage.setItem('hederaLastUser', user); - this.signalEmit ('login'); - } - else - { - this._focusUserInput (); + this.signalEmit('login'); + } else { + this._focusUserInput(); throw error; } } - ,hide: function () - { - Vn.Node.remove (this.node); + ,hide: function() { + Vn.Node.remove(this.node); } - ,_focusUserInput: function () - { + ,_focusUserInput: function() { var userEntry = this.$('user'); - userEntry.focus (); - userEntry.select (); + userEntry.focus(); + userEntry.select(); } - ,_disableUi: function (disabled) - { + ,_disableUi: function(disabled) { this.$('user').disabled = disabled; this.$('pass').disabled = disabled; this.$('submit').disabled = disabled; } - ,onPasswordLost: function () - { + ,onPasswordLost: function() { var user = this.$('user').value; if (!user) - Htk.Toast.showError (_('Please write your user name')); + Htk.Toast.showError(_('Please write your user name')); else - this._conn.send ('core/recover-password', {recoverUser: user}, - this._onPasswordRecovered.bind (this)); + this._conn.send('core/recover-password', {recoverUser: user}, + this._onPasswordRecovered.bind(this)); } - ,_onPasswordRecovered: function (json, error) - { + ,_onPasswordRecovered: function(json, error) { if (error) throw error; - Htk.Toast.showMessage (_('A mail has been sent wich you can recover your password')); + Htk.Toast.showMessage(_('A mail has been sent wich you can recover your password')); } }); diff --git a/js/vn/json-connection.js b/js/vn/json-connection.js index ffd4912a..40939699 100644 --- a/js/vn/json-connection.js +++ b/js/vn/json-connection.js @@ -1,12 +1,11 @@ -var Object = require ('./object'); -var JsonException = require ('./json-exception'); +var Object = require('./object'); +var JsonException = require('./json-exception'); /** * Handler for JSON rest connections. **/ -module.exports = new Class -({ +module.exports = new Class({ Extends: Object ,_connected: false @@ -16,29 +15,26 @@ module.exports = new Class /** * Initilizes the connection object. **/ - ,initialize: function () - { - this.parent (); - this.fetchToken (); + ,initialize: function() { + this.parent(); + this.fetchToken(); } - ,fetchToken: function () - { + ,fetchToken: function() { var token = null; - if (sessionStorage.getItem ('vnToken')) - token = sessionStorage.getItem ('vnToken'); - if (localStorage.getItem ('vnToken')) - token = localStorage.getItem ('vnToken'); + if (sessionStorage.getItem('vnToken')) + token = sessionStorage.getItem('vnToken'); + if (localStorage.getItem('vnToken')) + token = localStorage.getItem('vnToken'); this.token = token; } - ,clearToken: function () - { + ,clearToken: function() { this.token = null; - localStorage.removeItem ('vnToken'); - sessionStorage.removeItem ('vnToken'); + localStorage.removeItem('vnToken'); + sessionStorage.removeItem('vnToken'); } /** @@ -49,43 +45,37 @@ module.exports = new Class * @param {Boolean} remember Specifies if the user should be remembered * @param {Function} openCallback The function to call when operation is done **/ - ,open: function (user, pass, remember, callback) - { - if (user !== null && user !== undefined) - { + ,open: function(user, pass, remember, callback) { + if (user !== null && user !== undefined) { var params = { user: user ,password: pass ,remember: remember }; - } - else + } else var params = null; - this.send ('core/login', params, - this._onOpen.bind (this, callback, remember)); + this.lbSend('POST', 'Accounts/login', params, + this._onOpen.bind(this, callback, remember)); } /* * Called when open operation is done. */ - ,_onOpen: function (callback, remember, json, error) - { - if (json && json.login) - { + ,_onOpen: function(callback, remember, json, error) { + if (json) { this._connected = true; this.token = json.token; var storage = remember ? localStorage : sessionStorage; - storage.setItem ('vnToken', this.token); + storage.setItem('vnToken', this.token); - this.signalEmit ('openned'); - } - else - this._closeClient (); + this.signalEmit('openned'); + } else + this._closeClient(); if (callback) - callback (this, this._connected, error); + callback(this, this._connected, error); } /** @@ -93,58 +83,52 @@ module.exports = new Class * * @param {Function} callback The function to call when operation is done **/ - ,close: function (callback) - { - this._closeClient (); - this.send ('core/logout', null, - this._onClose.bind (this, callback)); + ,close: function(callback) { + this.lbSend('POST', 'Accounts/logout', null, + this._onClose.bind(this, callback)); + this._closeClient(); } /* * Called when close operation is done. */ - ,_onClose: function (callback, json, error) - { - this.signalEmit ('closed'); + ,_onClose: function(callback, json, error) { + this.signalEmit('closed'); if (callback) - callback (this, json === true, error); + callback(this, null, error); } - ,_closeClient: function () - { + ,_closeClient: function() { this._connected = false; - this.clearToken (); + this.clearToken(); } /** - * Suppants another user. + * Supplants another user. * * @param {String} user The user name * @param {Function} callback The callback function **/ - ,supplantUser: function (user, callback) - { + ,supplantUser: function(user, callback) { var params = {supplantUser: user}; - this.send ('core/supplant', params, - this._onUserSupplant.bind (this, callback)); + this.send('client/supplant', params, + this._onUserSupplant.bind(this, callback)); } - ,_onUserSupplant: function (callback, json, error) - { + ,_onUserSupplant: function(callback, json) { if (json) this.token = json; if (callback) - callback (json != null); + callback(json != null); } /** * Ends the user supplanting and restores the last login. **/ - ,supplantEnd: function () - { - this.fetchToken (); + ,supplantEnd: function() { + this.fetchToken(); } /** @@ -155,18 +139,16 @@ module.exports = new Class * @param {Map} params The params to pass to the service * @param {Function} callback The response callback **/ - ,send: function (restService, params, callback) - { + ,send: function(restService, params, callback) { if (!params) params = {}; params.srv = 'json:'+ restService; - this.sendWithUrl (params, callback, 'POST', '.'); + this.sendWithUrl(params, callback, 'POST', '.'); } - ,sendForm: function (form, callback) - { + ,sendForm: function(form, callback) { var params = {}; var elements = form.elements; @@ -174,83 +156,88 @@ module.exports = new Class if (elements[i].name) params[elements[i].name] = elements[i].value; - this.sendWithUrl (params, callback, 'POST', form.action); + this.sendWithUrl(params, callback, 'POST', form.action); } - ,sendFormMultipart: function (form, callback) - { - var formData = new FormData (form); + ,sendFormMultipart: function(form, callback) { + var formData = new FormData(form); + var request = new XMLHttpRequest(); + request.open('POST', form.action, true); if (this.token) - formData.append ('token', this.token); - - var request = new XMLHttpRequest (); - request.open ('POST', form.action, true); + request.setRequestHeader('Authorization', this.token); request.onreadystatechange = - this._onStateChange.bind (this, request, callback); - request.send (formData); + this._onStateChange.bind(this, request, callback); + request.send(formData); - this._addRequest (); + this._addRequest(); } - ,sendFormData: function (formData, callback) - { + ,sendFormData: function(formData, callback) { + var request = new XMLHttpRequest(); + request.open('POST', '', true); if (this.token) - formData.append ('token', this.token); - - var request = new XMLHttpRequest (); - request.open ('POST', '', true); + request.setRequestHeader('Authorization', this.token); request.onreadystatechange = - this._onStateChange.bind (this, request, callback); - request.send (formData); + this._onStateChange.bind(this, request, callback); + request.send(formData); - this._addRequest (); + this._addRequest(); } /* * Called when REST response is received. */ - ,sendWithUrl: function (params, callback, method, url) - { - if (this.token) - params['token'] = this.token; - - var request = new XMLHttpRequest (); - request.open (method, url, true); - request.setRequestHeader ('Content-Type', + ,sendWithUrl: function(params, callback, method, url) { + var request = new XMLHttpRequest(); + request.open(method, url, true); + request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + if (this.token) + request.setRequestHeader('Authorization', this.token); request.onreadystatechange = - this._onStateChange.bind (this, request, callback); - request.send (Vn.Url.makeUri (params)); + this._onStateChange.bind(this, request, callback); + request.send(Vn.Url.makeUri(params)); - this._addRequest (); + this._addRequest(); } - ,_addRequest: function () - { + ,lbSend: function(method, url, params, callback) { + var request = new XMLHttpRequest(); + request.open(method, `api/${url}`, true); + request.setRequestHeader('Content-Type', + 'application/json;charset=utf-8'); + if (this.token) + request.setRequestHeader('Authorization', this.token); + request.onreadystatechange = + this._onStateChange.bind(this, request, callback); + request.send(params && JSON.stringify(params)); + + this._addRequest(); + } + + ,_addRequest: function() { this._requestsCount++; if (this._requestsCount === 1) - this.signalEmit ('loading-changed', true); + this.signalEmit('loading-changed', true); } - ,_onStateChange: function (request, callback) - { + ,_onStateChange: function(request, callback) { if (request.readyState !== 4) return; this._requestsCount--; if (this._requestsCount === 0) - this.signalEmit ('loading-changed', false); + this.signalEmit('loading-changed', false); var data = null; var error = null; try { - if (request.status == 0) - { - var ex = new JsonException (); + if (request.status == 0) { + var ex = new JsonException(); ex.message = _('The server does not respond, please check your Internet connection'); throw ex; } @@ -259,70 +246,75 @@ module.exports = new Class try { contentType = request - .getResponseHeader ('Content-Type') - .split (';')[0] - .trim (); + .getResponseHeader('Content-Type') + .split(';')[0] + .trim(); + } catch (err) { + console.warn(err); } - catch (e) {} - if (contentType != 'application/json') - { - var ex = new JsonException (); + if (contentType != 'application/json') { + var ex = new JsonException(); ex.message = request.statusText; ex.code = request.status; throw ex; } - - var json = JSON.parse (request.responseText); - var jsData = json.data; - //var jsWarns = json.warnings; - - if (request.status == 200) - { - data = jsData; - } - else - { - var exception = jsData.exception; - - if (exception) - exception = exception - .replace (/\\/g, '.') - .replace (/Exception$/, '') - .replace (/^Vn\.Web\./, ''); - var ex = new JsonException (); - ex.exception = exception; - ex.message = jsData.message; - ex.code = jsData.code; - ex.file = jsData.file; - ex.line = jsData.line; - ex.trace = jsData.trace; + var json; + var jsData; + + if (request.responseText) + json = JSON.parse(request.responseText); + if (json) + jsData = json.data || json; + + if (request.status >= 200 && request.status < 300) { + data = jsData; + } else { + var exception = jsData.exception; + var error = jsData.error; + + if (exception) { + exception = exception + .replace(/\\/g, '.') + .replace(/Exception$/, '') + .replace(/^Vn\.Web\./, ''); + + var ex = new JsonException(); + ex.exception = exception; + ex.message = jsData.message; + ex.code = jsData.code; + ex.file = jsData.file; + ex.line = jsData.line; + ex.trace = jsData.trace; + } else if (error) { + var ex = new Error(); + ex.name = error.name; + ex.message = error.message; + ex.code = error.code; + ex.statusCode = request.status; + } + throw ex; } - } - catch (e) - { + } catch (e) { data = null; error = e; } if (callback) try { - callback (data, error); + callback(data, error); error = null; - } - catch (e) - { + } catch (e) { error = e; } - if (error) - { + if (error) { if (error.exception == 'SessionExpired') - this.clearToken (); + this.clearToken(); - this.signalEmit ('error', error); + this.signalEmit('error', error); } } }); diff --git a/rest/client/supplant.php b/rest/client/supplant.php new file mode 100644 index 00000000..4b2dbf49 --- /dev/null +++ b/rest/client/supplant.php @@ -0,0 +1,30 @@ +getValue( + 'SELECT id FROM account.user WHERE `name` = #', + [$_REQUEST['supplantUser']] + ); + + $isClient = $db->getValue( + 'SELECT COUNT(*) > 0 FROM vn.client WHERE id = #', + [$userId] + ); + if (!$isClient) + throw new Web\ForbiddenException(s('The user is not a client')); + + $isWorker = $db->getValue( + 'SELECT COUNT(*) > 0 FROM vn.worker WHERE id = #', + [$userId] + ); + if ($isWorker) + throw new Web\ForbiddenException(s('Workers cannot be supplanted')); + + return $this->service->createToken($_REQUEST['supplantUser']); + } +} diff --git a/rest/core/change-password.php b/rest/core/change-password.php deleted file mode 100644 index 4897d4f5..00000000 --- a/rest/core/change-password.php +++ /dev/null @@ -1,21 +0,0 @@ -query('CALL account.myUser_changePassword(#, #)', - [$oldPassword, $newPassword]); - Account::sync($db, $_SESSION['user'], $newPassword); - return TRUE; - } -} - diff --git a/rest/core/login.php b/rest/core/login.php deleted file mode 100644 index b9c58dd2..00000000 --- a/rest/core/login.php +++ /dev/null @@ -1,30 +0,0 @@ -getMessage()); - } - - $token = $this->service->createToken( - $_SESSION['user'], - !empty($_POST['remember']) - ); - - return [ - 'login' => TRUE, - 'token' => $token - ]; - } -} - diff --git a/rest/core/logout.php b/rest/core/logout.php deleted file mode 100644 index 632c5461..00000000 --- a/rest/core/logout.php +++ /dev/null @@ -1,9 +0,0 @@ -service->logout(); - return TRUE; - } -} - diff --git a/rest/core/supplant.php b/rest/core/supplant.php index 885f6d66..8b471567 100644 --- a/rest/core/supplant.php +++ b/rest/core/supplant.php @@ -7,4 +7,3 @@ class Supplant extends Vn\Web\JsonRequest { return $this->service->createToken($_REQUEST['supplantUser']); } } - diff --git a/rest/core/sync-user.php b/rest/core/sync-user.php deleted file mode 100644 index b85639dc..00000000 --- a/rest/core/sync-user.php +++ /dev/null @@ -1,17 +0,0 @@ -app->getName(); - $docsDir = "$baseDir/dms"; - $tempDir = "$baseDir/.dms"; - - $digXDir = 3; - $zerosDir = ''; - - for ($i = 0; $i < $digXDir; $i++) - $zerosDir .= '0'; - - // Checks document restrictions - - if (empty($_FILES['doc']['name'])) - throw new Lib\UserException('File not choosed'); - - $maxSize = $db->getValue('SELECT max_size FROM dms_config'); - - if ($_FILES['doc']['size'] > $maxSize * 1048576) - throw new Lib\UserException(sprintf('File size exceeds size: %d MB', $maxSize)); - - try { - // Registers the document in the database - - $db->query('START TRANSACTION'); - - $db->query('INSERT INTO dms_document SET description = #', [$description]); - $docId =(string) $db->getValue('SELECT LAST_INSERT_ID()'); - - $len = strlen($docId); - $neededLevels = ceil($len / $digXDir) - 1; - - $dirLevels = $db->getValue( - 'SELECT dir_levels FROM dms_config LOCK IN SHARE MODE'); - - if ($dirLevels > $neededLevels) - $neededLevels = $dirLevels; - - // Reorganizes the file repository if necessary - - if ($dirLevels < $neededLevels) - $dirLevels = $db->getValue( - 'SELECT dir_levels FROM dms_config FOR UPDATE'); - - if ($dirLevels < $neededLevels) { - if (is_dir($docsDir)) { - $dif =($neededLevels - $dirLevels) - 1; - $newDir = $docsDir; - - for ($i = 0; $i < $dif; $i++) - $newDir .= "/$zerosDir"; - - $success = rename($docsDir, $tempDir) - && mkdir($newDir, 0770, TRUE) - && rename($tempDir, "$newDir/$zerosDir"); - - if (!$success) - throw new Exception('Error while reorganizing directory tree'); - } - - $curLevels = $db->query('UPDATE dms_config SET dir_levels = #', - [$neededLevels]); - } - - // Saves the document to the repository - - $padLen =($neededLevels + 1) * $digXDir; - $paddedId = str_pad($docId, $padLen, '0', STR_PAD_LEFT); - - $saveDir = $docsDir; - - for ($i = 0; $i < $neededLevels; $i++) - $saveDir .= '/'. substr($paddedId, $i * $digXDir, $digXDir); - - if (!file_exists($saveDir)) - mkdir($saveDir, 0770, TRUE); - - $savePath = "$saveDir/". substr($paddedId, -$digXDir); - - move_uploaded_file($_FILES['doc']['tmp_name'], $savePath); - - $db->query('COMMIT'); - - return $docId; - } catch (Exception $e) { - $db->query('ROLLBACK'); - throw $e; - } - } -} - diff --git a/rest/misc/sms.php b/rest/misc/sms.php deleted file mode 100644 index 7be046a7..00000000 --- a/rest/misc/sms.php +++ /dev/null @@ -1,52 +0,0 @@ -getObject('SELECT uri, user, password, title FROM vn.smsConfig'); - - $sClient = new SoapClient($smsConfig->uri); - $xmlString = $sClient->sendSMS( - $smsConfig->user - ,$smsConfig->password - ,$smsConfig->title - ,$_REQUEST['destination'] - ,$_REQUEST['message'] - ); - $xmlResponse = new SimpleXMLElement($xmlString); - $res = $xmlResponse->sms; - - $db->query( - 'INSERT INTO vn.sms SET - `senderFk` = account.myUser_getId(), - `destinationFk` = #, - `destination` = #, - `message` = #, - `statusCode` = #, - `status` = #', - [ - empty($_REQUEST['destinationId']) ? NULL : $_REQUEST['destinationId'] - ,$_REQUEST['destination'] - ,$_REQUEST['message'] - ,$res->codigo - ,$res->descripcion - ] - ); - - if (!in_array((int) $res->codigo, self::OK_STATES)) - throw new Lib\UserException($res->descripcion); - - return TRUE; - } -} diff --git a/web/rest-service.php b/web/rest-service.php index 3677cf52..5c0ed716 100644 --- a/web/rest-service.php +++ b/web/rest-service.php @@ -31,13 +31,12 @@ class RestService extends Service { $_REQUEST['method'], $class, './rest'); $method->service = $this; + $isAuthorized = $db->getValue('SELECT myUser_checkRestPriv(#)', + [$_REQUEST['method']]); + if (!$isAuthorized) + throw new ForbiddenException(s('You don\'t have enough privileges')); + if ($method::SECURITY == Security::DEFINER) { - $isAuthorized = $db->getValue('SELECT myUser_checkRestPriv(#)', - [$_REQUEST['method']]); - - if (!$isAuthorized) - throw new UserException(s('You don\'t have enough privileges')); - $methodDb = $db; } else $methodDb = $this->getUserDb($_SESSION['user']); @@ -71,6 +70,8 @@ class RestService extends Service { $status = 401; } catch (BadLoginException $e) { $status = 401; + } catch (ForbiddenException $e) { + $status = 403; } catch (Lib\UserException $e) { $status = 400; } catch (\Exception $e) { diff --git a/web/service.php b/web/service.php index 5df829f1..abaa439b 100644 --- a/web/service.php +++ b/web/service.php @@ -21,6 +21,11 @@ class SessionExpiredException extends UserException {} */ class BadLoginException extends UserException {} +/** + * Thrown when user credentials are invalid. + */ +class ForbiddenException extends UserException {} + /** * Thrown when user credentials are invalid. */ @@ -130,62 +135,26 @@ abstract class Service { */ function login() { $db = $this->db; - $anonymousUser = FALSE; - - if (isset($_POST['user']) && !empty($_POST['password'])) { - $user = strtolower($_POST['user']); + $anonymousUser = TRUE; - $passwordHash = $db->getValue( - 'SELECT bcryptPassword FROM account.user WHERE `name` = #', - [$user] + if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { + $userId = $db->getValue( + 'SELECT userId FROM salix.AccessToken + WHERE id = # + AND NOW() <= TIMESTAMPADD(SECOND, ttl, created)', + [$_SERVER['HTTP_AUTHORIZATION']] ); - $passwordOk = !empty($passwordHash) - && password_verify($_POST['password'], $passwordHash); + if (!$userId) + throw new SessionExpiredException(); - // XXX: Compatibility with old MD5 passwords - if (empty($passwordHash)) { - $md5Password = $db->getValue( - 'SELECT `password` FROM account.user - WHERE active AND `name` = #', - [$user] - ); - - $passwordOk = !empty($md5Password) - && $md5Password == md5($_POST['password']); - } - - if (!$passwordOk) { - sleep(3); - throw new BadLoginException(); - } - } else { - if (isset($_POST['token']) || isset($_GET['token'])) { - if (isset($_POST['token'])) - $token = $_POST['token']; - if (isset($_GET['token'])) - $token = $_GET['token']; - - $key = $db->getValue('SELECT jwtKey FROM config'); - - try { - $jwtPayload = Jwt::decode($token, $key); - } catch (\Exception $e) { - throw new BadLoginException($e->getMessage()); - } - - $expiration = $jwtPayload['exp']; - - if (empty($expiration) || $expiration <= time()) - throw new SessionExpiredException(); - - $user = $jwtPayload['sub']; - } - else { - $user = $db->getValue('SELECT guestUser FROM config'); - $anonymousUser = TRUE; - } - } + $anonymousUser = FALSE; + $user = $db->getValue( + 'SELECT `name` FROM account.user WHERE id = #', + [$userId] + ); + } else + $user = $db->getValue('SELECT guestUser FROM config'); if (!$anonymousUser) { $isActive = $db->getValue( @@ -217,9 +186,15 @@ abstract class Service { * Logouts the current user. Cleans the last saved used credentials. */ function logout() { + if (!empty($_SERVER['HTTP_AUTHORIZATION'])) + $db->query( + 'DELETE FROM salix.AccessToken WHERE id = #', + [$_SERVER['HTTP_AUTHORIZATION']] + ); + unset($_SESSION['user']); } - + /** * Creates or returns a database connection where the authenticated user * is the role of the current logged user. diff --git a/webpack.config.js b/webpack.config.js index f64116ba..9d7845b9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -42,15 +42,15 @@ var baseConfig = { new webpack.DefinePlugin({ _DEV_MODE: devMode, _DEV_SERVER_PORT: wpConfig.devServerPort, - _PUBLIC_PATH: JSON.stringify (publicPath) + _PUBLIC_PATH: JSON.stringify(publicPath) }) ], - optimization: { - runtimeChunk: true, - splitChunks: { - chunks: 'all', - } - }, + optimization: { + runtimeChunk: true, + splitChunks: { + chunks: 'all', + } + }, watchOptions: { ignored: /node_modules/ } @@ -78,8 +78,12 @@ var devConfig = { devServer: { host: '0.0.0.0', port: wpConfig.devServerPort, - headers: { 'Access-Control-Allow-Origin': '*' }, - stats: { chunks: false } + headers: {'Access-Control-Allow-Origin': '*'}, + stats: { chunks: false }, + proxy: { + '/api': 'http://localhost:3000', + '/': 'http://localhost/projects/hedera-web' + } }, devtool: 'eval' };