changeset 485:d68af9e84485

working
author Matt Johnston <matt@ucc.asn.au>
date Thu, 06 Feb 2014 22:45:16 +0800
parents 4a51e2bf689b
children bb713e6d0e48
files web/config.py web/log.py web/secure.py web/templog.py web/views/set.tpl
diffstat 5 files changed, 151 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/web/config.py	Thu Feb 06 20:35:43 2014 +0800
+++ b/web/config.py	Thu Feb 06 22:45:16 2014 +0800
@@ -44,6 +44,9 @@
 # determine by zooming in an image viewer
 GRAPH_LEFT_MARGIN = 63
 
+# 1 hour
+CSRF_TIMEOUT = 3600
+
 try:
     from localconfig import *
 except ImportError:
--- a/web/log.py	Thu Feb 06 20:35:43 2014 +0800
+++ b/web/log.py	Thu Feb 06 22:45:16 2014 +0800
@@ -285,3 +285,7 @@
         r.append(n)
 
     return json.dumps(r, sort_keys=True, indent=4)
+
+
+def get_csrf_blob(user_ident):
+    return "aaa"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/secure.py	Thu Feb 06 22:45:16 2014 +0800
@@ -0,0 +1,65 @@
+import os
+import time
+import fcntl
+import hmac
+import binascii
+import sys
+
+import config
+
+__all__ = ["get_csrf_blob", "check_csrf_blob", "setup_csrf"]
+
+def get_user_hash():
+    return "aaa"
+
+def setup_csrf():
+    NONCE_SIZE=16
+    global _csrf_fd, _csrf_key
+    _csrf_fd = open('%s/csrf.dat' % config.DATA_PATH, 'r+')
+
+    try:
+        fcntl.lockf(_csrf_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
+        os.fchmod(_csrf_fd.fileno(), 0600)
+        _csrf_fd.write("%d-%s" % (os.getpid(), binascii.hexlify(os.urandom(NONCE_SIZE))))
+        _csrf_fd.flush()
+        _csrf_fd.seek(0)
+    except IOError:
+        pass
+    fcntl.lockf(_csrf_fd, fcntl.LOCK_SH)
+    _csrf_key = _csrf_fd.read()
+    # keep the lock open until we go away
+
+
+def get_csrf_blob():
+    expiry = int(config.CSRF_TIMEOUT + time.time())
+    content = '%s-%s' % (get_user_hash(), expiry)
+    mac = hmac.new(_csrf_key, content).hexdigest()
+    return "%s-%s" % (content, mac)
+
+def check_csrf_blob(blob):
+    toks = blob.split('-')
+    if len(toks) != 3:
+        return False
+
+    user, expiry, mac = toks
+    if user != get_user_hash():
+        return False
+
+    try:
+        exp = int(expiry)
+    except ValueError:
+        return False
+
+    if exp < 1000000000:
+        return False
+
+    if exp > time.time():
+        return False
+
+    check_content = "%s-%s" % (user, expiry)
+    check_mac = hmac.new(_csrf_key, content).hexdigest()
+    if mac == check_mac:
+        return True
+
+    return False
+
--- a/web/templog.py	Thu Feb 06 20:35:43 2014 +0800
+++ b/web/templog.py	Thu Feb 06 22:45:16 2014 +0800
@@ -9,12 +9,15 @@
 import urllib
 import sys
 import os
+import traceback
+import fcntl
 
 import bottle
 from bottle import route, request, response
 
 import config
 import log
+import secure
 
 DATE_FORMAT = '%Y%m%d-%H.%M'
 ZOOM_SCALE = 2.0
@@ -47,7 +50,9 @@
 
 @route('/set')
 def set():
-    return bottle.template('set', inline_data = log.get_params())
+    return bottle.template('set', 
+        inline_data = log.get_params(), 
+        csrf_blob = secure.get_csrf_blob())
 
 @route('/set_current.json')
 def set_fresh():
@@ -100,7 +105,8 @@
 @route('/env')
 def env():
     response.set_header('Content-Type', 'text/plain')
-    return '\n'.join(("%s %s" % k) for k in  request.environ.items())
+    return '\n'.join(traceback.format_stack())
+    #return '\n'.join(("%s %s" % k) for k in  request.environ.items())
     #return str(request.environ)
     #yield "\n"
     #var_lookup = environ['mod_ssl.var_lookup']
@@ -108,8 +114,18 @@
 
 @bottle.get('/<filename:re:.*\.js>')
 def javascripts(filename):
+    response.set_header('Cache-Control', "public, max-age=1296000")
     return bottle.static_file(filename, root='static')
 
+@route('/setparams', method='post')
+def update():
+    post_json = json.loads(request.forms.data)
+
+    csrf_blob = post_json['csrf_blob']
+
+    return str(post_json['params'])
+
+secure.setup_csrf()
 
 def main():
     #bottle.debug(True)
--- a/web/views/set.tpl	Thu Feb 06 20:35:43 2014 +0800
+++ b/web/views/set.tpl	Thu Feb 06 22:45:16 2014 +0800
@@ -46,6 +46,11 @@
     text-align: center;
 }
 
