changeset 492:23c6cf01d237

working kinda
author Matt Johnston <matt@ucc.asn.au>
date Tue, 11 Feb 2014 23:47:53 +0800
parents f2e990b99637
children 1800a07f20e0
files web/config.py web/log.py web/secure.py web/templog.py web/views/set.tpl
diffstat 5 files changed, 99 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/web/config.py	Tue Feb 11 22:11:03 2014 +0800
+++ b/web/config.py	Tue Feb 11 23:47:53 2014 +0800
@@ -4,11 +4,15 @@
 SERIAL_HOST='home.example.com'
 SERIAL_PORT=1999
 
+
 DATA_PATH = '/home/matt/templog/web/data'
 
-HMAC_KEY = 'a hmac key' # override in local config file
-
-ALLOWED_USERS = [] # local config. list of sha1 hashes of client ssl keys
+# 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'
 
 UPDATE_URL = 'http://evil.ucc.asn.au/~matt/templog/update'
 
--- a/web/log.py	Tue Feb 11 22:11:03 2014 +0800
+++ b/web/log.py	Tue Feb 11 23:47:53 2014 +0800
@@ -15,6 +15,7 @@
 import struct
 import binascii
 import json
+import subprocess
 from colorsys import hls_to_rgb
 
 import config
@@ -269,24 +270,25 @@
     debugf.write("Updated sensors in %.2f secs\n" % timedelta)
     debugf.flush()
 
+_FIELD_DEFAULTS = {
+    'fridge_setpoint': 16,
+    'fridge_difference': 0.2,
+    'overshoot_delay': 720, # 12 minutes
+    'overshoot_factor': 1, # ºC
+    'disabled': False,
+    'nowort': True,
+    'fridge_range_lower': 3,
+    'fridge_range_upper': 3,
+    }
+
 def get_params():
-    _FIELD_DEFAULTS = {
-        'fridge_setpoint': 16,
-        'fridge_difference': 0.2,
-        'overshoot_delay': 720, # 12 minutes
-        'overshoot_factor': 1, # ºC
-        'disabled': False,
-        'nowort': True,
-        'fridge_range_lower': 3,
-        'fridge_range_upper': 3,
-        }
 
     r = []
 
     vals = read_current_params()
 
     for k, v in _FIELD_DEFAULTS.iteritems():
-        n = {'name': k, 'value': vals[k]}
+        n = {'name': k, 'value': type(v)(vals[k])}
         if type(v) is bool:
             kind = 'yesno'
         else:
@@ -305,6 +307,54 @@
 
     return json.dumps(r, sort_keys=True, indent=4)
 
+def send_params(params):
+    # 'templog_receive' is ignored due to authorized_keys
+    # restrictions
+    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"
 
-def get_csrf_blob(user_ident):
-    return "aaa"
+    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)
+
+    if ta == int:
+        ta = float
+    if tb == int:
+        tb = float
+
+    return (ta == tb)
+
+def update_params(p):
+    params = {}
+    for i in p:
+        params[i['name']] = i['value']
+
+    if params.viewkeys() != _FIELD_DEFAULTS.viewkeys():
+        diff = params.viewkeys() ^ _FIELD_DEFAULTS.viewkeys()
+        return "Key mismatch, difference %s" % str(diff)
+
+    for k, v in params.items():
+        if not same_type(v, _FIELD_DEFAULTS[k]):
+            return "Bad type for %s, %s vs %s" % (k , type(v), type(_FIELD_DEFAULTS[k]))
+
+    ret = send_params(params) 
+    if ret is not True:
+        return "Failed sending params: %s" % ret
+
+    return True
+
+
+
--- a/web/secure.py	Tue Feb 11 22:11:03 2014 +0800
+++ b/web/secure.py	Tue Feb 11 23:47:53 2014 +0800
@@ -54,27 +54,33 @@
 def check_csrf_blob(blob):
     toks = blob.split('-')
     if len(toks) != 3:
+        print>>sys.stderr, "wrong toks"
         return False
 
     user, expiry, mac = toks
     if user != get_user_hash():
+        print>>sys.stderr, "wrong user"
         return False
 
     try:
         exp = int(expiry)
     except ValueError:
+        print>>sys.stderr, "failed exp"
         return False
 
     if exp < 1000000000:
         return False
 
-    if exp > time.time():
+    if exp < time.time():
+        print>>sys.stderr, "expired %d %d" % (exp, time.time())
         return False
 
     check_content = "%s-%s" % (user, expiry)
-    check_mac = hmac.new(_csrf_key, content).hexdigest()
+    check_mac = hmac.new(_csrf_key, check_content).hexdigest()
     if mac == check_mac:
+        print>>sys.stderr, "good hmac"
         return True
 
+    print>>sys.stderr, "fail"
     return False
 
--- a/web/templog.py	Tue Feb 11 22:11:03 2014 +0800
+++ b/web/templog.py	Tue Feb 11 23:47:53 2014 +0800
@@ -50,12 +50,21 @@
     return log.graph_png(start_epoch, length_minutes * 60)
 
 @route('/set/update', method='post')
-def update():
+def set_update():
     post_json = json.loads(request.forms.data)
 
     csrf_blob = post_json['csrf_blob']
 
-    return str(post_json['params'])
+    if not secure.check_csrf_blob(csrf_blob):
+        bottle.response.status = 403
+        return "Bad csrf"
+
+    ret = log.update_params(post_json['params'])
+    if not ret is True:
+        bottle.response.status = 403
+        return ret
+        
+    return "Good"
 
 @route('/set')
 def set():
@@ -139,4 +148,3 @@
 if __name__ == '__main__':
     main()
     
-
--- a/web/views/set.tpl	Tue Feb 11 22:11:03 2014 +0800
+++ b/web/views/set.tpl	Tue Feb 11 23:47:53 2014 +0800
@@ -76,6 +76,10 @@
     margin-top: 10pt;
 }
 
+span.inputrow {
+    //vertical-align: center;
+}
+
 </style>
 <title>Set templog</title>
 </head>
@@ -85,9 +89,11 @@
 <div id="{id}">
 <span class="existing">{title} <span id="oldvalue">{oldvaluetext}{unit}</span></span>
 <br/>
+<span class="inputrow">
 <input type="number" class="input" name="input_{name}" />
 <input type="button" class="button_down" value="-"/>
 <input type="button" class="button_up" value="+"/>
+</span>
 </div>
 </script>
 
@@ -95,8 +101,10 @@
 <div id="{id}">
 <span class="existing">{title} <span id="oldvalue">{oldvaluetext}</span></span>
 <br/>
+<span class="inputrow">
 <input type="button" class="button_no yesno" value="No"/>
 <input type="button" class="button_yes yesno" value="Yes"/>
+</span>
 </div>
 </script>
 
@@ -158,7 +166,8 @@
 
         req.fail(function(data, status, hdr) {
             self.trigger("status", 
-                "Failed: " + status + "\n" + hdr.responseText)
+                "Failed: "  + data.status + ' '
+                + data.statusText + ' ' + data.responseText)
         });
     }
 }