From f72a0163891efa876d3b21c181d02c7be992ceb1 Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Fri, 28 Jun 2019 13:59:48 +0000 Subject: [PATCH] [FIX] pos_hr: barcode and pin code access rights Since groups added on fields 'barcode' and 'pin' on hr employee in rev: c9ca3761464413327d2beb697553a3ccd7eef4d1 It is not possible anymore to launch the POS when 'pos_hr' module is installed. Because we don't want to allow users to access in read on all pin codes and barcodes of employees. We've made a function that will get a sha1 signature of the barcode and pin code of all employees to store it in the frontend of POS. We don't have other solutions concerning the security if we want to keep the POS working offline. We also know that it is not brute force proof, as pin code are composed of only digits and you can easily generate the signatures of all pincodes. closes odoo/odoo#34447 Signed-off-by: pimodoo --- addons/point_of_sale/static/lib/sha1.js | 159 +++++++++++++++++++ addons/point_of_sale/views/point_of_sale.xml | 1 + addons/pos_hr/models/__init__.py | 1 + addons/pos_hr/models/hr_employee.py | 22 +++ addons/pos_hr/static/src/js/gui.js | 2 +- addons/pos_hr/static/src/js/models.js | 28 +++- addons/pos_hr/static/src/js/screens.js | 2 +- 7 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 addons/point_of_sale/static/lib/sha1.js create mode 100644 addons/pos_hr/models/hr_employee.py diff --git a/addons/point_of_sale/static/lib/sha1.js b/addons/point_of_sale/static/lib/sha1.js new file mode 100644 index 00000000000..0fc324cfd33 --- /dev/null +++ b/addons/point_of_sale/static/lib/sha1.js @@ -0,0 +1,159 @@ +/* from http://www.movable-type.co.uk/scripts/sha1.html */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* SHA-1 implementation in JavaScript (c) Chris Veness 2002-2014 / MIT Licence */ +/* */ +/* - see http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html */ +/* http://csrc.nist.gov/groups/ST/toolkit/examples.html */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* jshint node:true *//* global define, escape, unescape */ +'use strict'; + + +/** + * SHA-1 hash function reference implementation. + * + * @namespace + */ +var Sha1 = {}; + + +/** + * Generates SHA-1 hash of string. + * + * @param {string} msg - (Unicode) string to be hashed. + * @returns {string} Hash of msg as hex character string. + */ +Sha1.hash = function(msg) { + // convert string to UTF-8, as SHA only deals with byte-streams + msg = msg.utf8Encode(); + + // constants [§4.2.1] + var K = [ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 ]; + + // PREPROCESSING + + msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1] + + // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1] + var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length + var N = Math.ceil(l/16); // number of 16-integer-blocks required to hold 'l' ints + var M = new Array(N); + + for (var i=0; i>> 32, but since JS converts + // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators + M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]); + M[N-1][15] = ((msg.length-1)*8) & 0xffffffff; + + // set initial hash value [§5.3.1] + var H0 = 0x67452301; + var H1 = 0xefcdab89; + var H2 = 0x98badcfe; + var H3 = 0x10325476; + var H4 = 0xc3d2e1f0; + + // HASH COMPUTATION [§6.1.2] + + var W = new Array(80); var a, b, c, d, e; + for (var i=0; i>>(32-n)); +}; + + +/** + * Hexadecimal representation of a number. + * @private + */ +Sha1.toHexStr = function(n) { + // note can't use toString(16) as it is implementation-dependant, + // and in IE returns signed numbers when used on full words + var s="", v; + for (var i=7; i>=0; i--) { v = (n>>>(i*4)) & 0xf; s += v.toString(16); } + return s; +}; + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +/** Extend String object with method to encode multi-byte string to utf8 + * - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html */ +if (typeof String.prototype.utf8Encode == 'undefined') { + String.prototype.utf8Encode = function() { + return unescape( encodeURIComponent( this ) ); + }; +} + +/** Extend String object with method to decode utf8 string to multi-byte */ +if (typeof String.prototype.utf8Decode == 'undefined') { + String.prototype.utf8Decode = function() { + try { + return decodeURIComponent( escape( this ) ); + } catch (e) { + return this; // invalid UTF-8? return as-is + } + }; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +if (typeof module != 'undefined' && module.exports) module.exports = Sha1; // CommonJs export +if (typeof define == 'function' && define.amd) define([], function() { return Sha1; }); // AMD diff --git a/addons/point_of_sale/views/point_of_sale.xml b/addons/point_of_sale/views/point_of_sale.xml index b476328700a..1d8cc009793 100644 --- a/addons/point_of_sale/views/point_of_sale.xml +++ b/addons/point_of_sale/views/point_of_sale.xml @@ -10,6 +10,7 @@ + diff --git a/addons/pos_hr/models/__init__.py b/addons/pos_hr/models/__init__.py index 5e5c6fc9b07..784da4cda9e 100644 --- a/addons/pos_hr/models/__init__.py +++ b/addons/pos_hr/models/__init__.py @@ -2,3 +2,4 @@ from . import pos_config from . import pos_order +from . import hr_employee diff --git a/addons/pos_hr/models/hr_employee.py b/addons/pos_hr/models/hr_employee.py new file mode 100644 index 00000000000..fc5a3f9b154 --- /dev/null +++ b/addons/pos_hr/models/hr_employee.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import hashlib + +from odoo import api, models + +class HrEmployee(models.Model): + + _inherit = 'hr.employee' + + def get_barcodes_and_pin_hashed(self): + if not self.env.user.has_group('point_of_sale.group_pos_user'): + return [] + # Apply visibility filters (record rules) + visible_emp_ids = self.search([('id', 'in', self.ids)]) + employees_data = self.sudo().search_read([('id', 'in', visible_emp_ids.ids)], ['barcode', 'pin']) + + for e in employees_data: + e['barcode'] = hashlib.sha1(e['barcode'].encode('utf8')).hexdigest() if e['barcode'] else False + e['pin'] = hashlib.sha1(e['pin'].encode('utf8')).hexdigest() if e['pin'] else False + return employees_data diff --git a/addons/pos_hr/static/src/js/gui.js b/addons/pos_hr/static/src/js/gui.js index b0faa322924..45c20b7551e 100644 --- a/addons/pos_hr/static/src/js/gui.js +++ b/addons/pos_hr/static/src/js/gui.js @@ -56,7 +56,7 @@ gui.Gui.include({ self.show_popup('password',{ 'title': _t('Password ?'), confirm: function (pw) { - if (pw !== password) { + if (Sha1.hash(pw) !== password) { self.show_popup('error', _t('Incorrect Password')); reject(); } else { diff --git a/addons/pos_hr/static/src/js/models.js b/addons/pos_hr/static/src/js/models.js index 54a6b1d31d1..4d9e0e20e23 100644 --- a/addons/pos_hr/static/src/js/models.js +++ b/addons/pos_hr/static/src/js/models.js @@ -2,6 +2,7 @@ odoo.define('pos_hr.employees', function (require) { "use strict"; var models = require('point_of_sale.models'); +var rpc = require('web.rpc'); models.load_models([{ model: 'res.users', @@ -27,7 +28,7 @@ models.load_models([{ }, },{ model: 'hr.employee', - fields: ['name', 'id', 'barcode', 'pin', 'user_id'], + fields: ['name', 'id', 'user_id'], domain: function(self){ return [['company_id', '=', self.config.company_id[0]]]; }, loaded: function(self, employees) { if (self.config.module_pos_hr) { @@ -53,4 +54,29 @@ models.load_models([{ } } }]); + +var posmodel_super = models.PosModel.prototype; +models.PosModel = models.PosModel.extend({ + load_server_data: function () { + var self = this; + return posmodel_super.load_server_data.apply(this, arguments).then(function () { + var employee_ids = _.map(self.employees, function(employee){return employee.id;}); + var records = rpc.query({ + model: 'hr.employee', + method: 'get_barcodes_and_pin_hashed', + args: [employee_ids], + }); + return records.then(function (employee_data) { + self.employees.forEach(function (employee) { + var data = _.findWhere(employee_data, {'id': employee.id}); + if (data !== undefined){ + employee.barcode = data.barcode; + employee.pin = data.pin; + } + }); + }); + }); + }, +}); + }); diff --git a/addons/pos_hr/static/src/js/screens.js b/addons/pos_hr/static/src/js/screens.js index 9b7fe1c4a9a..bef83516f0f 100644 --- a/addons/pos_hr/static/src/js/screens.js +++ b/addons/pos_hr/static/src/js/screens.js @@ -17,7 +17,7 @@ ScreenWidget.include({ var self = this; var employees = this.pos.employees; for(var i = 0, len = employees.length; i < len; i++){ - if(employees[i].barcode === code.code){ + if(employees[i].barcode === Sha1.hash(code.code)){ if (employees[i].id !== this.pos.get_cashier().id && employees[i].pin) { return this.gui.ask_password(employees[i].pin).then(function(){ self.pos.set_cashier(employees[i]);