diff options
author | suelockwood <deathbear@apache.org> | 2013-12-20 14:16:23 -0500 |
---|---|---|
committer | suelockwood <deathbear@apache.org> | 2013-12-23 12:20:29 -0500 |
commit | 8c333157b335d40225323d34d54c3f5e31e1c40d (patch) | |
tree | 796555bc96ab5241050d2635243f0a3fa8a8fc7c | |
parent | a6d55bbfce42a2c1967ba6c26f82b4c3f85d8e7e (diff) | |
download | couchdb-8c333157b335d40225323d34d54c3f5e31e1c40d.tar.gz |
URL ENCODE ALL THE THINGS.
Mixin added to test for bad characters that get through couch validation & encode when found.
Do not encode already encoded entries.
mixin added for scrubbing special characters out of names used as CSS selectors
SafeIDs used wherever there is a URL in views redirects & models.
Better parsing of _design doc names (use regex)
Allow the use of couchdb special characters / _ , $ etc in names for DBs, Views, Search Indexes etc, without breaking everything.
-rw-r--r-- | src/fauxton/app/mixins.js | 9 | ||||
-rw-r--r-- | src/fauxton/app/modules/databases/base.js | 5 | ||||
-rw-r--r-- | src/fauxton/app/modules/databases/resources.js | 23 | ||||
-rw-r--r-- | src/fauxton/app/modules/databases/views.js | 8 | ||||
-rw-r--r-- | src/fauxton/app/modules/documents/resources.js | 24 | ||||
-rw-r--r-- | src/fauxton/app/modules/documents/routes.js | 8 | ||||
-rw-r--r-- | src/fauxton/app/modules/documents/views.js | 23 | ||||
-rw-r--r-- | src/fauxton/app/templates/databases/item.html | 3 | ||||
-rw-r--r-- | src/fauxton/app/templates/documents/index_menu_item.html | 2 |
9 files changed, 67 insertions, 38 deletions
diff --git a/src/fauxton/app/mixins.js b/src/fauxton/app/mixins.js index b17e15c2d..15af3ee32 100644 --- a/src/fauxton/app/mixins.js +++ b/src/fauxton/app/mixins.js @@ -51,6 +51,15 @@ function($, _ ) { }; }; + mixins.removeSpecialCharacters = function(name){ + return name.replace(/[^\w\s]/gi,""); + }; + + mixins.safeURLName = function(name){ + var checkforBad = name.match(/[\$\-/_,+-]/g); + return (checkforBad !== null)?encodeURIComponent(name):name; + }; + return mixins; }); diff --git a/src/fauxton/app/modules/databases/base.js b/src/fauxton/app/modules/databases/base.js index 2e768e961..6ff12c646 100644 --- a/src/fauxton/app/modules/databases/base.js +++ b/src/fauxton/app/modules/databases/base.js @@ -27,9 +27,10 @@ function(app, FauxtonAPI, Databases, Views) { // Utility functions Databases.databaseUrl = function(database) { - var name = _.isObject(database) ? database.id : database; + var name = _.isObject(database) ? database.id : database, + dbname = app.mixins.safeURLName(name); - return ["/database/", name, "/_all_docs?limit=" + Databases.DocLimit].join(''); + return ["/database/", dbname, "/_all_docs?limit=" + Databases.DocLimit].join(''); }; return Databases; diff --git a/src/fauxton/app/modules/databases/resources.js b/src/fauxton/app/modules/databases/resources.js index a577847da..2e66176d9 100644 --- a/src/fauxton/app/modules/databases/resources.js +++ b/src/fauxton/app/modules/databases/resources.js @@ -51,18 +51,23 @@ function(app, FauxtonAPI, Documents) { url: function(context) { if (context === "index") { - return "/database/" + this.id + "/_all_docs"; + return "/database/" + this.safeID() + "/_all_docs"; } else if (context === "web-index") { - return "#/database/"+ encodeURIComponent(this.get("name")) + "/_all_docs?limit=" + Databases.DocLimit; + return "#/database/"+ this.safeID() + "/_all_docs?limit=" + Databases.DocLimit; } else if (context === "changes") { - return "/database/" + this.id + "/_changes?descending=true&limit=100&include_docs=true"; + return "/database/" + this.safeID() + "/_changes?descending=true&limit=100&include_docs=true"; } else if (context === "app") { - return "/database/" + this.id; + return "/database/" + this.safeID(); } else { - return app.host + "/" + this.id; + return app.host + "/" + this.safeID(); } }, - + safeName: function(){ + return app.mixins.safeURLName(this.get("name")); + }, + safeID: function() { + return app.mixins.safeURLName(this.id); + }, buildChanges: function (params) { this.changes = new Databases.Changes({ database: this, @@ -86,7 +91,7 @@ function(app, FauxtonAPI, Documents) { query = "?" + $.param(this.params); } - return app.host + '/' + this.database.id + '/_changes' + query; + return app.host + '/' + this.database.safeID() + '/_changes' + query; }, parse: function (resp) { @@ -97,7 +102,7 @@ function(app, FauxtonAPI, Documents) { Databases.Status = Backbone.Model.extend({ url: function() { - return app.host + "/" + this.database.id; + return app.host + "/" + this.database.safeID(); }, initialize: function(options) { @@ -156,7 +161,7 @@ function(app, FauxtonAPI, Documents) { // TODO: pagination! return _.map(resp, function(database) { return { - id: encodeURIComponent(database), + id: app.mixins.safeURLName(database), name: database }; }); diff --git a/src/fauxton/app/modules/databases/views.js b/src/fauxton/app/modules/databases/views.js index 02b0297b7..0b4e0b046 100644 --- a/src/fauxton/app/modules/databases/views.js +++ b/src/fauxton/app/modules/databases/views.js @@ -29,7 +29,7 @@ function(app, Components, FauxtonAPI, Databases) { }, serialize: function() { return { - encoded: encodeURIComponent(this.model.get("name")), + encoded: app.mixins.safeURLName(this.model.get("name")), database: this.model, docLimit: Databases.DocLimit }; @@ -82,7 +82,7 @@ function(app, Components, FauxtonAPI, Databases) { // TODO: switch to using a model, or Databases.databaseUrl() // Neither of which are in scope right now // var db = new Database.Model({id: dbname}); - var url = ["/database/", encodeURIComponent(dbname), "/_all_docs?limit=" + Databases.DocLimit].join(''); + var url = ["/database/", app.mixins.safeURLName(dbname), "/_all_docs?limit=" + Databases.DocLimit].join(''); FauxtonAPI.navigate(url); } }, @@ -159,7 +159,7 @@ function(app, Components, FauxtonAPI, Databases) { return; } db = new this.collection.model({ - id: encodeURIComponent(name), + id: name, name: name }); notification = FauxtonAPI.addNotification({msg: "Creating database."}); @@ -169,7 +169,7 @@ function(app, Components, FauxtonAPI, Databases) { type: "success", clear: true }); - var route = "#/database/" + name + "/_all_docs?limit=" + Databases.DocLimit; + var route = "#/database/" + app.mixins.safeURLName(name) + "/_all_docs?limit=" + Databases.DocLimit; app.router.navigate(route, { trigger: true }); } ).error(function(xhr) { diff --git a/src/fauxton/app/modules/documents/resources.js b/src/fauxton/app/modules/documents/resources.js index 863360518..a171caed5 100644 --- a/src/fauxton/app/modules/documents/resources.js +++ b/src/fauxton/app/modules/documents/resources.js @@ -27,7 +27,7 @@ function(app, FauxtonAPI) { if (context === "app") { return this.getDatabase().url("app") + "/" + this.safeID(); } else { - return app.host + "/" + this.getDatabase().id + "/" + this.id; + return app.host + "/" + this.getDatabase().safeID() + "/" + this.safeID(); } }, @@ -139,7 +139,7 @@ function(app, FauxtonAPI) { // treated separately. For instance, we could default into the // json editor for docs, or into a ddoc specific page. safeID: function() { - return this.id.replace('/', '%2F'); + return app.mixins.safeURLName(this.id); }, destroy: function() { @@ -177,7 +177,7 @@ function(app, FauxtonAPI) { copy: function (copyId) { return $.ajax({ type: 'COPY', - url: '/' + this.database.id + '/' + this.id, + url: '/' + this.database.safeID() + '/' + this.safeID(), headers: {Destination: copyId} }); }, @@ -200,7 +200,7 @@ function(app, FauxtonAPI) { if (context === "app") { return this.database.url("app") + "/" + this.safeID() + '/_info'; } else { - return app.host + "/" + this.database.id + "/" + this.id + '/_info'; + return app.host + "/" + this.database.safeID() + "/" + this.safeID() + '/_info'; } }, @@ -209,7 +209,8 @@ function(app, FauxtonAPI) { // treated separately. For instance, we could default into the // json editor for docs, or into a ddoc specific page. safeID: function() { - return this.id.replace('/', '%2F'); + var ddoc = this.id.replace(/_design\//,""); + return "_design/"+app.mixins.safeURLName(ddoc); } }); @@ -226,12 +227,15 @@ function(app, FauxtonAPI) { url: function(context) { if (!this.isEditable()) return false; - return this.collection.database.url(context) + "/" + this.id; + return this.collection.database.url(context) + "/" + this.safeID(); }, isEditable: function() { return this.docType() != "reduction"; }, + safeID: function() { + return app.mixins.safeURLName(this.id); + }, prettyJSON: function() { //var data = this.get("doc") ? this.get("doc") : this; @@ -275,9 +279,9 @@ function(app, FauxtonAPI) { } if (context === 'app') { - return 'database/' + this.database.id + "/_all_docs" + query; + return 'database/' + this.database.safeID() + "/_all_docs" + query; } - return app.host + "/" + this.database.id + "/_all_docs" + query; + return app.host + "/" + this.database.safeID() + "/_all_docs" + query; }, simple: function () { @@ -402,8 +406,10 @@ function(app, FauxtonAPI) { if (context === 'app') { startOfUrl = 'database'; } + var design = app.mixins.safeURLName(this.design), + view = app.mixins.safeURLName(this.view); - var url = [startOfUrl, this.database.id, "_design", this.design, this.idxType, this.view]; + var url = [startOfUrl, this.database.safeID(), "_design", design, this.idxType, view]; return url.join("/") + query; }, diff --git a/src/fauxton/app/modules/documents/routes.js b/src/fauxton/app/modules/documents/routes.js index b8b74b669..4727cdc78 100644 --- a/src/fauxton/app/modules/documents/routes.js +++ b/src/fauxton/app/modules/documents/routes.js @@ -92,7 +92,7 @@ function(app, FauxtonAPI, Documents, Databases) { doc.copy(newId).then(function () { doc.set({_id: newId}); docView.forceRender(); - FauxtonAPI.navigate('/database/' + database.id + '/' + newId, {trigger: true}); + FauxtonAPI.navigate('/database/' + database.safeID() + '/' + app.mixins.safeURLName(newId), {trigger: true}); FauxtonAPI.addNotification({ msg: "Document has been duplicated." }); @@ -162,7 +162,7 @@ function(app, FauxtonAPI, Documents, Databases) { var docOptions = app.getParams(); docOptions.include_docs = true; - this.databaseName = encodeURIComponent(options[0]); + this.databaseName = app.mixins.safeURLName(options[0]); this.data = { database: new Databases.Model({id:this.databaseName}) @@ -259,7 +259,7 @@ function(app, FauxtonAPI, Documents, Databases) { ddocInfo: ddocInfo })); - this.sidebar.setSelectedTab(ddoc + '_' + view); + this.sidebar.setSelectedTab(app.mixins.removeSpecialCharacters(ddoc) + '_' + app.mixins.removeSpecialCharacters(view)); this.crumbs = function () { return [ @@ -373,7 +373,7 @@ function(app, FauxtonAPI, Documents, Databases) { }, initialize: function (route, masterLayout, options) { - this.databaseName = encodeURIComponent(options[0]); + this.databaseName = app.mixins.safeURLName(options[0]); this.database = new Databases.Model({id: this.databaseName}); var docOptions = app.getParams(); diff --git a/src/fauxton/app/modules/documents/views.js b/src/fauxton/app/modules/documents/views.js index 9f95686c0..94fe699be 100644 --- a/src/fauxton/app/modules/documents/views.js +++ b/src/fauxton/app/modules/documents/views.js @@ -420,6 +420,11 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum index: this.index, ddoc: this.ddoc, database: this.database, + index_clean: app.mixins.removeSpecialCharacters(this.index), + ddoc_clean: app.mixins.removeSpecialCharacters(this.ddoc), + index_encoded: app.mixins.safeURLName(this.index), + ddoc_encoded: app.mixins.safeURLName(this.ddoc), + database_encoded: app.mixins.safeURLName(this.database), selected: this.selected }; }, @@ -798,7 +803,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum establish: function() { var promise = this.model.fetch(), - databaseId = this.database.id, + databaseId = this.database.safeID(), deferred = $.Deferred(); promise.then(function () { @@ -831,7 +836,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum this.model.save().then(function () { editor.editSaved(); - FauxtonAPI.navigate('/database/' + that.database.id + '/' + that.model.id); + FauxtonAPI.navigate('/database/' + that.database.safeID() + '/' + that.model.id); }).fail(function(xhr) { var responseText = JSON.parse(xhr.responseText).reason; notification = FauxtonAPI.addNotification({ @@ -1270,7 +1275,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum } promise.then(function () { - FauxtonAPI.navigate('/database/' + that.database.id + '/_all_docs?limit=' + Databases.DocLimit); + FauxtonAPI.navigate('/database/' + that.database.safeID() + '/_all_docs?limit=' + Databases.DocLimit); FauxtonAPI.triggerRouteEvent('reloadDesignDocs'); }); }, @@ -1308,16 +1313,18 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum }); if (that.newView) { - var fragment = '/database/' + that.database.id +'/' + ddocName + '/_view/' + viewName; + var fragment = '/database/' + that.database.safeID() +'/' + ddoc.safeID() + '/_view/' + app.mixins.safeURLName(viewName); FauxtonAPI.navigate(fragment, {trigger: false}); that.newView = false; - that.ddocID = ddoc.id; + that.ddocID = ddoc.safeID(); that.viewName = viewName; that.ddocInfo = ddoc; that.showIndex = true; that.render(); - FauxtonAPI.triggerRouteEvent('reloadDesignDocs',{selectedTab: ddocName.replace('_design/','') + '_' + viewName}); + FauxtonAPI.triggerRouteEvent('reloadDesignDocs', { + selectedTab: app.mixins.removeSpecialCharacters(ddocName.replace(/_design\//,'')) + '_' + app.mixins.removeSpecialCharacters(viewName) + }); } if (that.reduceFunStr !== reduceVal) { @@ -1614,7 +1621,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum var docId = this.$('#jump-to-doc-id').val().trim(); if (this.database.allDocs.where({"_id":docId}).length > 0){ - FauxtonAPI.navigate('/database/' + this.database.id +'/' + docId, {trigger: true}); + FauxtonAPI.navigate('/database/' + app.mixins.safeURLName(this.database.id) +'/' + app.mixins.safeURLName(docId), {trigger: true}); } else { FauxtonAPI.addNotification({ msg: 'Document ID does not exist.', @@ -1707,7 +1714,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum this.collection.each(function(design) { if (design.has('doc')){ - var ddoc = design.id.split('/')[1]; + var ddoc = design.id.replace(/_design\//,""); if (design.get('doc').views){ this.buildIndexList(design.get('doc').views, "views", ddoc); } diff --git a/src/fauxton/app/templates/databases/item.html b/src/fauxton/app/templates/databases/item.html index 701e58e71..e2f80712f 100644 --- a/src/fauxton/app/templates/databases/item.html +++ b/src/fauxton/app/templates/databases/item.html @@ -19,5 +19,6 @@ the License. <td><%= database.status.numDocs() %></td> <td><%= database.status.updateSeq() %></td> <td> - <a class="db-actions btn fonticon-replicate set-replication-start" href="#/replication/<%= database.get("name") %>"></a> + <a class="db-actions btn fonticon-replicate set-replication-start" title="Replicate <%= database.get("name") %>" href="#/replication/new/<%=encoded%>"></a> + <a class="db-actions btn icon-lock set-permissions" title="Set permissions for <%= database.get("name") %>" href="#/database/<%=encoded%>/permissions"></a> </td> diff --git a/src/fauxton/app/templates/documents/index_menu_item.html b/src/fauxton/app/templates/documents/index_menu_item.html index 1b141b292..7ca9012e6 100644 --- a/src/fauxton/app/templates/documents/index_menu_item.html +++ b/src/fauxton/app/templates/documents/index_menu_item.html @@ -12,6 +12,6 @@ License for the specific language governing permissions and limitations under the License. --> -<a id="<%= ddoc %>_<%= index %>" href="#database/<%= database %>/_design/<%= ddoc %>/_view/<%= index %>" class="toggle-view"> +<a id="<%= ddoc_clean %>_<%= index_clean %>" href="#database/<%= database_encoded %>/_design/<%= ddoc_encoded %>/_view/<%= index_encoded %>" class="toggle-view"> <%= ddoc %><span class="divider">/</span><%= index %> </a> |