changeset 301:47c259458160

merge
author Matt Johnston <matt@ucc.asn.au>
date Sat, 06 Jul 2019 19:17:21 +0800
parents 65a6b56fd18a (current diff) b0c0e88cbfcc (diff)
children ef407d48cbe5
files
diffstat 10 files changed, 75 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/py/config.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/py/config.py	Sat Jul 06 19:17:21 2019 +0800
@@ -12,7 +12,7 @@
 
 PARAMS_FILE = os.path.join(os.path.dirname(__file__), 'tempserver.conf')
 
-SENSOR_BASE_DIR = '/sys/devices/w1_bus_master2'
+SENSOR_BASE_DIR = '/sys/devices/w1_bus_master1'
 FRIDGE_GPIO_PIN = 17
 #WORT_NAME = '28-0000042cf4dd'
 #FRIDGE_NAME = '28-0000042cccc4'
--- a/py/gpio_rpi.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/py/gpio_rpi.py	Sat Jul 06 19:17:21 2019 +0800
@@ -1,7 +1,5 @@
 import os
 
-import RPi.GPIO as GPIO
-
 from utils import L,D,EX,W
 
 __all__ = ["Gpio"]
--- a/py/requirements.txt	Mon Feb 27 23:27:45 2017 +0800
+++ b/py/requirements.txt	Sat Jul 06 19:17:21 2019 +0800
@@ -1,9 +1,5 @@
-# sha256: nkIlLxfR3YnuMXReDE--WIYsJRR-sO9SlcnNm8tOosE
 lockfile==0.10.2
-
-# sha256: 2zFqD89UuXAsr2ymGbdr4l1T9e4Hgbr_C7ni4DVfryQ
-python-daemon==2.0.5
-
-# sha256: 6vR5rMmP_uCgKYgkZevyHzwwLhuUpBsWyKWmlbxhSQA
+python-daemon==2.2.3
 aiohttp==0.16.3
-
+chardet==2.3.0
+docutils==0.14
--- a/py/sensor_ds18b20.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/py/sensor_ds18b20.py	Sat Jul 06 19:17:21 2019 +0800
@@ -62,7 +62,7 @@
                 D("no match")
                 return None
             temp = int(match.groups(1)[0]) / 1000.0
-            if temp > 80:
+            if temp > 80 or temp == 0:
                 E("Problem reading sensor '%s': %f" % (s, temp))
                 return None
             return temp
--- a/py/uploader.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/py/uploader.py	Sat Jul 06 19:17:21 2019 +0800
@@ -71,7 +71,7 @@
             yield from self.send(tosend)
             readings = None
             D("Sent updated %d readings" % nreadings)
-        except aiohttp.errors.ClientError as e:
+        except aiohttp.ClientResponseError as e:
             self.limitlog.log("Error with uploader: %s" % str(e))
         except asyncio.TimeoutError as e:
             self.limitlog.log("uploader http timed out: %s" % str(e))
--- a/web/config.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/web/config.py	Sat Jul 06 19:17:21 2019 +0800
@@ -9,13 +9,12 @@
 
 # local config items
 HMAC_KEY = 'a hmac key' 
-ALLOWED_USERS = [] # list of sha1 hashes of client ssl keys
-SSH_HOST = 'remotehost'
-SSH_KEYFILE = '/home/matt/.ssh/somekey'
-SSH_PROG = 'ssh'
+ALLOWED_USERS = [] # list of hashes allowed, as provided by the Email link
 
 UPDATE_URL = 'http://evil.ucc.asn.au/~matt/templog/update'
 
+EMAIL = "[email protected]"
+
 GRAPH_WIDTH = 600
 GRAPH_HEIGHT = 700
 ZOOM = 1
@@ -24,15 +23,18 @@
 
 LINE_WIDTH = 2
 
