Updated Branches: refs/heads/master c7249d028 -> 221ada71f
Fauxton: Permissions settings for databases Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/724b3f2c Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/724b3f2c Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/724b3f2c Branch: refs/heads/master Commit: 724b3f2c1e162ddf8c40a1748a4c5b49cd163c85 Parents: c7249d0 Author: Garren Smith <[email protected]> Authored: Tue Sep 24 13:31:01 2013 +0200 Committer: Garren Smith <[email protected]> Committed: Fri Sep 27 11:27:26 2013 +0200 ---------------------------------------------------------------------- .gitignore | 1 + src/fauxton/app/addons/permissions/base.js | 25 +++ src/fauxton/app/addons/permissions/resources.js | 70 +++++++ src/fauxton/app/addons/permissions/routes.js | 64 ++++++ .../app/addons/permissions/templates/item.html | 17 ++ .../permissions/templates/permissions.html | 15 ++ .../addons/permissions/templates/section.html | 37 ++++ .../addons/permissions/tests/resourceSpec.js | 51 +++++ .../app/addons/permissions/tests/viewsSpec.js | 159 +++++++++++++++ src/fauxton/app/addons/permissions/views.js | 199 +++++++++++++++++++ src/fauxton/settings.json.default | 1 + 11 files changed, 639 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index a4798c9..58f992e 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,7 @@ src/fauxton/app/addons/* !src/fauxton/app/addons/contribute !src/fauxton/app/addons/auth !src/fauxton/app/addons/exampleAuth +!src/fauxton/app/addons/permissions src/fauxton/settings.json* !src/fauxton/settings.json.default share/www/fauxton http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/base.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/base.js b/src/fauxton/app/addons/permissions/base.js new file mode 100644 index 0000000..016ba1c --- /dev/null +++ b/src/fauxton/app/addons/permissions/base.js @@ -0,0 +1,25 @@ +// 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", + "addons/permissions/routes" +], + +function(app, FauxtonAPI, Permissions) { + + Permissions.initialize = function() {}; + + return Permissions; +}); + http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/resources.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/resources.js b/src/fauxton/app/addons/permissions/resources.js new file mode 100644 index 0000000..66eaffd --- /dev/null +++ b/src/fauxton/app/addons/permissions/resources.js @@ -0,0 +1,70 @@ +// 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 ) { + var Permissions = FauxtonAPI.addon(); + + Permissions.Security = Backbone.Model.extend({ + defaults: { + admins: { + names: [], + roles: [] + }, + + members: { + names: [], + roles: [] + } + }, + + isNew: function () { + return false; + }, + + initialize: function (attrs, options) { + this.database = options.database; + }, + + url: function () { + return this.database.id + '/_security'; + }, + + addItem: function (value, type, section) { + var sectionValues = this.get(section); + + if (!sectionValues || !sectionValues[type]) { + return { + error: true, + msg: 'Section ' + section + 'does not exist' + }; + } + + if (sectionValues[type].indexOf(value) > -1) { + return { + error: true, + msg: 'Role/Name has already been added' + }; + } + + sectionValues[type].push(value); + return this.set(section, sectionValues); + } + + }); + + return Permissions; +}); + http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/routes.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/routes.js b/src/fauxton/app/addons/permissions/routes.js new file mode 100644 index 0000000..f6c6587 --- /dev/null +++ b/src/fauxton/app/addons/permissions/routes.js @@ -0,0 +1,64 @@ +// 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", + "modules/databases/base", + "addons/permissions/views" +], +function (app, FauxtonAPI, Databases, Permissions) { + + var PermissionsRouteObject = FauxtonAPI.RouteObject.extend({ + layout: 'one_pane', + selectedHeader: 'Databases', + + routes: { + 'database/:database/permissions': 'permissions' + }, + + initialize: function (route, masterLayout, options) { + var docOptions = app.getParams(); + docOptions.include_docs = true; + + this.databaseName = options[0]; + this.database = new Databases.Model({id:this.databaseName}); + this.security = new Permissions.Security(null, { + database: this.database + }); + }, + + establish: function () { + return [this.database.fetch(), this.security.fetch()]; + }, + + permissions: function () { + this.setView('#dashboard-content', new Permissions.Permissions({ + database: this.database, + model: this.security + })); + + }, + + crumbs: function () { + return [ + {"name": "Databases", "link": "/_all_dbs"}, + {"name": this.database.id, "link": Databases.databaseUrl(this.database)}, + {"name": "Permissions", "link": "/permissions"} + ]; + }, + + }); + + Permissions.RouteObjects = [PermissionsRouteObject]; + return Permissions; +}); http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/templates/item.html ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/templates/item.html b/src/fauxton/app/addons/permissions/templates/item.html new file mode 100644 index 0000000..aa01d0b --- /dev/null +++ b/src/fauxton/app/addons/permissions/templates/item.html @@ -0,0 +1,17 @@ +<!-- +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. +--> +<li> +<span> <%= item %> </span> +<button type="button" style="float:none; margin-bottom:6px" class="close">×</button> +</li> http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/templates/permissions.html ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/templates/permissions.html b/src/fauxton/app/addons/permissions/templates/permissions.html new file mode 100644 index 0000000..d247425 --- /dev/null +++ b/src/fauxton/app/addons/permissions/templates/permissions.html @@ -0,0 +1,15 @@ +<!-- +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. +--> +<p>Each database contains lists of admins and members. Admins and members are each defined by names and roles, which are lists of strings.</p> +<div id="sections"> </div> http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/templates/section.html ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/templates/section.html b/src/fauxton/app/addons/permissions/templates/section.html new file mode 100644 index 0000000..102d857 --- /dev/null +++ b/src/fauxton/app/addons/permissions/templates/section.html @@ -0,0 +1,37 @@ +<!-- +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. +--> +<h1> <%= section %> </h1> +<p id="help"> <%= help %> </p> + +<div class="row"> + <div class="span6"> + <h3> Names </h3> + <ul id="items-names"></ul> + + <form class="permission-item-form form-inline"> + <input data-section="<%= section %>" data-type="names" type="text" class="item input-small" placeholder="Add Name"> + <input type="submit" class="btn" value="Add Name" > + </form> + + </div> + <div class="span6"> + <h3> Roles </h3> + <ul id="items-roles"></ul> + + <form class="permission-item-form form-inline"> + <input data-section="<%= section %>" data-type="roles" type="text" class="item input-small" placeholder="Add Role"> + <input type="submit" class="btn" value="Add Role" > + </form> + </div> +</div> http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/tests/resourceSpec.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/tests/resourceSpec.js b/src/fauxton/app/addons/permissions/tests/resourceSpec.js new file mode 100644 index 0000000..f73687a --- /dev/null +++ b/src/fauxton/app/addons/permissions/tests/resourceSpec.js @@ -0,0 +1,51 @@ +// 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([ + 'api', + 'addons/permissions/resources', + 'testUtils' +], function (FauxtonAPI, Models, testUtils) { + var assert = testUtils.assert; + + describe('Permissions', function () { + + describe('#addItem', function () { + var security; + + beforeEach(function () { + security = new Models.Security(null, {database: 'fakedb'}); + }); + + it('Should add value to section', function () { + + security.addItem('_user', 'names', 'admins'); + assert.equal(security.get('admins').names[0], '_user'); + }); + + it('Should handle incorrect type', function () { + security.addItem('_user', 'asdasd', 'admins'); + }); + + it('Should handle incorrect section', function () { + security.addItem('_user', 'names', 'Asdasd'); + }); + + it('Should reject duplicates', function () { + security.addItem('_user', 'names', 'admins'); + security.addItem('_user', 'names', 'admins'); + assert.equal(security.get('admins').names.length, 1); + }); + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/tests/viewsSpec.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/tests/viewsSpec.js b/src/fauxton/app/addons/permissions/tests/viewsSpec.js new file mode 100644 index 0000000..e5330c0 --- /dev/null +++ b/src/fauxton/app/addons/permissions/tests/viewsSpec.js @@ -0,0 +1,159 @@ +// 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([ + 'api', + 'addons/permissions/views', + 'addons/permissions/resources', + 'testUtils' +], function (FauxtonAPI, Views, Models, testUtils) { + var assert = testUtils.assert, + ViewSandbox = testUtils.ViewSandbox; + + describe('Permission View', function () { + + beforeEach(function () { + security = new Models.Security({'admins': { + 'names': ['_user'], + 'roles': [] + } + }, {database: 'fakedb'}); + + section = new Views.Permissions({ + database: 'fakedb', + model: security + }); + + viewSandbox = new ViewSandbox(); + viewSandbox.renderView(section); + }); + + afterEach(function () { + viewSandbox.remove(); + }); + + describe('itemRemoved', function () { + + it('Should set model', function () { + var saveMock = sinon.spy(security, 'set'); + Views.events.trigger('itemRemoved'); + + assert.ok(saveMock.calledOnce); + var args = saveMock.args; + assert.deepEqual(args[0][0], {"admins":{"names":["_user"],"roles":[]},"members":{"names":[],"roles":[]}}); + }); + + it('Should save model', function () { + var saveMock = sinon.spy(security, 'save'); + Views.events.trigger('itemRemoved'); + + assert.ok(saveMock.calledOnce); + }); + }); + + }); + + describe('PermissionsSection', function () { + var section, security; + + beforeEach(function () { + security = new Models.Security({'admins': { + 'names': ['_user'], + 'roles': [] + } + }, {database: 'fakedb'}); + + section = new Views.PermissionSection({ + section: 'admins', + model: security + }); + + viewSandbox = new ViewSandbox(); + viewSandbox.renderView(section); + }); + + afterEach(function () { + viewSandbox.remove(); + }); + + describe('#discardRemovedViews', function () { + it('Should not filter out active views', function () { + section.discardRemovedViews(); + + assert.equal(section.nameViews.length, 1); + + }); + + it('Should filter out removed views', function () { + section.nameViews[0].removed = true; + section.discardRemovedViews(); + + assert.equal(section.nameViews.length, 0); + + }); + + }); + + describe('#getItemFromView', function () { + + it('Should return item list', function () { + var items = section.getItemFromView(section.nameViews); + + assert.deepEqual(items, ['_user']); + }); + + }); + + describe('#addItems', function () { + + it('Should add item to model', function () { + //todo add a test here + + }); + + }); + + }); + + describe('PermissionItem', function () { + var item; + + beforeEach(function () { + item = new Views.PermissionItem({ + item: '_user' + }); + + viewSandbox = new ViewSandbox(); + viewSandbox.renderView(item); + }); + + afterEach(function () { + viewSandbox.remove(); + }); + + it('should trigger event on remove item', function () { + var eventSpy = sinon.spy(); + + Views.events.on('itemRemoved', eventSpy); + + item.$('.close').click(); + + assert.ok(eventSpy.calledOnce); + }); + + it('should set removed to true', function () { + item.$('.close').click(); + + assert.ok(item.removed); + }); + }); + +}); http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/app/addons/permissions/views.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/permissions/views.js b/src/fauxton/app/addons/permissions/views.js new file mode 100644 index 0000000..4c2987b --- /dev/null +++ b/src/fauxton/app/addons/permissions/views.js @@ -0,0 +1,199 @@ +// 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", + "addons/permissions/resources" +], +function (app, FauxtonAPI, Permissions ) { + var events = {}; + Permissions.events = _.extend(events, Backbone.Events); + + Permissions.Permissions = FauxtonAPI.View.extend({ + template: "addons/permissions/templates/permissions", + + initialize: function (options) { + this.database = options.database; + this.listenTo(Permissions.events, 'itemRemoved', this.itemRemoved); + }, + + itemRemoved: function (event) { + this.model.set({ + admins: this.adminsView.items(), + members: this.membersView.items() + }); + + this.model.save().then(function () { + FauxtonAPI.addNotification({ + msg: 'Database permissions has been updated.' + }); + }, function (xhr) { + FauxtonAPI.addNotification({ + msg: 'Could not update permissions - reason: ' + xhr.responseText, + type: 'error' + }); + }); + }, + + beforeRender: function () { + this.adminsView = this.insertView('#sections', new Permissions.PermissionSection({ + model: this.model, + section: 'admins', + help: 'Database admins can update design documents and edit the admin and member lists.' + })); + + this.membersView = this.insertView('#sections', new Permissions.PermissionSection({ + model: this.model, + section: 'members', + help: 'Database members can access the database. If no members are defined, the database is public.' + })); + }, + + serialize: function () { + return { + databaseName: this.database.id, + }; + } + }); + + Permissions.PermissionSection = FauxtonAPI.View.extend({ + template: "addons/permissions/templates/section", + initialize: function (options) { + this.section = options.section; + this.help = options.help; + }, + + events: { + "submit .permission-item-form": "addItem", + 'click .close': "removeItem" + }, + + beforeRender: function () { + var section = this.model.get(this.section); + + this.nameViews = []; + this.roleViews = []; + + _.each(section.names, function (name) { + var nameView = this.insertView('#items-names', new Permissions.PermissionItem({ + item: name, + })); + this.nameViews.push(nameView); + }, this); + + _.each(section.roles, function (role) { + var roleView = this.insertView('#items-roles', new Permissions.PermissionItem({ + item: role, + })); + this.roleViews.push(roleView); + }, this); + }, + + getItemFromView: function (viewList) { + return _.map(viewList, function (view) { + return view.item; + }); + }, + + discardRemovedViews: function () { + this.nameViews = _.filter(this.nameViews, function (view) { + return !view.removed; + }); + + this.roleViews = _.filter(this.roleViews, function (view) { + return !view.removed; + }); + }, + + items: function () { + this.discardRemovedViews(); + + return { + names: this.getItemFromView(this.nameViews), + roles: this.getItemFromView(this.roleViews) + }; + }, + + addItem: function (event) { + event.preventDefault(); + var $item = this.$(event.currentTarget).find('.item'), + value = $item.val(), + section = $item.data('section'), + type = $item.data('type'), + that = this; + + var resp = this.model.addItem(value, type, section); + + if (resp && resp.error) { + return FauxtonAPI.addNotification({ + msg: resp.msg, + type: 'error' + }); + } + + this.model.save().then(function () { + that.render(); + FauxtonAPI.addNotification({ + msg: 'Database permissions has been updated.' + }); + }, function (xhr) { + FauxtonAPI.addNotification({ + msg: 'Could not update permissions - reason: ' + xhr.responseText, + type: 'error' + }); + }); + }, + + serialize: function () { + return { + section: this.section, + help: this.help + }; + } + + }); + + Permissions.PermissionItem = FauxtonAPI.View.extend({ + template: "addons/permissions/templates/item", + initialize: function (options) { + this.item = options.item; + this.viewsList = options.viewsList; + }, + + events: { + 'click .close': "removeItem" + }, + + removeItem: function (event) { + var that = this; + event.preventDefault(); + + this.removed = true; + Permissions.events.trigger('itemRemoved'); + + this.$el.hide('fast', function () { + that.remove(); + }); + }, + + + serialize: function () { + return { + item: this.item + }; + } + + }); + + return Permissions; +}); http://git-wip-us.apache.org/repos/asf/couchdb/blob/724b3f2c/src/fauxton/settings.json.default ---------------------------------------------------------------------- diff --git a/src/fauxton/settings.json.default b/src/fauxton/settings.json.default index 81cb4cb..30088f0 100644 --- a/src/fauxton/settings.json.default +++ b/src/fauxton/settings.json.default @@ -6,6 +6,7 @@ { "name": "stats" }, { "name": "replication" }, { "name": "contribute" }, + { "name": "permissions" }, { "name": "auth" } ], "template": {
