Mercurial > wikked
changeset 368:831b22e93f94 0.6.3
Implement error and permission handling for UI views.
* Views are protected by decorators.
* Decorators check for permission errors and render error pages.
* Global Flask error handling is for API endpoints.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Tue, 22 Sep 2015 03:17:21 -0700 |
parents | 73e0564e4fea |
children | 8b55ceaf99dd |
files | wikked/auth.py wikked/templates/error-unauthorized.html wikked/views/__init__.py wikked/views/edit.py wikked/views/error.py wikked/views/history.py wikked/views/read.py wikked/views/special.py |
diffstat | 8 files changed, 106 insertions(+), 20 deletions(-) [+] |
line wrap: on
line diff
--- a/wikked/auth.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/auth.py Tue Sep 22 03:17:21 2015 -0700 @@ -61,6 +61,19 @@ def isPageWritable(self, page, username): return self._isAllowedForMeta(page, 'writers', username) + def hasPermission(self, meta_name, username): + perm = self._permissions.get(meta_name) + if perm is not None: + # Permissions are declared at the wiki level. + if username is None and 'anonymous' in perm: + return True + if username is not None and ( + '*' in perm or username in perm): + return True + return False + + return True + def _isAllowedForMeta(self, page, meta_name, username): perm = page.getMeta(meta_name) if perm is not None: @@ -77,17 +90,7 @@ return False - perm = self._permissions.get(meta_name) - if perm is not None: - # Permissions are declared at the wiki level. - if username is None and 'anonymous' in perm: - return True - if username is not None and ( - '*' in perm or username in perm): - return True - return False - - return True + return self.hasPermission(meta_name, username) def _updatePermissions(self, config): self._permissions = {
--- a/wikked/templates/error-unauthorized.html Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/templates/error-unauthorized.html Tue Sep 22 03:17:21 2015 -0700 @@ -2,11 +2,19 @@ {% block content %} <article> <header> - <h1>You're not authorized for this</h1> + <h1>{{error|default("You're not authorized for this")}}</h1> </header> <section> - <p>The page you're trying to access is protected. - Please <a href="/login">log into an account</a> that has access to it.</p> + {%if error_details%} + <p>{{error_details}}</p> + {%else%} + <p>The page you're trying to access is protected.</p> + {%endif%} + {%if auth.is_logged_in%} + <p>User <a href="{{auth.url_profile}}">{{auth.username}}</a> is not authorized for this. Please <a href="/login">log into a different account</a> to have access to it.</p> + {%else%} + <p>Please <a href="/login">log into an account</a> that has access to it.</p> + {%endif%} </section> </article> {% endblock %}
--- a/wikked/views/__init__.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/views/__init__.py Tue Sep 22 03:17:21 2015 -0700 @@ -1,6 +1,56 @@ -from flask import request +import functools +from flask import request, render_template from flask.ext.login import current_user -from wikked.web import app +from wikked.web import app, get_wiki +from wikked.webimpl import PermissionError + + +def show_unauthorized_error(error=None, error_details=None, tpl_name=None): + if error is not None: + error = str(error) + + data = {} + if error: + data['error'] = error + if error_details: + data['error_details'] = error_details + + add_auth_data(data) + add_navigation_data(None, data) + tpl_name = tpl_name or 'error-unauthorized.html' + return render_template(tpl_name, **data) + + +def errorhandling_ui(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + try: + return f(*args, **kwargs) + except PermissionError as ex: + return show_unauthorized_error(ex) + return wrapper + + +def errorhandling_ui2(tpl_name): + def decorator(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + try: + return f(*args, **kwargs) + except PermissionError as ex: + return show_unauthorized_error(ex, tpl_name=tpl_name) + return wrapper + return decorator + + +def requires_reader_auth(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + wiki = get_wiki() + if not wiki.auth.hasPermission('readers', current_user.get_id()): + return show_unauthorized_error() + return f(*args, **kwargs) + return wrapper def add_auth_data(data):
--- a/wikked/views/edit.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/views/edit.py Tue Sep 22 03:17:21 2015 -0700 @@ -1,6 +1,7 @@ from flask import redirect, url_for, request, render_template from flask.ext.login import current_user from wikked.views import ( + errorhandling_ui2, show_unauthorized_error, add_auth_data, add_navigation_data) from wikked.web import app, get_wiki from wikked.webimpl import url_from_viewarg @@ -14,6 +15,11 @@ @app.route('/create/<path:url>') def create_page(url): + wiki = get_wiki() + if not wiki.auth.hasPermission('writers', current_user.get_id()): + return show_unauthorized_error( + error="You're not authorized to create new pages.") + data = { 'is_new': True, 'create_in': url.lstrip('/'), @@ -30,12 +36,14 @@ @app.route('/edit', methods=['POST']) +@errorhandling_ui2('error-unauthorized-edit.html') def edit_new_page(): url = request.form['title'] return edit_page(url) @app.route('/edit/<path:url>', methods=['GET', 'POST']) +@errorhandling_ui2('error-unauthorized-edit.html') def edit_page(url): wiki = get_wiki() user = current_user.get_id()
--- a/wikked/views/error.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/views/error.py Tue Sep 22 03:17:21 2015 -0700 @@ -23,7 +23,7 @@ resp = { 'error': { 'type': 'permission', - 'message': error.message + 'message': str(error) } } return jsonify(resp), 403
--- a/wikked/views/history.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/views/history.py Tue Sep 22 03:17:21 2015 -0700 @@ -1,7 +1,9 @@ import urllib.parse from flask import request, abort, render_template from flask.ext.login import current_user -from wikked.views import add_auth_data, add_navigation_data +from wikked.views import ( + errorhandling_ui, requires_reader_auth, + add_auth_data, add_navigation_data) from wikked.web import app, get_wiki from wikked.webimpl import url_from_viewarg from wikked.webimpl.history import ( @@ -10,6 +12,7 @@ @app.route('/special/history') +@requires_reader_auth def site_history(): wiki = get_wiki() user = current_user.get_id() @@ -26,6 +29,7 @@ @app.route('/hist/<path:url>') +@errorhandling_ui def page_history(url): wiki = get_wiki() user = current_user.get_id() @@ -40,6 +44,7 @@ @app.route('/rev/<path:url>') +@errorhandling_ui def page_rev(url): rev = request.args.get('rev') if rev is None: @@ -62,6 +67,7 @@ @app.route('/diff/<path:url>') +@errorhandling_ui def diff_page(url): rev1 = request.args.get('rev1') rev2 = request.args.get('rev2') @@ -89,6 +95,7 @@ @app.route('/diff_rev/<rev>') +@errorhandling_ui def diff_revision(rev): wiki = get_wiki() user = current_user.get_id()
--- a/wikked/views/read.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/views/read.py Tue Sep 22 03:17:21 2015 -0700 @@ -2,7 +2,7 @@ from flask import ( render_template, request, abort) from flask.ext.login import current_user -from wikked.views import add_auth_data, add_navigation_data +from wikked.views import add_auth_data, add_navigation_data, errorhandling_ui from wikked.web import app, get_wiki from wikked.webimpl import url_from_viewarg from wikked.webimpl.read import ( @@ -18,6 +18,7 @@ @app.route('/read/<path:url>') +@errorhandling_ui def read(url): wiki = get_wiki() url = url_from_viewarg(url) @@ -33,6 +34,7 @@ @app.route('/search') +@errorhandling_ui def search(): query = request.args.get('q') if query is None or query == '': @@ -55,6 +57,7 @@ @app.route('/inlinks/<path:url>') +@errorhandling_ui def incoming_links(url): wiki = get_wiki() user = current_user.get_id()
--- a/wikked/views/special.py Tue Sep 22 03:15:42 2015 -0700 +++ b/wikked/views/special.py Tue Sep 22 03:17:21 2015 -0700 @@ -1,6 +1,8 @@ from flask import render_template from flask.ext.login import current_user -from wikked.views import add_auth_data, add_navigation_data +from wikked.views import ( + requires_reader_auth, + add_auth_data, add_navigation_data) from wikked.web import app, get_wiki from wikked.webimpl.special import ( get_orphans, get_broken_redirects, get_double_redirects, @@ -70,6 +72,7 @@ @app.route('/special') +@requires_reader_auth def special_pages_dashboard(): data = { 'is_special_page': True, @@ -106,24 +109,28 @@ @app.route('/special/list/orphans') +@requires_reader_auth def special_list_orphans(): return call_api('orphans', get_orphans, raw_url='/api/orphans') @app.route('/special/list/broken-redirects') +@requires_reader_auth def special_list_broken_redirects(): return call_api('broken-redirects', get_broken_redirects, raw_url='/api/broken-redirects') @app.route('/special/list/double-redirects') +@requires_reader_auth def special_list_double_redirects(): return call_api('double-redirects', get_double_redirects, raw_url='/api/double-redirects') @app.route('/special/list/dead-ends') +@requires_reader_auth def special_list_dead_ends(): return call_api('dead-ends', get_dead_ends, raw_url='/api/dead-ends')