-SENSOR_NAMES = {'sensor_28 CE B2 1A 03 00 00 99': "Old Fridge",
+SENSOR_NAMES = {
+    'sensor_28 CE B2 1A 03 00 00 99': "Old Fridge",
     'sensor_28 CC C1 1A 03 00 00 D4': "Old Ambient",
     'sensor_28 49 BC 1A 03 00 00 54': "Old Wort",
     'sensor_voltage': 'Voltage',
     'sensor_fridge_setpoint': 'Setpoint',
     'sensor_fridge_on': 'Cool',
-    'sensor_28-0000042cf4dd': "Wort",
+    'sensor_28-0000042cf4dd': "New Old Wort",
+    'sensor_28-0000042d36cc': "Wort",
     'sensor_28-0000042cccc4': "OldFridge",
-    'sensor_28-0000042c6dbb': "Fridge",
+    'sensor_28-0000042c6dbb': "New Old Fridge",
+    'sensor_28-0000068922df': "Fridge",
     'sensor_internal': "Processor",
     }
 
--- a/web/log.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/web/log.py	Sat Jul 06 19:17:21 2019 +0800
@@ -221,8 +221,9 @@
 
 def record_debug(params):
     f = debug_file('a+')
-    f.write('===== %s =====\n' % time.strftime('%a, %d %b %Y %H:%M:%S'))
+    f.write('===== start %s =====\n' % time.strftime('%a, %d %b %Y %H:%M:%S'))
     json.dump(params, f, sort_keys=True, indent=4)
+    f.write('===== end %s =====\n' % time.strftime('%a, %d %b %Y %H:%M:%S'))
     f.flush()
     return f
 
@@ -298,6 +299,13 @@
     'fridge_range_upper': 3,
     }
 
+def fake_params():
+    """ for quicker testing """
+    r = []
+    r.append({'name': 'going', 'value': 'true', 'kind': 'yesno', 'title': 'going'})
+    r.append({'name': 'temperature', 'value': 12.5, 'kind': 'number', 'title': 'temperature', 'digits': 1, 'amount': 0.1, 'unit': '°'})
+    return r
+
 def get_params():
     """ Can return None if there aren't any parameters yet,
     otherwise returns the parameter list """
@@ -328,26 +336,6 @@
 
     return json.dumps(r, sort_keys=True, indent=4)
 
-def send_params(params):
-    # 'templog_receive' is ignored due to authorized_keys
-    # restrictions. the rpi has authorized_keys with
-    # command="/home/matt/templog/venv/bin/python /home/matt/templog/py/receive.py",no-pty,no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3NzaC....
-    args = [config.SSH_PROG, '-i', config.SSH_KEYFILE,
-        config.SSH_HOST, 'templog_receive']
-    try:
-        p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-        (out, err) = p.communicate(json.dumps(params))
-    except OSError, e:
-        print>>sys.stderr, e
-        return "Failed update"
-
-    if 'Good Update' in out:
-        return True
-
-    print>>sys.stderr, "Strange return from update:"
-    print>>sys.stderr, out
-    return "Unexpected update result"
-
 def same_type(a, b):
     ta = type(a)
     tb = type(b)
--- a/web/secure.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/web/secure.py	Sat Jul 06 19:17:21 2019 +0800
@@ -11,47 +11,39 @@
 
 import config
 
-__all__ = ["get_csrf_blob", "check_csrf_blob", "setup_csrf", "get_user_hash",
-"check_user_hash"]
+__all__ = [
+    "get_csrf_blob", 
+    "check_csrf_blob", 
+    "setup_csrf", 
+    "check_cookie",
+    "init_cookie",
+]
+
+AUTH_COOKIE = 'templogauth'
+AUTH_COOKIE_LEN = 16
 
 HASH=hashlib.sha1
 
 CLEAN_RE = re.compile('[^a-z0-9A-Z]')
 
-def clean_hash(h):
-    return CLEAN_RE.sub('', h.lower())
-
-def get_user_hash():
-    """
-    Uses the following apache config. 
-    Needs a separate port or IP to no-certificate SSL, SNI isn't good enough.
-
-    <location /~matt/templog/set>
-    Require all granted
-    SSLVerifyClient optional_no_ca
-    SSLVerifyDepth 1
-    SSLOptions +StdEnvVars +ExportCertData +OptRenegotiate
-    </location>
-    """
+def cookie_hash(c):
+    return hashlib.sha256(c).hexdigest()
 
-    verify = bottle.request.environ.get('SSL_CLIENT_VERIFY', '')
-    if not (verify == 'GENEROUS' or verify == 'SUCCESS'):
-        return 'FAILVERIFY'
-    blob = bottle.request.environ.get('SSL_CLIENT_CERT')
-    if not blob:
-        return 'NOCERT'
+def init_cookie():
+    """ Generates a new httponly auth cookie if required. 
+    Returns the hash of the cookie (new or existing)
+    """
+    c = bottle.request.get_cookie(AUTH_COOKIE)
+    if not c:
+        c = binascii.hexlify(os.urandom(AUTH_COOKIE_LEN))
+        bottle.response.set_cookie(AUTH_COOKIE, c, secure=True, httponly=True)
+    return cookie_hash(c)
 
