diff --git a/configure-aspen.py b/configure-aspen.py index e27e2faa66..ae0799bc67 100644 --- a/configure-aspen.py +++ b/configure-aspen.py @@ -3,6 +3,7 @@ import gittip import gittip.wireup import gittip.authentication +import gittip.csrf gittip.wireup.canonical() @@ -14,8 +15,10 @@ website.github_callback = os.environ['GITHUB_CALLBACK'].decode('ASCII') website.hooks.inbound_early.register(gittip.canonize) +website.hooks.inbound_early.register(gittip.csrf.inbound) website.hooks.inbound_early.register(gittip.authentication.inbound) website.hooks.outbound_late.register(gittip.authentication.outbound) +website.hooks.outbound_late.register(gittip.csrf.outbound) def add_stuff(request): diff --git a/gittip/authentication.py b/gittip/authentication.py index fa9f7ce618..6ab9315ce9 100644 --- a/gittip/authentication.py +++ b/gittip/authentication.py @@ -136,4 +136,4 @@ def outbound(response): #cookie['domain'] cookie['path'] = '/' cookie['expires'] = rfc822.formatdate(expires) - #cookie['httponly'] = "Yes, please." + cookie['httponly'] = "Yes, please." diff --git a/requirements.txt b/requirements.txt index a935c6386d..39b1ae458a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -./vendor/aspen-0.18.25.tar.bz2 +./vendor/aspen-0.18.26.tar.bz2 ./vendor/psycopg2-2.4.5.tar.gz ./vendor/simplejson-2.3.2.tar.gz ./vendor/certifi-0.0.8.tar.gz diff --git a/templates/base.html b/templates/base.html index 2b99cd4284..f4ec782270 100644 --- a/templates/base.html +++ b/templates/base.html @@ -8,6 +8,7 @@ +
{% block heading %} diff --git a/vendor/aspen-0.18.25.tar.bz2 b/vendor/aspen-0.18.25.tar.bz2 deleted file mode 100644 index 4958b7b761..0000000000 Binary files a/vendor/aspen-0.18.25.tar.bz2 and /dev/null differ diff --git a/vendor/aspen-0.18.26.tar.bz2 b/vendor/aspen-0.18.26.tar.bz2 new file mode 100644 index 0000000000..1e883fb370 Binary files /dev/null and b/vendor/aspen-0.18.26.tar.bz2 differ diff --git a/www/%participant_id/tip.json b/www/%participant_id/tip.json index 46ee710c2b..6c5847221b 100644 --- a/www/%participant_id/tip.json +++ b/www/%participant_id/tip.json @@ -9,7 +9,6 @@ from gittip.networks import github # ========================================================================== ^L - out = {} if not user.ANON: @@ -36,7 +35,7 @@ if not user.ANON: # ===================== # Insert instead of update. The analytics may be interesting some day. - if POST and 'amount' in body and body.get('csrf') == user.session_token: + if POST and 'amount' in body: try: amount = decimal.Decimal(body['amount']) diff --git a/www/assets/%version/gittip.js b/www/assets/%version/gittip.js index 3d8ce97503..6de62d0a2a 100644 --- a/www/assets/%version/gittip.js +++ b/www/assets/%version/gittip.js @@ -136,7 +136,6 @@ Gittip.submitForm = function(url, data, success, error) console.log("failed", xhr, foo, bar); } - data.csrf = Gittip.getCookie('session'); jQuery.ajax({ url: url , type: "GET" , data: data @@ -309,17 +308,45 @@ Gittip.initPayment = function(stripe_publishable_key, participantId) }); }; -Gittip.getCookie = function(name) -{ // http://www.quirksmode.org/js/cookies.html - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - for(var i=0;i < ca.length;i++) { - var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1,c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); - } - return null; -} +Gittip.initCSRF = function() +{ // https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax + jQuery(document).ajaxSend(function(event, xhr, settings) { + function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + function sameOrigin(url) { + // url could be relative or scheme relative or absolute + var host = document.location.host; // host + port + var protocol = document.location.protocol; + var sr_origin = '//' + host; + var origin = protocol + sr_origin; + // Allow absolute or scheme relative URLs to same origin + return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || + (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || + // or any other URL that isn't scheme relative or absolute i.e relative. + !(/^(\/\/|http:|https:).*/.test(url)); + } + function safeMethod(method) { + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + } + + if (!safeMethod(settings.type) && sameOrigin(settings.url)) { + xhr.setRequestHeader("X-CSRF-TOKEN", getCookie('csrf_token')); + } + }); +}; Gittip.initTipButtons = function() { @@ -346,7 +373,7 @@ Gittip.initTipButtons = function() select(this, amount); jQuery.ajax( { url: '/' + tippee + '/tip.json' - , data: {amount: amount, csrf: Gittip.getCookie('session')} + , data: {amount: amount} , type: "POST" , error: function(x,y,z) { select(cur); console.log(x,y,z);