You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2013/05/14 14:49:19 UTC

[3/6] git commit: updated refs/heads/route-events to f6081cb

Add authentication plugin, example module and integrate couchdb admin
authentication


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/92257863
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/92257863
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/92257863

Branch: refs/heads/route-events
Commit: 92257863b7feb784a73ebd7ab6c9368e2992ac20
Parents: 17fbae3
Author: Garren Smith <ga...@gmail.com>
Authored: Wed May 8 17:11:42 2013 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Tue May 14 14:48:04 2013 +0200

----------------------------------------------------------------------
 .gitignore                                         |    1 +
 src/fauxton/app/addons/auth/base.js                |   28 ++++++-
 src/fauxton/app/addons/auth/resources.js           |   64 ++++++++++----
 .../app/addons/auth/templates/nav_link.html        |    2 +-
 .../app/addons/auth/templates/noAccess.html        |   19 ++++
 src/fauxton/app/addons/config/routes.js            |    2 +
 src/fauxton/app/addons/exampleAuth/base.js         |   59 ++++++++++++
 .../app/addons/exampleAuth/templates/noAccess.html |   19 ++++
 src/fauxton/app/addons/logs/routes.js              |    2 +
 src/fauxton/app/api.js                             |   70 ++++++++++++++-
 src/fauxton/app/modules/documents/routes.js        |    7 +-
 src/fauxton/app/router.js                          |   13 ++-
 12 files changed, 260 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 67da1c8..c1ece09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,6 +92,7 @@ src/fauxton/app/addons/*
 !src/fauxton/app/addons/stats
 !src/fauxton/app/addons/contribute
 !src/fauxton/app/addons/auth
+!src/fauxton/app/addons/exampleAuth
 src/fauxton/settings.json*
 !src/fauxton/settings.json.default
 share/www/fauxton

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/auth/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/base.js b/src/fauxton/app/addons/auth/base.js
index 4cf1b83..9f40308 100644
--- a/src/fauxton/app/addons/auth/base.js
+++ b/src/fauxton/app/addons/auth/base.js
@@ -19,7 +19,7 @@ define([
 function(app, FauxtonAPI, Auth) {
 
   Auth.initialize = function() {
-    Auth.session = new Auth.Session();
+    var session = Auth.session = new Auth.Session();
     Auth.navLink = new Auth.NavLink({model: Auth.session});
 
     FauxtonAPI.addHeaderLink({
@@ -28,6 +28,32 @@ function(app, FauxtonAPI, Auth) {
       view: Auth.navLink,
       establish: [Auth.session.fetchOnce()]
     });
+
+    var auth = function (roles, layout) {
+      var deferred = $.Deferred();
+
+      var sessionDeferred = session.fetchOnce().then(function () {
+        console.log(session);
+
+        if (session.isAdminParty()) {
+          deferred.resolve();
+        } else if(session.matchesRoles(roles)) {
+          deferred.resolve();
+        } else {
+          deferred.reject();
+        }
+      });
+
+      return [sessionDeferred, deferred];
+    };
+
+    var authDenied = function () {
+      app.masterLayout.setView('#dashboard', new Auth.NoAccessView());
+      app.masterLayout.renderView('#dashboard');
+    };
+
+    FauxtonAPI.auth.registerAuth(auth);
+    FauxtonAPI.auth.registerAuthDenied(authDenied);
   };
 
   return Auth;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/auth/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/resources.js b/src/fauxton/app/addons/auth/resources.js
index 64dd254..92cff91 100644
--- a/src/fauxton/app/addons/auth/resources.js
+++ b/src/fauxton/app/addons/auth/resources.js
@@ -17,6 +17,8 @@ define([
 
 function (app, FauxtonAPI) {
 
+  var Auth = new FauxtonAPI.addon();
+
   var Admin = Backbone.Model.extend({
 
     url: function () {
@@ -44,12 +46,10 @@ function (app, FauxtonAPI) {
     }
   });
 
-  var Auth = new FauxtonAPI.addon();
-
   Auth.Session = Backbone.Model.extend({
     url: '/_session',
 
-    is_admin_party: function () {
+    isAdminParty: function () {
       var userCtx = this.get('userCtx');
 
       if (!userCtx.name && userCtx.roles.indexOf("_admin") > -1) {
@@ -70,6 +70,30 @@ function (app, FauxtonAPI) {
       };
     },
 
+    userRoles: function () {
+      var user = this.user();
+
+      if (user && user.roles) { 
+        return user.roles;
+      }
+
+      return [];
+    },
+
+    matchesRoles: function (roles) {
+      if (roles.length === 0) {
+        return true;
+      }
+
+      var numberMatchingRoles = _.intersection(this.userRoles(), roles).length;
+
+      if (numberMatchingRoles > 0) {
+        return true;
+      }
+
+      return false;
+    },
+
     fetchOnce: function (opt) {
       var options = _.extend({}, opt);
 
@@ -80,7 +104,7 @@ function (app, FauxtonAPI) {
       return this._deferred;
     },
 
-    validate_user: function (username, password, msg) {
+    validateUser: function (username, password, msg) {
       if (_.isEmpty(username) || _.isEmpty(password)) {
         var deferred = $.Deferred();
 
@@ -89,7 +113,7 @@ function (app, FauxtonAPI) {
       }
     },
 
-    validate_passwords: function (password, password_confirm, msg) {
+    validatePasswords: function (password, password_confirm, msg) {
       if (_.isEmpty(password) || _.isEmpty(password_confirm) || (password !== password_confirm)) {
         var deferred = $.Deferred();
 
@@ -99,9 +123,9 @@ function (app, FauxtonAPI) {
 
     },
 
-    create_admin: function (username, password, login) {
+    createAdmin: function (username, password, login) {
       var self = this,
-          error_promise =  this.validate_user(username, password, 'Authname or password cannot be blank.');
+          error_promise =  this.validateUser(username, password, 'Authname or password cannot be blank.');
 
       if (error_promise) { return error_promise; }
 
@@ -120,7 +144,7 @@ function (app, FauxtonAPI) {
     },
 
     login: function (username, password) {
-      var error_promise =  this.validate_user(username, password, 'Authname or password cannot be blank.');
+      var error_promise =  this.validateUser(username, password, 'Authname or password cannot be blank.');
 
       if (error_promise) { return error_promise; }
 
@@ -150,8 +174,8 @@ function (app, FauxtonAPI) {
       });
     },
 
-    change_password: function (password, password_confirm) {
-      var error_promise =  this.validate_passwords(password, password_confirm, 'Passwords do not match.');
+    changePassword: function (password, password_confirm) {
+      var error_promise =  this.validatePasswords(password, password_confirm, 'Passwords do not match.');
 
       if (error_promise) { return error_promise; }
 
@@ -208,10 +232,10 @@ function (app, FauxtonAPI) {
     },
 
     events: {
-      "click #create-admin": "create_admin"
+      "click #create-admin": "createAdmin"
     },
 
-    create_admin: function (event) {
+    createAdmin: function (event) {
       event.preventDefault();
       this.clear_error_msg();
 
@@ -219,7 +243,7 @@ function (app, FauxtonAPI) {
       username = this.$('#username').val(),
       password = this.$('#password').val();
 
-      var promise = this.model.create_admin(username, password, this.login_after);
+      var promise = this.model.createAdmin(username, password, this.login_after);
 
       promise.then(function () {
         self.$('.modal').modal('hide');
@@ -265,10 +289,10 @@ function (app, FauxtonAPI) {
     template: 'addons/auth/templates/change_password_modal',
 
     events: {
-      "click #change-password": "change_password"
+      "click #change-password": "changePassword"
     },
 
-    change_password: function () {
+    changePassword: function () {
       event.preventDefault();
       this.clear_error_msg();
 
@@ -276,7 +300,7 @@ function (app, FauxtonAPI) {
           new_password = this.$('#password').val(),
           password_confirm = this.$('#password-confirm').val();
 
-      var promise = this.model.change_password(new_password, password_confirm);
+      var promise = this.model.changePassword(new_password, password_confirm);
 
       promise.done(function () {
         self.hide_modal();
@@ -299,7 +323,7 @@ function (app, FauxtonAPI) {
 
     serialize: function () {
       return {
-        admin_party: this.model.is_admin_party(),
+        admin_party: this.model.isAdminParty(),
         user: this.model.user()
       };
     },
@@ -346,5 +370,11 @@ function (app, FauxtonAPI) {
     }
   });
 
+  Auth.NoAccessView = FauxtonAPI.View.extend({
+    template: "addons/auth/templates/noAccess"
+
+  });
+
+
   return Auth;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/auth/templates/nav_link.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/templates/nav_link.html b/src/fauxton/app/addons/auth/templates/nav_link.html
index 2f1462f..ba0a6f9 100644
--- a/src/fauxton/app/addons/auth/templates/nav_link.html
+++ b/src/fauxton/app/addons/auth/templates/nav_link.html
@@ -32,7 +32,7 @@ the License.
   <li> <a id="user-logout" href="#"> Logout </a> </li> 
   <% } else { %>
   <li> <a id="user-login" href="#"> Login </a> </li> 
-  <li> <a id="user-sign-up"> Sign up </a> </li>
+  <!--<li> <a id="user-sign-up"> Sign up </a> </li>-->
   <% } %>
 </ul>
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/auth/templates/noAccess.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/templates/noAccess.html b/src/fauxton/app/addons/auth/templates/noAccess.html
new file mode 100644
index 0000000..f1a9506
--- /dev/null
+++ b/src/fauxton/app/addons/auth/templates/noAccess.html
@@ -0,0 +1,19 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+
+<div class="row-fluid" >
+  <div class="span6 offset4">
+  <h3> You do not have permission to view this page </h3>
+</div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/config/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/config/routes.js b/src/fauxton/app/addons/config/routes.js
index 42934a8..d86715f 100644
--- a/src/fauxton/app/addons/config/routes.js
+++ b/src/fauxton/app/addons/config/routes.js
@@ -28,6 +28,8 @@ function(app, FauxtonAPI, Config) {
       this.configs = new Config.Collection();
     },
 
+    roles: ["_admin"],
+
     crumbs: [
       {"name": "Config","link": "_config"}
     ],

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/exampleAuth/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/exampleAuth/base.js b/src/fauxton/app/addons/exampleAuth/base.js
new file mode 100644
index 0000000..aa99670
--- /dev/null
+++ b/src/fauxton/app/addons/exampleAuth/base.js
@@ -0,0 +1,59 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  "app",
+
+  "api"
+],
+
+function(app, FauxtonAPI) {
+  // This is an example module of using the new auth module.
+
+  var noAccessView = FauxtonAPI.View.extend({
+    template: "addons/exampleAuth/templates/noAccess"
+
+  });
+
+  // To utilise the authentication - all that is required, is one callback
+  // that is registered with the auth api. This function can return an array
+  // of deferred objects.
+  // The roles argument that is passed in is the required roles for the current user
+  // to be allowed to access the current page.
+  // The layout is the main layout for use when you want to render a view onto the page
+  var auth = function (roles) {
+    var deferred = $.Deferred();
+
+    if (roles.indexOf('_admin') > -1) {
+      deferred.reject();
+    } else {
+      deferred.resolve();
+    }
+
+    return [deferred];
+  };
+
+  // If you would like to do something with when access is denied you can register this callback.
+  // It will be called is access has been denied on the previous page.
+  var authFail = function () {
+    app.masterLayout.setView('#dashboard', new noAccessView());
+    app.masterLayout.renderView('#dashboard');
+  };
+
+  // Register the auth call back. This will be called before new route rendered
+  FauxtonAPI.auth.registerAuth(auth);
+  // Register a failed route request callback. This is called if access is denied.
+  FauxtonAPI.auth.registerAuthDenied(authFail);
+
+
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/exampleAuth/templates/noAccess.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/exampleAuth/templates/noAccess.html b/src/fauxton/app/addons/exampleAuth/templates/noAccess.html
new file mode 100644
index 0000000..f1a9506
--- /dev/null
+++ b/src/fauxton/app/addons/exampleAuth/templates/noAccess.html
@@ -0,0 +1,19 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+
+<div class="row-fluid" >
+  <div class="span6 offset4">
+  <h3> You do not have permission to view this page </h3>
+</div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/addons/logs/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/logs/routes.js b/src/fauxton/app/addons/logs/routes.js
index 1ce260c..ae0a6b6 100644
--- a/src/fauxton/app/addons/logs/routes.js
+++ b/src/fauxton/app/addons/logs/routes.js
@@ -32,6 +32,8 @@ function(app, FauxtonAPI, Log) {
       "_log": "showLog"
     },
 
+    roles: ["_admin"],
+
     apiUrl: function() {
       return this.logs.url();
     },

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/api.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/api.js b/src/fauxton/app/api.js
index 1c0b8f8..9c4ecef 100644
--- a/src/fauxton/app/api.js
+++ b/src/fauxton/app/api.js
@@ -108,6 +108,57 @@ function(app, Fauxton) {
     }
   });
 
+  // This is not exposed externally as it should not need to be accessed or overridden
+  var Auth = function (options) {
+    this._options = options;
+    this.initialize.apply(this, arguments);
+  };
+
+  // Piggy-back on Backbone's self-propagating extend function,
+  Auth.extend = Backbone.Model.extend;
+
+  _.extend(Auth.prototype, Backbone.Events, {
+    authDeniedCb: function() {},
+
+    initialize: function() {
+      var self = this;
+
+      $(document).ajaxError(function(event, jqxhr, settings, exception) {
+        console.log("UNAUTH");
+        console.log(arguments);
+        if (exception === "Unauthorized" || exception === "Forbidden") {
+          self.authDeniedCb();
+        }
+      });
+    },
+
+    authHandlerCb : function (roles, layout) {
+      var deferred = $.Deferred();
+      deferred.resolve();
+      return deferred;
+    },
+
+    registerAuth: function (authHandlerCb) {
+      this.authHandlerCb = authHandlerCb;
+    },
+
+    registerAuthDenied: function (authDeniedCb) {
+      this.authDeniedCb = authDeniedCb;
+    },
+
+    checkAccess: function (roles) {
+      var requiredRoles = roles || [],
+          authDeniedCb = this.authDeniedCb,
+          promise = $.when.apply(null, this.authHandlerCb(requiredRoles));
+
+      promise.fail(function () { authDeniedCb();});
+
+      return promise;
+    }
+  });
+
+  FauxtonAPI.auth = new Auth();
+
   FauxtonAPI.RouteObject = function(options) {
     this._options = options;
 
@@ -133,6 +184,7 @@ function(app, Fauxton) {
     renderedState: false,
     establish: function() {},
     route: function() {},
+    roles: [],
     initialize: function() {}
   }, {
 
@@ -248,7 +300,23 @@ function(app, Fauxton) {
 
     routeCallback: function (route) {
       var routes = this.get('routes');
-      return this[routes[route]];
+      var routeObj = routes[route];
+
+      if (typeof routeObj === 'object') {
+        return this[routeObj.route];
+      } else {
+        return this[routeObj];
+      }
+    },
+
+    getRouteRoles: function (routeUrl) {
+      var route = this.get('routes')[routeUrl];
+
+      if ((typeof route === 'object') && route.roles) {
+       return route.roles; 
+      }
+
+      return this.roles;
     }
 
   });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/modules/documents/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/routes.js b/src/fauxton/app/modules/documents/routes.js
index 103d904..bb8b193 100644
--- a/src/fauxton/app/modules/documents/routes.js
+++ b/src/fauxton/app/modules/documents/routes.js
@@ -173,7 +173,10 @@ function(app, FauxtonAPI, Documents, Databases) {
 
     routes: {
       "database/:database/_all_docs(:extra)": "allDocs", 
-      "database/:database/_design/:ddoc/_view/:view": "viewFn",
+      "database/:database/_design/:ddoc/_view/:view": {
+        route: "viewFn",
+        roles: ['_admin']
+      },
       "database/:database/new_view": "newViewEditor"
     },
 
@@ -281,8 +284,6 @@ function(app, FauxtonAPI, Documents, Databases) {
 
   });
 
-
-
   var ChangesRouteObject = FauxtonAPI.RouteObject.extend({
     layout: "with_tabs",
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/92257863/src/fauxton/app/router.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/router.js b/src/fauxton/app/router.js
index 45cc8b2..4a79ff1 100644
--- a/src/fauxton/app/router.js
+++ b/src/fauxton/app/router.js
@@ -64,10 +64,16 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
           }
 
           var routeObject = self.activeRouteObject,
-              routeCallback = routeObject.routeCallback(route);
+              routeCallback = routeObject.routeCallback(route),
+              roles = routeObject.getRouteRoles(route);
+
+          var authPromise = app.auth.checkAccess(roles);
+
+          authPromise.then(function () {
+            routeCallback.apply(routeObject, args);
+            routeObject.render(route, masterLayout, args);
+          });
 
-          routeCallback.apply(routeObject, args);
-          routeObject.render(route, masterLayout, args);
         });
       }, this);
     },
@@ -108,6 +114,7 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
       //TODO: It would be nice to handle this with a router
       this.navBar = app.navBar = new Fauxton.NavBar();
       this.apiBar = app.apiBar = new Fauxton.ApiBar();
+      this.auth = app.auth = FauxtonAPI.auth;
 
       app.masterLayout = this.masterLayout = new Layout(this.navBar, this.apiBar);
       app.footer = new Fauxton.Footer({el: "#footer-content"});