changeset 196:cbf7db1ff0d5

Better authentication handling: - Not bundling user info with every response anymore. - Ability to optionally bundle user info with the page read API endpoint. - New API endpoint for current user info.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 09 Feb 2014 20:46:22 -0800
parents 964fd28e9b39
children 067eca50f8f7
files wikked/assets/js/wikked/models.js wikked/assets/tpl/nav.html wikked/views/admin.py wikked/views/edit.py wikked/views/history.py wikked/views/read.py wikked/views/special.py
diffstat 7 files changed, 91 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/wikked/assets/js/wikked/models.js	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/assets/js/wikked/models.js	Sun Feb 09 20:46:22 2014 -0800
@@ -13,12 +13,11 @@
     var exports = {};
 
     var NavigationModel = exports.NavigationModel = Backbone.Model.extend({
-        idAttribute: 'path',
         defaults: function() {
             return {
                 path: "",
                 action: "read",
-                user: false
+                username: false
             };
         },
         initialize: function() {
@@ -26,12 +25,15 @@
                 model._onChangePath(path);
             });
             this._onChangePath(this.get('path'));
-            this.on('change:auth', function(model, auth) {
-                model._onChangeAuth(auth);
+            this.on('change:user', function(model, auth) {
+                model._onChangeUser(auth);
             });
-            this._onChangeAuth(this.get('auth'));
+            this._onChangeUser(this.get('user'));
             return this;
         },
+        url: function() {
+            return '/api/user/info';
+        },
         doPreviewSearch: function(query, callback) {
             if (this._isSearching) {
                 this._pendingQuery = query;
@@ -78,18 +80,16 @@
                 this.doPreviewSearch(q, c);
             }
         },
-        _onChangeAuth: function(auth) {
-            if (auth) {
+        _onChangeUser: function(user) {
+            if (user) {
                 this.set({
                     url_login: false,
-                    url_logout: '/#/logout',
-                    username: auth.username
+                    url_logout: '/#/logout'
                 });
             } else {
                 this.set({
                     url_login: '/#/login',
-                    url_logout: false,
-                    username: false
+                    url_logout: false
                 });
             }
         }
@@ -189,7 +189,7 @@
 
     var MasterPageModel = exports.MasterPageModel = PageModel.extend({
         initialize: function() {
-            this.nav = new NavigationModel({ id: this.id });
+            this.nav = new NavigationModel({ path: this.id });
             this.footer = new FooterModel();
             MasterPageModel.__super__.initialize.apply(this, arguments);
             this.on('change:auth', function(model, auth) {
@@ -253,10 +253,21 @@
                 -1,
                 'cog');
         },
+        url: function() {
+            var url = PageReadModel.__super__.url.apply(this, arguments);
+            if (!this.nav.get('user')) {
+                url += '?user';
+            }
+            return url;
+        },
         checkStatePath: function() {
             return this.get('path');
         },
         _onChange: function() {
+            if (this.get('user')) {
+                // Forward user info to the navigation model.
+                this.nav.set('user', this.get('user'));
+            }
             if (this.getMeta('redirect')) {
                 // Handle redirects.
                 var newPath = this.getMeta('redirect').replace(/^\//, "");
--- a/wikked/assets/tpl/nav.html	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/assets/tpl/nav.html	Sun Feb 09 20:46:22 2014 -0800
@@ -24,8 +24,8 @@
             <button type="submit" class="btn btn-sm btn-default">Search</button>
         </form>
         <ul class="nav navbar-nav navbar-right">
-            {{#if username}}
-            <li><a href="{{url_profile}}"><span class="glyphicon glyphicon-cog"></span> {{username}}</a></li>
+            {{#if user}}
+            <li><a href="{{url_profile}}"><span class="glyphicon glyphicon-cog"></span> {{user.username}}</a></li>
             <li><a href="{{url_logout}}"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li>
             {{else}}
             <li><a href="{{url_login}}"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
--- a/wikked/views/admin.py	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/views/admin.py	Sun Feb 09 20:46:22 2014 -0800
@@ -1,6 +1,5 @@
-from flask import g, abort, request
+from flask import g, jsonify, abort, request
 from flask.ext.login import login_user, logout_user, current_user
-from wikked.views import (make_auth_response)
 from wikked.web import app, login_manager
 
 
@@ -10,7 +9,7 @@
         return login_manager.unauthorized()
     g.wiki.index.reset(g.wiki.getPages())
     result = {'ok': 1}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/user/login', methods=['POST'])
@@ -24,7 +23,7 @@
         if app.bcrypt.check_password_hash(user.password, password):
             login_user(user, remember=bool(remember))
             result = {'username': username, 'logged_in': 1}
-            return make_auth_response(result)
+            return jsonify(result)
     abort(401)
 
 
@@ -32,7 +31,7 @@
 def api_user_is_logged_in():
     if current_user.is_authenticated():
         result = {'logged_in': True}
-        return make_auth_response(result)
+        return jsonify(result)
     abort(401)
 
 
@@ -40,13 +39,33 @@
 def api_user_logout():
     logout_user()
     result = {'ok': 1}
-    return make_auth_response(result)
+    return jsonify(result)
+
+
+@app.route('/api/user/info')
+def api_current_user_info():
+    user = current_user
+    if user.is_authenticated():
+        result = {
+                'user': {
+                    'username': current_user.username,
+                    'groups': current_user.groups
+                    }
+                }
+        return jsonify(result)
+    return jsonify({'user': False})
 
 
 @app.route('/api/user/info/<name>')
 def api_user_info(name):
     user = g.wiki.auth.getUser(name)
     if user is not None:
-        result = {'username': user.username, 'groups': user.groups}
-        return make_auth_response(result)
+        result = {
+                'user': {
+                    'username': user.username,
+                    'groups': user.groups
+                    }
+                }
+        return jsonify(result)
     abort(404)
+
--- a/wikked/views/edit.py	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/views/edit.py	Sun Feb 09 20:46:22 2014 -0800
@@ -3,7 +3,7 @@
 from flask.ext.login import current_user
 from wikked.page import Page, PageData
 from wikked.formatter import PageFormatter, FormattingContext
-from wikked.views import (make_page_title, make_auth_response, get_page_or_none,
+from wikked.views import (make_page_title, get_page_or_none,
         is_page_writable, get_page_meta, url_from_viewarg,
         split_url_from_viewarg)
 from wikked.web import app
@@ -59,7 +59,7 @@
             }
     if custom_data:
         result.update(custom_data)
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 def do_edit_page(url, default_message):
@@ -86,7 +86,7 @@
     g.wiki.setPage(url, page_fields)
 
     result = {'saved': 1}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/edit/', methods=['GET', 'POST'])
--- a/wikked/views/history.py	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/views/history.py	Sun Feb 09 20:46:22 2014 -0800
@@ -1,5 +1,5 @@
 import os.path
-from flask import g, request, abort
+from flask import g, jsonify, request, abort
 from pygments import highlight
 from pygments.lexers import get_lexer_by_name
 from pygments.formatters import get_formatter_by_name
@@ -7,7 +7,7 @@
 from wikked.scm.base import ACTION_NAMES
 from wikked.utils import PageNotFoundError
 from wikked.views import (is_page_readable, get_page_meta, get_page_or_404,
-        make_auth_response, url_from_viewarg,
+        url_from_viewarg,
         CHECK_FOR_READ)
 from wikked.web import app
 
@@ -64,7 +64,7 @@
     history = g.wiki.getHistory(limit=limit)
     hist_data = get_history_data(history, needs_files=True)
     result = {'history': hist_data}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/history/<path:url>')
@@ -73,7 +73,7 @@
     history = page.getHistory()
     hist_data = get_history_data(history)
     result = {'url': url, 'meta': get_page_meta(page), 'history': hist_data}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/revision/<path:url>')
@@ -85,7 +85,7 @@
     page_rev = page.getRevision(rev)
     meta = dict(get_page_meta(page, True), rev=rev)
     result = {'meta': meta, 'text': page_rev}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/diff/<path:url>')
@@ -105,7 +105,7 @@
     else:
         meta = dict(get_page_meta(page, True), rev1=rev1, rev2=rev2)
     result = {'meta': meta, 'diff': diff}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/revert/<path:url>', methods=['POST'])
@@ -128,5 +128,5 @@
             }
     g.wiki.revertPage(url, page_fields)
     result = {'reverted': 1}
-    return make_auth_response(result)
+    return jsonify(result)
 
--- a/wikked/views/read.py	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/views/read.py	Sun Feb 09 20:46:22 2014 -0800
@@ -1,8 +1,9 @@
 import time
 import urllib
-from flask import render_template, request, g
+from flask import render_template, request, g, jsonify
+from flask.ext.login import current_user
 from wikked.views import (get_page_meta, get_page_or_404, get_page_or_none,
-        is_page_readable, make_auth_response,
+        is_page_readable,
         url_from_viewarg, split_url_from_viewarg,
         CHECK_FOR_READ)
 from wikked.web import app
@@ -43,7 +44,7 @@
     pages = filter(is_page_readable, g.wiki.getPages(url_from_viewarg(url)))
     page_metas = [get_page_meta(page) for page in pages]
     result = {'path': url, 'pages': list(page_metas)}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/read/')
@@ -53,7 +54,17 @@
 
 @app.route('/api/read/<path:url>')
 def api_read_page(url):
-    #TODO: remove redundant quoting/spliting/unquoting around here.
+    additional_info = {}
+    if 'user' in request.args:
+        user = current_user
+        if user.is_authenticated():
+            additional_info['user'] = {
+                    'username': user.username,
+                    'groups': user.groups
+                    }
+        else:
+            additional_info['user'] = False
+
     endpoint, value, path = split_url_from_viewarg(url)
     if endpoint is None:
         # Normal page.
@@ -64,7 +75,8 @@
                 force_resolve=('force_resolve' in request.args))
 
         result = {'meta': get_page_meta(page), 'text': page.text}
-        return make_auth_response(result)
+        result.update(additional_info)
+        return jsonify(result)
 
     # Meta listing page.
     meta_page_url = '%s:%s' % (endpoint, path)
@@ -103,7 +115,8 @@
     if info_page:
         result['meta'] = get_page_meta(info_page)
 
-    return make_auth_response(result)
+    result.update(additional_info)
+    return jsonify(result)
 
 
 @app.route('/api/raw/')
@@ -115,7 +128,7 @@
 def api_read_page_raw(url):
     page = get_page_or_404(url, check_perms=CHECK_FOR_READ)
     result = {'meta': get_page_meta(page), 'text': page.raw_text}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/query')
@@ -126,7 +139,7 @@
             'query': query,
             'pages': [get_page_meta(p) for p in pages]
         }
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/state/')
@@ -138,7 +151,7 @@
 def api_get_state(url):
     page = get_page_or_404(url, check_perms=CHECK_FOR_READ)
     state = page.getState()
-    return make_auth_response({
+    return jsonify({
         'meta': get_page_meta(page, True),
         'state': STATE_NAMES[state]
         })
@@ -164,7 +177,7 @@
             links.append({'url': link, 'missing': True})
 
     result = {'meta': get_page_meta(page), 'out_links': links}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/inlinks/')
@@ -187,5 +200,5 @@
             links.append({'url': link, 'missing': True})
 
     result = {'meta': get_page_meta(page), 'in_links': links}
-    return make_auth_response(result)
+    return jsonify(result)
 
--- a/wikked/views/special.py	Thu Feb 06 22:00:32 2014 -0800
+++ b/wikked/views/special.py	Sun Feb 09 20:46:22 2014 -0800
@@ -1,6 +1,5 @@
-from flask import g, request, abort
-from wikked.views import (is_page_readable, get_page_meta, get_page_or_none,
-        make_auth_response)
+from flask import g, jsonify, request, abort
+from wikked.views import is_page_readable, get_page_meta, get_page_or_none
 from wikked.web import app
 
 
@@ -22,7 +21,7 @@
             app.logger.error("   %s" % e)
 
     result = {'orphans': orphans}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 
@@ -40,7 +39,7 @@
             readable_hits.append({'url': h.url, 'title': h.title, 'text': h.hl_text})
 
     result = {'query': query, 'hit_count': len(readable_hits), 'hits': readable_hits}
-    return make_auth_response(result)
+    return jsonify(result)
 
 
 @app.route('/api/searchpreview')
@@ -57,5 +56,5 @@
             readable_hits.append({'url': h.url, 'title': h.title})
 
     result = {'query': query, 'hit_count': len(readable_hits), 'hits': readable_hits}
-    return make_auth_response(result)
+    return jsonify(result)