-    b64 = ''.join(l for l in blob.split('\n')
-        if not l.startswith('-'))
-
-    return HASH(binascii.a2b_base64(b64)).hexdigest()
-
-def check_user_hash(allowed_users):
-    current_hash = clean_hash(get_user_hash())
-    for a in allowed_users:
-        if current_hash == clean_hash(a):
-            return True
-    return False
+def check_cookie(allowed_users):
+    c = bottle.request.get_cookie(AUTH_COOKIE)
+    if not c:
+        return False
+    return cookie_hash(c) in allowed_users
 
 def setup_csrf():
     NONCE_SIZE=16
@@ -72,7 +64,7 @@
 
 def get_csrf_blob():
     expiry = int(config.CSRF_TIMEOUT + time.time())
-    content = '%s-%s' % (get_user_hash(), expiry)
+    content = '%s-%s' % (init_cookie(), expiry)
     mac = hmac.new(_csrf_key, content).hexdigest()
     return "%s-%s" % (content, mac)
 
@@ -83,7 +75,7 @@
         return False
 
     user, expiry, mac = toks
-    if user != get_user_hash():
+    if user != init_cookie():
         print>>sys.stderr, "wrong user"
         return False
 
--- a/web/templog.py	Mon Feb 27 23:27:45 2017 +0800
+++ b/web/templog.py	Sat Jul 06 19:17:21 2019 +0800
@@ -69,7 +69,7 @@
 
 @route('/set/update', method='post')
 def set_update():
-    if not secure.check_user_hash(config.ALLOWED_USERS):
+    if not secure.check_cookie(config.ALLOWED_USERS):
         # the "Save" button should be disabled if the cert wasn't
         # good
         response.status = 403
@@ -92,9 +92,13 @@
 
 @route('/set')
 def set():
-    allowed = ["false", "true"][secure.check_user_hash(config.ALLOWED_USERS)]
+    cookie_hash = secure.init_cookie()
+    allowed = ["false", "true"][secure.check_cookie(config.ALLOWED_USERS)]
     response.set_header('Cache-Control', 'no-cache')
-    inline_data = log.get_params()
+    if request.query.fake:
+        inline_data = log.fake_params()
+    else:
+        inline_data = log.get_params()
     if not inline_data:
         response.status = 503 # Service Unavailable
         return bottle.template('noparamsyet')
@@ -102,7 +106,9 @@
     return bottle.template('set', 
         inline_data = inline_data,
         csrf_blob = secure.get_csrf_blob(),
-        allowed = allowed)
+        allowed = allowed,
+        cookie_hash = cookie_hash,
+        email = urllib.quote(config.EMAIL))
 
 def get_request_zoom():
     """ returns (length, end) tuple.
--- a/web/views/set.tpl	Mon Feb 27 23:27:45 2017 +0800
+++ b/web/views/set.tpl	Sat Jul 06 19:17:21 2019 +0800
@@ -21,6 +21,10 @@
     font-family: sans-serif;
 }
 
+a {
+    color: #000;
+}
+
 input {
     border: 2px solid transparent;
     border-radius: 4px;
@@ -29,6 +33,8 @@
     padding: 0;
     font-size: 30pt;
     height: 34pt;
+    vertical-align: middle;
+    line-height: 1em;
 }
 
 input[type="button"] {
@@ -37,7 +43,6 @@
     -webkit-appearance: none;
     -moz-appearance: none;
     background:#fff;
-    vertical-align: middle;
 }
 
 input[type="submit"] {
@@ -81,6 +86,10 @@
     //vertical-align: center;
 }
 
+#mailauth {
+    display: none;
+}
+
 </style>
 <title>Set templog</title>
 </head>
@@ -233,6 +242,7 @@
     if (!allowed) {
         $("#savebutton").attr("disabled", true);
         $('#status').text("No cert")
+        $('#mailauth').show();
     }
 
     $("#savebutton").click(function() {
@@ -318,6 +328,8 @@
 <span id="savebox">
 <input type="button" id="savebutton" value="Save"/>
 <span id="status"></span>
+<span id="mailauth"> <a href="mailto:{{email}}?Subject=Allow%20Templog&body=Hash%20is%20{{cookie_hash}}">Email</a>
+</span>
 </span>