[FIX] hw_drivers: HTTPS certificate info on IoT homepage
HTTPS certificate IoT issues can be complicated to troubleshoot as the information are not visible/given. This PR aim to share this information on the IoT box homepage. As there is a lot of possible causes for a given problem, a code is used that will be explained/detailed in Odoo's IoT documentation: https://github.com/odoo/documentation/pull/3818 OPW-3227004 closes odoo/odoo#116650 X-original-commit: 8bc6b2d0033676507f95b74b3ae383ab17164203 Signed-off-by: Loan (LSE) <lse@odoo.com> Signed-off-by: Sens Loan (lse) <lse@odoo.com>
This commit is contained in:
@@ -40,7 +40,7 @@ class DriverController(http.Controller):
|
||||
This route is called when we want to check if certificate is up-to-date
|
||||
Used in cron.daily
|
||||
"""
|
||||
helpers.check_certificate()
|
||||
helpers.get_certificate_status()
|
||||
|
||||
@http.route('/hw_drivers/event', type='json', auth='none', cors='*', csrf=False, save_session=False)
|
||||
def event(self, listener):
|
||||
|
||||
@@ -80,7 +80,10 @@ class Manager(Thread):
|
||||
helpers.start_nginx_server()
|
||||
if platform.system() == 'Linux':
|
||||
helpers.check_git_branch()
|
||||
helpers.check_certificate()
|
||||
is_certificate_ok, certificate_details = helpers.get_certificate_status()
|
||||
if not is_certificate_ok:
|
||||
_logger.warning("An error happened when trying to get the HTTPS certificate: %s",
|
||||
certificate_details)
|
||||
|
||||
# We first add the IoT Box to the connected DB because IoT handlers cannot be downloaded if
|
||||
# the identifier of the Box is not found in the DB. So add the Box to the DB.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
from enum import Enum
|
||||
from importlib import util
|
||||
import platform
|
||||
import io
|
||||
@@ -29,6 +30,13 @@ _logger = logging.getLogger(__name__)
|
||||
# Helper
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
class CertificateStatus(Enum):
|
||||
OK = 1
|
||||
NEED_REFRESH = 2
|
||||
ERROR = 3
|
||||
|
||||
|
||||
class IoTRestart(Thread):
|
||||
"""
|
||||
Thread to restart odoo server in IoT Box when we must return a answer before
|
||||
@@ -73,27 +81,42 @@ def start_nginx_server():
|
||||
def check_certificate():
|
||||
"""
|
||||
Check if the current certificate is up to date or not authenticated
|
||||
:return CheckCertificateStatus
|
||||
"""
|
||||
server = get_odoo_server_url()
|
||||
if server:
|
||||
if platform.system() == 'Windows':
|
||||
path = Path(get_path_nginx()).joinpath('conf/nginx-cert.crt')
|
||||
elif platform.system() == 'Linux':
|
||||
path = Path('/etc/ssl/certs/nginx-cert.crt')
|
||||
if path.exists():
|
||||
with path.open('r') as f:
|
||||
cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
|
||||
cert_end_date = datetime.datetime.strptime(cert.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%SZ") - datetime.timedelta(days=10)
|
||||
for key in cert.get_subject().get_components():
|
||||
if key[0] == b'CN':
|
||||
cn = key[1].decode('utf-8')
|
||||
if cn == 'OdooTempIoTBoxCertificate' or datetime.datetime.now() > cert_end_date:
|
||||
_logger.info(_('Your certificate %s must be updated') % (cn))
|
||||
load_certificate()
|
||||
else:
|
||||
_logger.info(_('Your certificate %s is valid until %s') % (cn, cert_end_date))
|
||||
else:
|
||||
load_certificate()
|
||||
|
||||
if not server:
|
||||
return {"status": CertificateStatus.ERROR,
|
||||
"error_code": "ERR_IOT_HTTPS_CHECK_NO_SERVER"}
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
path = Path(get_path_nginx()).joinpath('conf/nginx-cert.crt')
|
||||
elif platform.system() == 'Linux':
|
||||
path = Path('/etc/ssl/certs/nginx-cert.crt')
|
||||
|
||||
if not path.exists():
|
||||
return {"status": CertificateStatus.NEED_REFRESH}
|
||||
|
||||
try:
|
||||
with path.open('r') as f:
|
||||
cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
|
||||
except EnvironmentError:
|
||||
_logger.exception("Unable to read certificate file")
|
||||
return {"status": CertificateStatus.ERROR,
|
||||
"error_code": "ERR_IOT_HTTPS_CHECK_CERT_READ_EXCEPTION"}
|
||||
|
||||
cert_end_date = datetime.datetime.strptime(cert.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%SZ") - datetime.timedelta(days=10)
|
||||
for key in cert.get_subject().get_components():
|
||||
if key[0] == b'CN':
|
||||
cn = key[1].decode('utf-8')
|
||||
if cn == 'OdooTempIoTBoxCertificate' or datetime.datetime.now() > cert_end_date:
|
||||
message = _('Your certificate %s must be updated') % (cn)
|
||||
_logger.info(message)
|
||||
return {"status": CertificateStatus.NEED_REFRESH}
|
||||
else:
|
||||
message = _('Your certificate %s is valid until %s') % (cn, cert_end_date)
|
||||
_logger.info(message)
|
||||
return {"status": CertificateStatus.OK, "message": message}
|
||||
|
||||
def check_git_branch():
|
||||
"""
|
||||
@@ -165,6 +188,27 @@ def save_conf_server(url, token, db_uuid, enterprise_code):
|
||||
write_file('odoo-db-uuid.conf', db_uuid or '')
|
||||
write_file('odoo-enterprise-code.conf', enterprise_code or '')
|
||||
|
||||
def get_certificate_status(is_first=True):
|
||||
"""
|
||||
Will get the HTTPS certificate details if present. Will load the certificate if missing.
|
||||
|
||||
:param is_first: Use to make sure that the recursion happens only once
|
||||
:return: (bool, str)
|
||||
"""
|
||||
check_certificate_result = check_certificate()
|
||||
certificateStatus = check_certificate_result["status"]
|
||||
|
||||
if certificateStatus == CertificateStatus.ERROR:
|
||||
return False, check_certificate_result["error_code"]
|
||||
|
||||
if certificateStatus == CertificateStatus.NEED_REFRESH and is_first:
|
||||
certificate_process = load_certificate()
|
||||
if certificate_process is not True:
|
||||
return False, certificate_process
|
||||
return get_certificate_status(is_first=False) # recursive call to attempt certificate read
|
||||
return True, check_certificate_result.get("message",
|
||||
"The HTTPS certificate was generated correctly")
|
||||
|
||||
def get_img_name():
|
||||
major, minor = get_version().split('.')
|
||||
return 'iotboxv%s_%s.zip' % (major, minor)
|
||||
@@ -228,39 +272,52 @@ def load_certificate():
|
||||
"""
|
||||
db_uuid = read_file_first_line('odoo-db-uuid.conf')
|
||||
enterprise_code = read_file_first_line('odoo-enterprise-code.conf')
|
||||
if db_uuid and enterprise_code:
|
||||
url = 'https://www.odoo.com/odoo-enterprise/iot/x509'
|
||||
data = {
|
||||
'params': {
|
||||
'db_uuid': db_uuid,
|
||||
'enterprise_code': enterprise_code
|
||||
}
|
||||
if not (db_uuid and enterprise_code):
|
||||
return "ERR_IOT_HTTPS_LOAD_NO_CREDENTIAL"
|
||||
|
||||
url = 'https://www.odoo.com/odoo-enterprise/iot/x509'
|
||||
data = {
|
||||
'params': {
|
||||
'db_uuid': db_uuid,
|
||||
'enterprise_code': enterprise_code
|
||||
}
|
||||
urllib3.disable_warnings()
|
||||
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
|
||||
}
|
||||
urllib3.disable_warnings()
|
||||
http = urllib3.PoolManager(cert_reqs='CERT_NONE', retries=urllib3.Retry(4))
|
||||
try:
|
||||
response = http.request(
|
||||
'POST',
|
||||
url,
|
||||
body = json.dumps(data).encode('utf8'),
|
||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||
)
|
||||
result = json.loads(response.data.decode('utf8'))['result']
|
||||
if result:
|
||||
write_file('odoo-subject.conf', result['subject_cn'])
|
||||
if platform.system() == 'Linux':
|
||||
with writable():
|
||||
Path('/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem'])
|
||||
Path('/root_bypass_ramdisks/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem'])
|
||||
Path('/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem'])
|
||||
Path('/root_bypass_ramdisks/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem'])
|
||||
elif platform.system() == 'Windows':
|
||||
Path(get_path_nginx()).joinpath('conf/nginx-cert.crt').write_text(result['x509_pem'])
|
||||
Path(get_path_nginx()).joinpath('conf/nginx-cert.key').write_text(result['private_key_pem'])
|
||||
time.sleep(3)
|
||||
if platform.system() == 'Windows':
|
||||
odoo_restart(0)
|
||||
elif platform.system() == 'Linux':
|
||||
start_nginx_server()
|
||||
except Exception as e:
|
||||
_logger.exception("An error occurred while trying to reach odoo.com servers.")
|
||||
return "ERR_IOT_HTTPS_LOAD_REQUEST_EXCEPTION\n\n%s" % e
|
||||
|
||||
if response.status != 200:
|
||||
return "ERR_IOT_HTTPS_LOAD_REQUEST_STATUS %s\n\n%s" % (response.status, response.reason)
|
||||
|
||||
result = json.loads(response.data.decode('utf8'))['result']
|
||||
if not result:
|
||||
return "ERR_IOT_HTTPS_LOAD_REQUEST_NO_RESULT"
|
||||
|
||||
write_file('odoo-subject.conf', result['subject_cn'])
|
||||
if platform.system() == 'Linux':
|
||||
with writable():
|
||||
Path('/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem'])
|
||||
Path('/root_bypass_ramdisks/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem'])
|
||||
Path('/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem'])
|
||||
Path('/root_bypass_ramdisks/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem'])
|
||||
elif platform.system() == 'Windows':
|
||||
Path(get_path_nginx()).joinpath('conf/nginx-cert.crt').write_text(result['x509_pem'])
|
||||
Path(get_path_nginx()).joinpath('conf/nginx-cert.key').write_text(result['private_key_pem'])
|
||||
time.sleep(3)
|
||||
if platform.system() == 'Windows':
|
||||
odoo_restart(0)
|
||||
elif platform.system() == 'Linux':
|
||||
start_nginx_server()
|
||||
return True
|
||||
|
||||
def download_iot_handlers(auto=True):
|
||||
"""
|
||||
|
||||
@@ -76,6 +76,8 @@ class IoTboxHomepage(Home):
|
||||
else:
|
||||
network = 'Not Connected'
|
||||
|
||||
is_certificate_ok, certificate_details = helpers.get_certificate_status()
|
||||
|
||||
iot_device = []
|
||||
for device in iot_devices:
|
||||
iot_device.append({
|
||||
@@ -95,6 +97,8 @@ class IoTboxHomepage(Home):
|
||||
'network_status': network,
|
||||
'version': helpers.get_version(),
|
||||
'system': platform.system(),
|
||||
'is_certificate_ok': is_certificate_ok,
|
||||
'certificate_details': certificate_details,
|
||||
}
|
||||
|
||||
@http.route()
|
||||
|
||||
@@ -89,6 +89,11 @@
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
.warn-tr {
|
||||
color: #856404;
|
||||
background-color: #fff3cd;
|
||||
border: 2px solid #f3e4ce;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
@@ -189,6 +194,24 @@
|
||||
<td class="heading">Server</td>
|
||||
<td><a href='{{ server_status }}' target=_blank>{{ server_status }}<a class="btn btn-sm float-end" href='/server'>configure</a></td>
|
||||
</tr>
|
||||
<tr class="{{ 'warn-tr' if not is_certificate_ok }}">
|
||||
<td class="heading">HTTPS certificate</td>
|
||||
<td>
|
||||
{% if is_certificate_ok %}
|
||||
<details>
|
||||
<summary>OK</summary>
|
||||
<code>{{ certificate_details }}</code>
|
||||
</details>
|
||||
{% else %}
|
||||
Error code:
|
||||
{% set error_code = certificate_details.split(' ') | first | replace("_", "-") | lower %}
|
||||
{% set doc_url = 'https://www.odoo.com/documentation/master/applications/productivity/iot/config/https_certificate_iot.html#' ~ error_code %}
|
||||
<a target="_blank" class="btn btn-sm float-end" href="{{ doc_url }}">help</a>
|
||||
<br/>
|
||||
<code style="white-space: pre-wrap;">{{ certificate_details }}</code>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if server_status != "Not Configured" %}
|
||||
<tr>
|
||||
<td class="heading">Six payment terminal</td>
|
||||
|
||||
Reference in New Issue
Block a user