handle cross-origin cookies with a custom header X-Set-Cookie, fix #24

Cookies set from a different domain can not be read by JS executed in
the current domain. As a workaround, Isso sends both a Set-Cookie and
X-Set-Cookie header. The former is used by the browser to make the
HTTP request to the API, the latter is read by `embed.min.js` to
determine if a comment can be edited or deleted.

When a comment is deleted, the server sends an expired cookies in
Set-Cookie and X-Set-Cookie.
This commit is contained in:
Martin Zimmermann 2013-11-05 12:20:17 +01:00
parent 05c8b571e2
commit 6691810316
4 changed files with 28 additions and 9 deletions

View File

@ -76,6 +76,11 @@ define(["q"], function(Q) {
function onload() {
var rule = url.replace(endpoint, "").split("?", 1)[0];
var cookie = xhr.getResponseHeader("X-Set-Cookie");
if (cookie && cookie.match(/^isso-/)) {
document.cookie = cookie;
}
if (rule in rules && rules[rule].indexOf(xhr.status) === -1) {
response.reject(xhr.responseText);

View File

@ -241,7 +241,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/api", "app/markup", "app/i
// remove edit and delete buttons when cookie is gone
var clear = function(button) {
if (! utils.cookie(comment.id)) {
if (! utils.cookie("isso-" + comment.id)) {
$(button, footer).remove();
} else {
setTimeout(function() { clear(button); }, 15*1000);
@ -253,14 +253,14 @@ define(["app/text/html", "app/dom", "app/utils", "app/api", "app/markup", "app/i
// show direct reply to own comment when cookie is max aged
var show = function(el) {
if (utils.cookie(comment.id)) {
if (utils.cookie("isso-" + comment.id)) {
setTimeout(function() { show(el); }, 15*1000);
} else {
footer.append(el);
}
};
if (utils.cookie(comment.id)) {
if (utils.cookie("isso-" + comment.id)) {
show($("a.reply", footer).detach());
}
};

View File

@ -5,10 +5,11 @@ import json
import time
import hashlib
import logging
import sqlite3
import functools
from itsdangerous import SignatureExpired, BadSignature
from werkzeug.http import dump_cookie
from werkzeug.wrappers import Response
from werkzeug.exceptions import BadRequest, Forbidden, NotFound
@ -118,9 +119,13 @@ def new(app, environ, request, uri):
# success!
logger.info('comment created: %s', json.dumps(rv))
resp = Response(json.dumps(rv), 202 if rv["mode"] == 2 else 201,
content_type='application/json')
resp.set_cookie(str(rv["id"]), app.sign([rv["id"], checksum]), max_age=app.conf.getint('general', 'max-age'))
cookie = functools.partial(dump_cookie,
value=app.sign([rv["id"], checksum]),
max_age=app.conf.getint('general', 'max-age'))
resp = Response(json.dumps(rv), 202 if rv["mode"] == 2 else 201, content_type='application/json')
resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
return resp
@ -176,8 +181,13 @@ def single(app, environ, request, id):
checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest()
rv["text"] = app.markdown(rv["text"])
cookie = functools.partial(dump_cookie,
value=app.sign([rv["id"], checksum]),
max_age=app.conf.getint('general', 'max-age'))
resp = Response(json.dumps(rv), 200, content_type='application/json')
resp.set_cookie(str(rv["id"]), app.sign([rv["id"], checksum]), max_age=app.conf.getint('general', 'max-age'))
resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
return resp
if request.method == 'DELETE':
@ -192,8 +202,11 @@ def single(app, environ, request, id):
logger.info('comment %i deleted', id)
cookie = functools.partial(dump_cookie, expires=0, max_age=0)
resp = Response(json.dumps(rv), 200, content_type='application/json')
resp.delete_cookie(str(id), path='/')
resp.headers.add("Set-Cookie", cookie(str(id)))
resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
return resp

View File

@ -38,6 +38,7 @@ class CORSMiddleWare(object):
headers.append(("Access-Control-Allow-Headers", "Origin, Content-Type"))
headers.append(("Access-Control-Allow-Credentials", "true"))
headers.append(("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"))
headers.append(("Access-Control-Expose-Headers", "X-Set-Cookie"))
return start_response(status, headers, exc_info)
if environ.get("REQUEST_METHOD") == "OPTIONS":