+#savebox {
+    align: center;
+    width: 100%;
+}
+
 .onbutton {
     background-color: #cdf;
 }
@@ -64,17 +69,6 @@
 <title>Set templog</title>
 </head>
 
-<body>
-
-<section id="paramlist">
-</section>
-
-<div id="jsontest">
-</div>
-
-<input type="button" id="savebutton" value="Save"/>
-
-</body>
 
 <script type="html/num_input">
 <div id="{id}">
@@ -97,10 +91,11 @@
 
 <script>
 
-function Setter(params) {
+function Setter(params, csrf_blob) {
     var self = $.observable(this);
 
     self.params = params;
+    self.csrf_blob = csrf_blob
 
     $.each(self.params, function(idx, param) {
         param.id = "param_id_" + idx;
@@ -128,17 +123,40 @@
     }
 
     self.save = function() {
-        var j = JSON.stringify(self.params);
-        self.trigger("saved", j)
+        self.trigger("status", "Saving...")
+
+        var post_json = {};
+        post_json.csrf_blob = self.csrf_blob;
+        post_json.params =
+            self.params.map(function(v, idx, array) {
+                return {
+                    name: v.name,
+                    value: v.value
+                };
+            });
+
+        var post_data = {data: JSON.stringify(post_json)};
+
+        var req = $.ajax({type: "POST",
+            url: "setparams",
+            data: post_data});
+
+        req.done(function(data, status, hdr) {
+            self.trigger("status", "Saved")
+        });
+
+        req.fail(function(data, status, hdr) {
+            self.trigger("status", 
+                "Failed: " + status + "\n" + hdr.responseText)
+        });
     }
 }
 
 (function() { 'use strict';
 
 var params = {{!inline_data}};
-window.setter = new Setter(params);
-
-var root = $("#paramlist");
+var csrf_blob = "{{!csrf_blob}}";
+window.setter = new Setter(params, csrf_blob);
 
 var number_template = $("[type='html/num_input']").html();
 var button_template = $("[type='html/yesno_button']").html();
@@ -172,23 +190,24 @@
     $("#oldvalue", el).toggleClass("modified", !same);
 });
 
-setter.on("saved", function(j) {
-    $("#jsontest").text(j);
-});
+setter.on("status", function(status) {
+    $('#status').text(status)
+})
 
-
+var root;
 
-$.route(function(hash) {
+window.onload = function() {
+    // clear list and add new ones
+    root = $("#paramlist");
 
-// clear list and add new ones
-root.empty() && $.each(setter.params, function (idx, p) {
-    add(p);
+    root.empty() && $.each(setter.params, function (idx, p) {
+        add(p);
+    })
 
     $("#savebutton").click(function() {
         setter.save();
     })
-})
-})
+}
 
 function set_text_state(el, param)
 {
@@ -256,4 +275,19 @@
 
 </script>
 
+<body>
+
+<section id="paramlist">
+</section>
+
+<div id="savebox">
+<input type="button" id="savebutton" value="Save"/>
+
+<div id="status">
+</div>
+</div>
+
+
+</body>
+
 </html>