changeset e4bfb7718b16 in tryton:default
details: https://hg.tryton.org/tryton?cmd=changeset;node=e4bfb7718b16
description:
        Protect trusted devices against brute force attack

        issue9386
        review321511002
diffstat:

 CHANGELOG               |   1 +
 tryton/device_cookie.py |  76 +++++++++++++++++++++++++++++++++++++++++++++++++
 tryton/rpc.py           |   4 +-
 3 files changed, 80 insertions(+), 1 deletions(-)

diffs (116 lines):

diff -r 6bca8daa4d9c -r e4bfb7718b16 CHANGELOG
--- a/CHANGELOG Sat Feb 20 00:53:18 2021 +0100
+++ b/CHANGELOG Sun Feb 21 16:23:11 2021 +0100
@@ -1,3 +1,4 @@
+* Handle device cookie
 * Add breadcrumb as title of window form
 * Display revision on dialog
 * Execute report asynchronously
diff -r 6bca8daa4d9c -r e4bfb7718b16 tryton/device_cookie.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tryton/device_cookie.py   Sun Feb 21 16:23:11 2021 +0100
@@ -0,0 +1,76 @@
+# This file is part of Tryton.  The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+import os
+import json
+import logging
+from threading import Lock
+
+from tryton.config import get_config_dir, CONFIG
+
+logger = logging.getLogger(__name__)
+COOKIES_PATH = os.path.join(get_config_dir(), 'device_cookies')
+
+
+_lock = Lock()
+
+
+def renew():
+    from tryton.common import common
+
+    def set_cookie(new_cookie):
+        try:
+            new_cookie = new_cookie()
+        except Exception:
+            logger.error("Cannot renew device cookie", exc_info=True)
+        else:
+            _set(new_cookie)
+
+    current_cookie = get()
+    common.RPCExecute(
+        'model', 'res.user.device', 'renew', current_cookie,
+        process_exception=False, callback=set_cookie)
+
+
+def get():
+    cookies = _load()
+    return cookies.get(_key())
+
+
+def _key():
+    from tryton import common
+
+    host = CONFIG['login.host']
+    hostname = common.get_hostname(host)
+    port = common.get_port(host)
+    database = CONFIG['login.db']
+    username = CONFIG['login.login']
+
+    return '%(username)s@%(hostname)s:%(port)s/%(database)s' % {
+        'username': username,
+        'hostname': hostname,
+        'port': port,
+        'database': database,
+        }
+
+
+def _set(cookie):
+    cookies = _load()
+    cookies[_key()] = cookie
+    try:
+        with _lock:
+            with open(COOKIES_PATH, 'w') as cookies_file:
+                json.dump(cookies, cookies_file)
+    except Exception:
+        logger.error('Unable to save cookies file')
+
+
+def _load():
+    if not os.path.isfile(COOKIES_PATH):
+        return {}
+    try:
+        with open(COOKIES_PATH) as cookies:
+            cookies = json.load(cookies)
+    except Exception:
+        logger.error("Unable to load device cookies file", exc_info=True)
+        cookies = {}
+    return cookies
diff -r 6bca8daa4d9c -r e4bfb7718b16 tryton/rpc.py
--- a/tryton/rpc.py     Sat Feb 20 00:53:18 2021 +0100
+++ b/tryton/rpc.py     Sun Feb 21 16:23:11 2021 +0100
@@ -11,7 +11,7 @@
 
 from functools import partial
 
-from tryton import bus
+from tryton import bus, device_cookies
 from tryton.jsonrpc import ServerProxy, ServerPool, Fault
 from tryton.fingerprints import Fingerprints
 from tryton.config import get_config_dir
@@ -80,6 +80,7 @@
     database = CONFIG['login.db']
     username = CONFIG['login.login']
     language = CONFIG['client.lang']
+    parameters['device_cookie'] = device_cookies.get()
     connection = ServerProxy(hostname, port, database)
     logging.getLogger(__name__).info('common.db.login(%s, %s, %s)'
         % (username, 'x' * 10, language))
@@ -91,6 +92,7 @@
         CONNECTION.close()
     CONNECTION = ServerPool(
         hostname, port, database, session=session, cache=not CONFIG['dev'])
+    device_cookies.renew()
     bus.listen(CONNECTION)
 
 

Reply via email to