Mercurial > hg > ltpdarepo
changeset 193:215684074923
Implement Atom feed security in a way compatible with feed readers.
author | Daniele Nicolodi <daniele@grinta.net> |
---|---|
date | Tue, 08 Nov 2011 23:05:48 +0100 |
parents | 83c2d2f07df4 |
children | 53c58b8bac9f |
files | src/ltpdarepo/__init__.py src/ltpdarepo/templates/database.html src/ltpdarepo/views/feed.py |
diffstat | 3 files changed, 62 insertions(+), 32 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ltpdarepo/__init__.py Sun Aug 14 12:23:55 2011 +0200 +++ b/src/ltpdarepo/__init__.py Tue Nov 08 23:05:48 2011 +0100 @@ -174,8 +174,9 @@ from .views.browse import module app.register_blueprint(module, url_prefix='/browse') -from .views.feed import module +from .views.feed import module, url_for_atom_feed app.register_blueprint(module, url_prefix='/browse') +app.jinja_env.globals['url_for_atom_feed'] = url_for_atom_feed from .views.profile import module app.register_blueprint(module, url_prefix='/user')
--- a/src/ltpdarepo/templates/database.html Sun Aug 14 12:23:55 2011 +0200 +++ b/src/ltpdarepo/templates/database.html Tue Nov 08 23:05:48 2011 +0100 @@ -1,6 +1,6 @@ {% extends "layout.html" %} {% block head %} -<link href="{{ url_for('feed.atom', database=database.id) }}" rel="alternate" title="Recent Submissions" type="application/atom+xml"> +<link href="{{ url_for_atom_feed(database.id) }}" rel="alternate" title="Recent Submissions" type="application/atom+xml"> {% endblock %} {% block title %}{{ database.id }}{% endblock %} {% block body %} @@ -29,7 +29,7 @@ {% endif %} <h2>Feed</h2> <p class="discrete">Atom feed with the latest submisions to the database:</p> -<a class="feed" href="{{ url_for('feed.atom', database=database.id) }}"> +<a class="feed" href="{{ url_for_atom_feed(database.id) }}"> <img src="{{ url_for('static', filename='feed.png') }}"></img> </a> {% endblock %}
--- a/src/ltpdarepo/views/feed.py Sun Aug 14 12:23:55 2011 +0200 +++ b/src/ltpdarepo/views/feed.py Tue Nov 08 23:05:48 2011 +0100 @@ -1,43 +1,72 @@ -from flask import Blueprint, request, url_for, g +from flask import Blueprint, request, url_for, g, current_app, abort +from itsdangerous import BadSignature, Signer as BaseSigner from werkzeug.contrib.atom import AtomFeed from .browse import Objs from ltpdarepo.database import Database -from ltpdarepo.security import require, view app = Blueprint('feed', __name__) -@app.route('/<database>/atom.xml') -@require('user') -def atom(database): - with view('database', database): - db = Database.load(id=database) - if db is None: - # not found - abort(404) +class Signer(BaseSigner): + def __init__(self, secret=None, salt=None): + if secret is None: + secret = current_app.config['SECRET_KEY'] + if salt is None: + salt = request.host + super(Signer, self).__init__(secret, salt, sep='/') - feed = AtomFeed( - title=db.id, subtitle=db.description, - url=url_for('browse.database', database=database, _external=True), - feed_url=request.url, - generator=('LTPDA Repository', None, g.version)) + def unsign(self, token): + # translate bad signature exceptions into unauthorized http errors + try: + return super(Signer, self).unsign(token) + except BadSignature: + # unauthorized + abort(403) + + +def url_for_atom_feed(database, **kwargs): + signer = Signer() + token = signer.sign(database) + return url_for('feed.atom', token=token, **kwargs) + + +@app.route('/<path:token>/atom.xml') +def atom(token): + # this route cannot require authentication in the usual way + # because most feed readers do not support authentication. the + # trick is therefore to encode an authorization token into the url - # first n objects ordered per descending submission time - objs = Objs(database=database).orderby('submitted', 1).limit(64) - - for obj in objs.all(): - obj['url'] = url_for('browse.obj', database=database, - objid=obj['id'], _external=True) - feed.add(title='%s: %s' % (obj['name'], obj['title']), - content='%s %s' % (obj['description'], obj['analysis']), - content_type='text', - author=obj['author'], - url=obj['url'], - updated=obj['submitted']) + # check the token + signer = Signer() + database = signer.unsign(token) + + db = Database.load(id=database) + if db is None: + # not found + abort(404) + + feed = AtomFeed( + title=db.id, subtitle=db.description, + url=url_for('browse.database', database=database, _external=True), + feed_url=request.url, + generator=('LTPDA Repository', None, g.version)) - # return with the correct mime type - return feed.get_response() + # first n objects ordered per descending submission time + objs = Objs(database=database).orderby('submitted', 1).limit(64) + + for obj in objs.all(): + obj['url'] = url_for('browse.obj', database=database, + objid=obj['id'], _external=True) + feed.add(title='%s: %s' % (obj['name'], obj['title']), + content='%s %s' % (obj['description'], obj['analysis']), + content_type='text', + author=obj['author'], + url=obj['url'], + updated=obj['submitted']) + + # return with the correct mime type + return feed.get_response() module = app