use itsdangerous
This commit is contained in:
parent
759b304cb5
commit
05336728f4
@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright 2012 posativ <info@posativ.org>. All rights reserved.
|
# Copyright 2012, Martin Zimmermann <info@posativ.org>.
|
||||||
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are met:
|
# modification, are permitted provided that the following conditions are met:
|
||||||
@ -17,12 +18,14 @@
|
|||||||
# those of the authors and should not be interpreted as representing official
|
# those of the authors and should not be interpreted as representing official
|
||||||
# policies, either expressed or implied, of posativ <info@posativ.org>.
|
# policies, either expressed or implied, of posativ <info@posativ.org>.
|
||||||
#
|
#
|
||||||
# lightweight Disqus alternative
|
# Isso – a lightweight Disqus alternative
|
||||||
|
|
||||||
__version__ = '0.1'
|
__version__ = '0.1'
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from itsdangerous import URLSafeTimedSerializer
|
||||||
|
|
||||||
from werkzeug.routing import Map, Rule
|
from werkzeug.routing import Map, Rule
|
||||||
from werkzeug.serving import run_simple
|
from werkzeug.serving import run_simple
|
||||||
from werkzeug.wrappers import Request, Response
|
from werkzeug.wrappers import Request, Response
|
||||||
@ -32,7 +35,7 @@ from isso import admin, comment, db, utils
|
|||||||
|
|
||||||
|
|
||||||
_dumps = json.dumps
|
_dumps = json.dumps
|
||||||
setattr(json, 'dumps', lambda obj: _dumps(obj, cls=utils.IssoEncoder))
|
setattr(json, 'dumps', lambda obj, **kw: _dumps(obj, cls=utils.IssoEncoder, **kw))
|
||||||
|
|
||||||
|
|
||||||
url = lambda path, endpoint, methods: Rule(path, endpoint=endpoint, methods=methods)
|
url = lambda path, endpoint, methods: Rule(path, endpoint=endpoint, methods=methods)
|
||||||
@ -54,13 +57,22 @@ class Isso:
|
|||||||
MODERATION = False
|
MODERATION = False
|
||||||
SQLITE = None
|
SQLITE = None
|
||||||
|
|
||||||
|
MAX_AGE = 15*60
|
||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
|
|
||||||
self.__dict__.update(dict((k, v) for k, v in conf.iteritems() if k.isupper()))
|
self.__dict__.update(dict((k, v) for k, v in conf.iteritems() if k.isupper()))
|
||||||
|
self.signer = URLSafeTimedSerializer(self.SECRET_KEY)
|
||||||
|
|
||||||
if self.SQLITE:
|
if self.SQLITE:
|
||||||
self.db = db.SQLite(self)
|
self.db = db.SQLite(self)
|
||||||
|
|
||||||
|
def sign(self, obj):
|
||||||
|
return self.signer.dumps(obj)
|
||||||
|
|
||||||
|
def unsign(self, obj):
|
||||||
|
return self.signer.loads(obj, max_age=self.MAX_AGE)
|
||||||
|
|
||||||
def dispatch(self, request, start_response):
|
def dispatch(self, request, start_response):
|
||||||
adapter = url_map.bind_to_environ(request.environ)
|
adapter = url_map.bind_to_environ(request.environ)
|
||||||
try:
|
try:
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
from werkzeug.wrappers import Response
|
from werkzeug.wrappers import Response
|
||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
|
|
||||||
|
from itsdangerous import SignatureExpired, BadSignature
|
||||||
|
|
||||||
from isso import json, models
|
from isso import json, models
|
||||||
|
|
||||||
|
|
||||||
@ -12,7 +14,9 @@ def create(app, environ, request, path):
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return Response(unicode(e), 400)
|
return Response(unicode(e), 400)
|
||||||
|
|
||||||
return Response(json.dumps(rv), 201, content_type='application/json')
|
response = Response(json.dumps(rv), 201, content_type='application/json')
|
||||||
|
response.set_cookie('session', app.signer.dumps([path, rv.id]), max_age=app.MAX_AGE)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def get(app, environ, request, path, id=None):
|
def get(app, environ, request, path, id=None):
|
||||||
@ -25,6 +29,14 @@ def get(app, environ, request, path, id=None):
|
|||||||
|
|
||||||
def modify(app, environ, request, path, id):
|
def modify(app, environ, request, path, id):
|
||||||
|
|
||||||
|
try:
|
||||||
|
rv = app.unsign(request.cookies.get('session', ''))
|
||||||
|
except (SignatureExpired, BadSignature):
|
||||||
|
return abort(403)
|
||||||
|
|
||||||
|
if not (rv[0] == '*' or rv == [path, id]):
|
||||||
|
abort(401)
|
||||||
|
|
||||||
if request.method == 'PUT':
|
if request.method == 'PUT':
|
||||||
try:
|
try:
|
||||||
rv = app.db.update(path, id, models.Comment.fromjson(request.data))
|
rv = app.db.update(path, id, models.Comment.fromjson(request.data))
|
||||||
|
@ -15,15 +15,16 @@ def comment(**kw):
|
|||||||
|
|
||||||
class TestComments(unittest.TestCase):
|
class TestComments(unittest.TestCase):
|
||||||
|
|
||||||
get = lambda self, *x, **z: Client(self.app, Response).get(*x, **z)
|
|
||||||
put = lambda self, *x, **z: Client(self.app, Response).put(*x, **z)
|
|
||||||
post = lambda self, *x, **z: Client(self.app, Response).post(*x, **z)
|
|
||||||
delete = lambda self, *x, **z: Client(self.app, Response).delete(*x, **z)
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
fd, self.path = tempfile.mkstemp()
|
fd, self.path = tempfile.mkstemp()
|
||||||
self.app = Isso({'SQLITE': self.path})
|
self.app = Isso({'SQLITE': self.path})
|
||||||
|
|
||||||
|
self.client = Client(self.app, Response)
|
||||||
|
self.get = lambda *x, **z: self.client.get(*x, **z)
|
||||||
|
self.put = lambda *x, **z: self.client.put(*x, **z)
|
||||||
|
self.post = lambda *x, **z: self.client.post(*x, **z)
|
||||||
|
self.delete = lambda *x, **z: self.client.delete(*x, **z)
|
||||||
|
|
||||||
def testGet(self):
|
def testGet(self):
|
||||||
|
|
||||||
self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
|
self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
|
||||||
@ -40,7 +41,7 @@ class TestComments(unittest.TestCase):
|
|||||||
rv = self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
|
rv = self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
|
||||||
|
|
||||||
assert rv.status_code == 201
|
assert rv.status_code == 201
|
||||||
# XXX assert cookie
|
assert len(filter(lambda header: header[0] == 'Set-Cookie', rv.headers)) == 1
|
||||||
|
|
||||||
c = Comment.fromjson(rv.data)
|
c = Comment.fromjson(rv.data)
|
||||||
|
|
||||||
@ -91,10 +92,11 @@ class TestComments(unittest.TestCase):
|
|||||||
|
|
||||||
def testDeleteWithReference(self):
|
def testDeleteWithReference(self):
|
||||||
|
|
||||||
self.post('/comment/path/new', data=json.dumps(comment(text='First')))
|
client = Client(self.app, Response)
|
||||||
|
resp = client.post('/comment/path/new', data=json.dumps(comment(text='First')))
|
||||||
self.post('/comment/path/new', data=json.dumps(comment(text='Second', parent=1)))
|
self.post('/comment/path/new', data=json.dumps(comment(text='Second', parent=1)))
|
||||||
|
|
||||||
r = self.delete('/comment/path/1')
|
r = client.delete('/comment/path/1')
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert Comment(**json.loads(r.data)).deleted
|
assert Comment(**json.loads(r.data)).deleted
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user