summaryrefslogtreecommitdiff
path: root/share
diff options
context:
space:
mode:
authorDamien F. Katz <damien@apache.org>2008-11-17 18:18:51 +0000
committerDamien F. Katz <damien@apache.org>2008-11-17 18:18:51 +0000
commitaee6f18edf8cdf3f7c09c93fcf1af48c2c15fcd8 (patch)
treefd3a259ba4b0edd2c9ea5e6657f0ae314b36cbb5 /share
parentd32d8acff4bac6f51b87ddef7091c04ff7245d40 (diff)
More security and validation work. Still incomplete.
git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@718311 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'share')
-rw-r--r--share/www/script/couch.js181
-rw-r--r--share/www/script/couch_tests.js27
2 files changed, 112 insertions, 96 deletions
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index cae0abc9..934dacf8 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -13,58 +13,64 @@
// A simple class to represent a database. Uses XMLHttpRequest to interface with
// the CouchDB server.
-function CouchDB(name, options) {
+function CouchDB(name, httpHeaders) {
this.name = name;
this.uri = "/" + encodeURIComponent(name) + "/";
+
+ // The XMLHttpRequest object from the most recent request. Callers can
+ // use this to check result http status and headers.
+ this.last_req = null;
+
request = function(method, uri, requestOptions) {
- return CouchDB.request(method, uri, combine(requestOptions, options));
+ requestOptions = requestOptions || {}
+ requestOptions.headers = combine(requestOptions.headers, httpHeaders)
+ return CouchDB.request(method, uri, requestOptions);
}
// Creates the database on the server
this.createDb = function() {
- var req = request("PUT", this.uri);
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ this.last_req = request("PUT", this.uri);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
// Deletes the database on the server
this.deleteDb = function() {
- var req = request("DELETE", this.uri);
- if (req.status == 404)
+ this.last_req = request("DELETE", this.uri);
+ if (this.last_req.status == 404)
return false;
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
// Save a document to the database
this.save = function(doc, options) {
- var req;
if (doc._id == undefined)
doc._id = CouchDB.newUuids(1)[0];
- req = request("PUT", this.uri + encodeURIComponent(doc._id) + encodeOptions(options), {
- body: JSON.stringify(doc)
- });
- maybeThrowError(req);
- var result = JSON.parse(req.responseText);
+ this.last_req = request("PUT", this.uri +
+ encodeURIComponent(doc._id) + encodeOptions(options),
+ {body: JSON.stringify(doc)});
+ CouchDB.maybeThrowError(this.last_req);
+ var result = JSON.parse(this.last_req.responseText);
doc._rev = result.rev;
return result;
}
// Open a document from the database
this.open = function(docId, options) {
- var req = request("GET", this.uri + encodeURIComponent(docId) + encodeOptions(options));
- if (req.status == 404)
+ this.last_req = request("GET", this.uri + encodeURIComponent(docId) + encodeOptions(options));
+ if (this.last_req.status == 404)
return null;
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
// Deletes a document from the database
this.deleteDoc = function(doc) {
- var req = request("DELETE", this.uri + encodeURIComponent(doc._id) + "?rev=" + doc._rev);
- maybeThrowError(req);
- var result = JSON.parse(req.responseText);
+ this.last_req = request("DELETE", this.uri + encodeURIComponent(doc._id) + "?rev=" + doc._rev);
+ CouchDB.maybeThrowError(this.last_req);
+ var result = JSON.parse(this.last_req.responseText);
doc._rev = result.rev; //record rev in input document
doc._deleted = true;
return result;
@@ -72,9 +78,9 @@ function CouchDB(name, options) {
// Deletes an attachment from a document
this.deleteDocAttachment = function(doc, attachment_name) {
- var req = request("DELETE", this.uri + encodeURIComponent(doc._id) + "/" + attachment_name + "?rev=" + doc._rev);
- maybeThrowError(req);
- var result = JSON.parse(req.responseText);
+ this.last_req = request("DELETE", this.uri + encodeURIComponent(doc._id) + "/" + attachment_name + "?rev=" + doc._rev);
+ CouchDB.maybeThrowError(this.last_req);
+ var result = JSON.parse(this.last_req.responseText);
doc._rev = result.rev; //record rev in input document
return result;
}
@@ -92,11 +98,11 @@ function CouchDB(name, options) {
if (docs[i]._id == undefined)
docs[i]._id = newUuids.pop();
}
- var req = request("POST", this.uri + "_bulk_docs" + encodeOptions(options), {
+ this.last_req = request("POST", this.uri + "_bulk_docs" + encodeOptions(options), {
body: JSON.stringify({"docs": docs})
});
- maybeThrowError(req);
- var result = JSON.parse(req.responseText);
+ CouchDB.maybeThrowError(this.last_req);
+ var result = JSON.parse(this.last_req.responseText);
for (var i = 0; i < docs.length; i++) {
docs[i]._rev = result.new_revs[i].rev;
}
@@ -117,49 +123,49 @@ function CouchDB(name, options) {
reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")";
body.reduce = reduceFun;
}
- var req = request("POST", this.uri + "_temp_view" + encodeOptions(options), {
+ this.last_req = request("POST", this.uri + "_temp_view" + encodeOptions(options), {
headers: {"Content-Type": "application/json"},
body: JSON.stringify(body)
});
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
this.view = function(viewname, options, keys) {
- var req = null ;
if(!keys) {
- req = request("GET", this.uri + "_view/" + viewname + encodeOptions(options));
+ this.last_req = request("GET", this.uri + "_view/" +
+ viewname + encodeOptions(options));
} else {
- req = request("POST", this.uri + "_view/" + viewname + encodeOptions(options), {
+ this.last_req = request("POST", this.uri + "_view/" +
+ viewname + encodeOptions(options), {
headers: {"Content-Type": "application/json"},
body: JSON.stringify({keys:keys})
});
}
- if (req.status == 404)
+ if (this.last_req.status == 404)
return null;
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
// gets information about the database
this.info = function() {
- var req = request("GET", this.uri);
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ this.last_req = request("GET", this.uri);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
this.allDocs = function(options,keys) {
- var req = null;
if(!keys) {
- req = request("GET", this.uri + "_all_docs" + encodeOptions(options));
+ this.last_req = request("GET", this.uri + "_all_docs" + encodeOptions(options));
} else {
- req = request("POST", this.uri + "_all_docs" + encodeOptions(options), {
+ this.last_req = request("POST", this.uri + "_all_docs" + encodeOptions(options), {
headers: {"Content-Type": "application/json"},
body: JSON.stringify({keys:keys})
});
}
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
this.allDocsBySeq = function(options,keys) {
@@ -172,14 +178,14 @@ function CouchDB(name, options) {
body: JSON.stringify({keys:keys})
});
}
- maybeThrowError(req);
+ CouchDB.maybeThrowError(req);
return JSON.parse(req.responseText);
}
this.compact = function() {
- var req = request("POST", this.uri + "_compact");
- maybeThrowError(req);
- return JSON.parse(req.responseText);
+ this.last_req = request("POST", this.uri + "_compact");
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
}
// Convert a options object to an url query string.
@@ -207,59 +213,44 @@ function CouchDB(name, options) {
}
function combine(object1, object2) {
- if (!object2) {
+ if (!object2)
return object1;
- }
- if (!object1) {
+ if (!object1)
return object2;
- }
- for (var name in object2) {
+
+ for (var name in object2)
object1[name] = object2[name];
- }
+
return object1;
}
- function maybeThrowError(req) {
- if (req.status >= 400) {
- if (req.responseText) {
- try {
- var result = JSON.parse(req.responseText);
- } catch (ParseError) {
- var result = {error:"unknown", reason:req.responseText};
- }
- } else {
- var result = {};
- }
- result.http_status = req.status;
- throw result;
- }
- }
+
}
+// this is the XMLHttpRequest object from last request made by the following
+// CouchDB.* functions (except for calls to request itself).
+// Use this from callers to check HTTP status or header values of requests.
+CouchDB.last_req = null;
+
+
CouchDB.allDbs = function() {
- var req = CouchDB.request("GET", "/_all_dbs");
- var result = JSON.parse(req.responseText);
- if (req.status != 200)
- throw result;
- return result;
+ CouchDB.last_req = CouchDB.request("GET", "/_all_dbs");
+ CouchDB.maybeThrowError(CouchDB.last_req);
+ return JSON.parse(CouchDB.last_req.responseText);
}
CouchDB.getVersion = function() {
- var req = CouchDB.request("GET", "/");
- var result = JSON.parse(req.responseText);
- if (req.status != 200)
- throw result;
- return result.version;
+ CouchDB.last_req = CouchDB.request("GET", "/");
+ CouchDB.maybeThrowError(CouchDB.last_req);
+ return JSON.parse(CouchDB.last_req.responseText).version;
}
CouchDB.replicate = function(source, target) {
- var req = CouchDB.request("POST", "/_replicate", {
+ CouchDB.last_req = CouchDB.request("POST", "/_replicate", {
body: JSON.stringify({source: source, target: target})
});
- var result = JSON.parse(req.responseText);
- if (req.status != 200)
- throw result;
- return result;
+ CouchDB.maybeThrowError(CouchDB.last_req);
+ return JSON.parse(CouchDB.last_req.responseText);
}
CouchDB.request = function(method, uri, options) {
@@ -272,7 +263,7 @@ CouchDB.request = function(method, uri, options) {
} else {
throw new Error("No XMLHTTPRequest support detected");
}
- req.open(method, uri, false, options.username, options.password);
+ req.open(method, uri, false);
if (options.headers) {
var headers = options.headers;
for (var headerName in headers) {
@@ -297,12 +288,22 @@ CouchDB.newUuids = function(n) {
}
return uuids;
} else {
- var req = CouchDB.request("POST", "/_uuids?count=" + (100 + n));
- var result = JSON.parse(req.responseText);
- if (req.status != 200)
- throw result;
+ CouchDB.last_req = CouchDB.request("POST", "/_uuids?count=" + (100 + n));
+ CouchDB.maybeThrowError(CouchDB.last_req);
+ var result = JSON.parse(CouchDB.last_req.responseText);
CouchDB.uuids_cache =
CouchDB.uuids_cache.concat(result.uuids.slice(0, 100));
return result.uuids.slice(100);
}
}
+
+CouchDB.maybeThrowError = function(req) {
+ if (req.status >= 400) {
+ try {
+ var result = JSON.parse(req.responseText);
+ } catch (ParseError) {
+ var result = {error:"unknown", reason:req.responseText};
+ }
+ throw result;
+ }
+}
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index 8575da6f..3c14b761 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -1991,11 +1991,25 @@ var tests = {
reason:"Documents must have an author field",
http_status:403};
}
- if (oldDoc && oldDoc.author != userCtx.name) {
+
+ // Note, the next line could be:
+ //
+ // if (oldDoc && oldDoc.author != userCtx.name) {
+ //
+ // when name is the result of basic authentication, and added:
+ //
+ // headers:
+ // {"WWW-Authenticate": "Basic realm=\"" + userCtx.db + "\""},
+ //
+ // to the thrown exception. But when trying to authenticate in this
+ // manner, most browsers have weird behaviors that make testing it
+ // in the browser difficult. So instead we use special header values
+ // a proof of concept.
+ if (oldDoc && oldDoc.author != userCtx["X-Couch-Username"]) {
throw {error:"unauthorized",
reason:"You are not the author of this document. You jerk.",
headers:
- {"WWW-Authenticate": "Basic realm=\"" + userCtx.db + "\""},
+ {"X-Couch-Foo": "bar"},
http_status:401};
}
}).toString() + ")"
@@ -2003,14 +2017,14 @@ var tests = {
db.save(designDoc);
- var userDb = new CouchDB("test_suite_db", {username:"test user", password:"foo"});
+ var userDb = new CouchDB("test_suite_db", {"X-Couch-Username":"test user"});
try {
userDb.save({foo:1});
T(false && "Can't get here. Should have thrown an error");
} catch (e) {
T(e.error == "forbidden");
- T(e.http_status == 403);
+ T(userDb.last_req.status == 403);
}
userDb.save({_id:"testdoc", foo:1, author:"test user"});
@@ -2019,7 +2033,7 @@ var tests = {
doc.foo=2;
userDb.save(doc);
- var user2Db = new CouchDB("test_suite_db", {username:"test user2"});
+ var user2Db = new CouchDB("test_suite_db", {"X-Couch-Username":"test user2"});
var doc = user2Db.open("testdoc");
doc.foo=3;
@@ -2028,7 +2042,8 @@ var tests = {
T(false && "Can't get here. Should have thrown an error 2");
} catch (e) {
T(e.error == "unauthorized");
- T(e.http_status == 401);
+ T(user2Db.last_req.status == 401);
+ T(user2Db.last_req.getResponseHeader("X-Couch-Foo") == "bar");
}