[FIX] hw_drivers: IoT Box connection

In Chrome 84, mixed content will be completely blocked for security
reasons ([see blog](https://blog.chromium.org/2019/10/no-more-mixed-messages-about-https.html))
Other browsers will probably follow in the next months.

As IoT Boxes don't have a valid certificate before connecting to a DB,
we used mixed passive content to communicate with the boxes.
This won't be possible anymore, and we have no possible way to
communicate directly from the browser to the box.

- When an IoT Box boots without registered DB, a unique code will be
 created with a validity of 5 minutes.
- This code will be shown on the customer display and printed on the
 status ticket.
- The box will call a route on odoo.com and a record will be created
 in odoo.com with the unique code.
- The user will have to enter the code manually in his DB to connect
 to the IoT Box, the DB will then contact odoo.com to search for a
 record containing the unique code. If it's found, the
 `openerp.enterprise.database` will be added to the record.
- The box will then query odoo.com at regular intervals (10 seconds)
 to check if a DB is linked to the code.

closes odoo/odoo#53623

Taskid: 2246535
X-original-commit: c55f12e32598a713105f3c049ac053e46ba15bb2
Related: odoo/enterprise#11421
Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
This commit is contained in:
Antoine Prieels
2020-06-25 06:08:33 +00:00
parent bc81853ba5
commit c5e1af7a35
4 changed files with 124 additions and 30 deletions
+63
View File
@@ -1,10 +1,12 @@
#!/usr/bin/python3
from base64 import b64decode
from datetime import datetime, timedelta
from dbus.mainloop.glib import DBusGMainLoop
from importlib import util
import json
import logging
import os
import requests
import socket
import subprocess
import sys
@@ -289,6 +291,63 @@ class EventManager(object):
event_manager = EventManager()
#----------------------------------------------------------
# ConnectionManager
#----------------------------------------------------------
class ConnectionManager(Thread):
def __init__(self):
super(ConnectionManager, self).__init__()
self.pairing_code = False
self.pairing_uuid = False
def run(self):
if not helpers.get_odoo_server_url():
end_time = datetime.now() + timedelta(minutes=5)
while (datetime.now() < end_time):
self._connect_box()
time.sleep(10)
self.pairing_code = False
self.pairing_uuid = False
self._refresh_displays()
def _connect_box(self):
data = {
'jsonrpc': 2.0,
'params': {
'pairing_code': self.pairing_code,
'pairing_uuid': self.pairing_uuid,
}
}
urllib3.disable_warnings()
req = requests.post('https://iot-proxy.odoo.com/odoo-enterprise/iot/connect-box', json=data, verify=False)
result = req.json().get('result', {})
if all(key in result for key in ['pairing_code', 'pairing_uuid']):
self.pairing_code = result['pairing_code']
self.pairing_uuid = result['pairing_uuid']
elif all(key in result for key in ['url', 'token', 'db_uuid', 'enterprise_code']):
self._connect_to_server(result['url'], result['token'], result['db_uuid'], result['enterprise_code'])
def _connect_to_server(self, url, token, db_uuid, enterprise_code):
if db_uuid and enterprise_code:
helpers.add_credential(db_uuid, enterprise_code)
# Save DB URL and token
subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot'])
# Notify the DB, so that the kanban view already shows the IoT Box
m.send_alldevices()
# Restart to checkout the git branch, get a certificate, load the IoT handlers...
subprocess.check_call(["sudo", "service", "odoo", "restart"])
def _refresh_displays(self):
"""Refresh all displays to hide the pairing code"""
for d in iot_devices:
if iot_devices[d].device_type == 'display':
iot_devices[d].action({
'action': 'display_refresh'
})
#----------------------------------------------------------
# Manager
@@ -386,6 +445,10 @@ class Manager(Thread):
# Must be started from main thread
DBusGMainLoop(set_as_default=True)
cm = ConnectionManager()
cm.daemon = True
cm.start()
m = Manager()
m.daemon = True
m.start()
@@ -13,6 +13,11 @@ from odoo import http
from odoo.addons.hw_drivers.tools import helpers
from odoo.addons.hw_drivers.controllers.driver import Driver, event_manager, iot_devices
try:
from odoo.addons.hw_drivers.controllers.driver import cm
except:
cm = None
path = os.path.realpath(os.path.join(os.path.dirname(__file__), '../../views'))
loader = jinja2.FileSystemLoader(path)
@@ -215,4 +220,5 @@ class DisplayController(http.Controller):
'cust_js': cust_js,
'display_ifaces': display_ifaces,
'display_identifier': display_identifier,
'pairing_code': cm and cm.pairing_code,
})
@@ -20,6 +20,11 @@ from odoo.addons.hw_drivers.iot_handlers.interfaces.PrinterInterface import PPDs
from odoo.addons.hw_drivers.tools import helpers
from odoo.addons.hw_proxy.controllers.main import drivers as old_drivers
try:
from odoo.addons.hw_drivers.controllers.driver import cm
except:
cm = None
_logger = logging.getLogger(__name__)
def print_star_error(deviceId):
@@ -231,6 +236,7 @@ class PrinterDriver(Driver):
ip = ''
mac = ''
homepage = ''
pairing_code = ''
ssid = helpers.get_ssid()
wlan = '\nWireless network:\n%s\n\n' % ssid
@@ -256,11 +262,15 @@ class PrinterDriver(Driver):
mac = '\nMAC Address:\n%s\n' % helpers.get_mac_address()
homepage = '\nHomepage:\nhttp://%s:8069\n\n' % main_ips
code = cm and cm.pairing_code
if code:
pairing_code = '\nPairing Code:\n%s\n' % code
center = b'\x1b\x61\x01'
title = b'\n\x1b\x21\x30\x1b\x4d\x01IoTBox Status\x1b\x4d\x00\x1b\x21\x00\n'
cut = b'\x1d\x56\x41'
self.print_raw(center + title + wlan.encode() + mac.encode() + ip.encode() + homepage.encode() + cut + b'\n')
self.print_raw(center + title + wlan.encode() + mac.encode() + ip.encode() + homepage.encode() + pairing_code.encode() + cut + b'\n')
def open_cashbox(self):
"""Sends a signal to the current printer to open the connected cashbox."""
+44 -29
View File
@@ -22,7 +22,12 @@
background: linear-gradient(to right bottom, #77717e, #c9a8a9);
height: 100vh;
}
.pos-display-status {
.pos-display-boxes {
position: absolute;
right: 20px;
bottom: 20px;
}
.pos-display-box {
padding: 10px 20px;
background: rgba(0, 0, 0, 0.17);
border: 1px solid rgba(0, 0, 0, 0.06);
@@ -30,9 +35,10 @@
color: #fff;
border-radius: 8px;
width: 500px;
position: absolute;
right: 20px;
bottom: 20px;
margin-top: 20px;
}
.pos-display-box hr {
background-color: #fff;
}
.info-text {
font-size: 15px;
@@ -48,31 +54,40 @@
<img style="width: 150px;" src="/web/static/src/img/logo_inverse_white_206px.png">
<p class="mt-3" style="color: #fff;font-size: 30px;">IoTBox</p>
</div>
<div class="pos-display-status">
<h4 class="text-center mb-3">POS Client display</h4>
<table class="table table-hover table-sm table-pos-info">
<thead>
<tr>
<th>Interface</th>
<th>IP</th>
</tr>
</thead>
<tbody>
{% for display_iface in display_ifaces -%}
<tr>
<td><i class="fa fa-{{ display_iface.icon }}"/> {{ display_iface.essid }}</td>
<td>{{ display_iface.addr }}</td>
</tr>
{%- endfor %}
</tbody>
</table>
<p class="mb-2 info-text">
<i class="fa fa-info-circle mr-1"></i>The customer cart will be displayed here once a Point of Sale session is started.
</p>
<p class="mb-2 info-text">
<i class="fa fa-info-circle mr-1"></i>Odoo version 11 or above is required.
</p>
<div class="error-message alert alert-danger mb-2 d-none" role="alert" />
<div class="pos-display-boxes">
{% if pairing_code %}
<div class="pos-display-box">
<h4 class="text-center mb-3">Pairing Code</h4>
<hr/>
<h4 class="text-center mb-3">{{ pairing_code }}</h4>
</div>
{% endif %}
<div class="pos-display-box">
<h4 class="text-center mb-3">POS Client display</h4>
<table class="table table-hover table-sm table-pos-info">
<thead>
<tr>
<th>Interface</th>
<th>IP</th>
</tr>
</thead>
<tbody>
{% for display_iface in display_ifaces -%}
<tr>
<td><i class="fa fa-{{ display_iface.icon }}"/> {{ display_iface.essid }}</td>
<td>{{ display_iface.addr }}</td>
</tr>
{%- endfor %}
</tbody>
</table>
<p class="mb-2 info-text">
<i class="fa fa-info-circle mr-1"></i>The customer cart will be displayed here once a Point of Sale session is started.
</p>
<p class="mb-2 info-text">
<i class="fa fa-info-circle mr-1"></i>Odoo version 11 or above is required.
</p>
<div class="error-message alert alert-danger mb-2 d-none" role="alert" />
</div>
</div>
</div>
</body>