summaryrefslogtreecommitdiff
path: root/share/www/script/couch_tests.js
diff options
context:
space:
mode:
authorDamien F. Katz <damien@apache.org>2008-11-20 04:42:43 +0000
committerDamien F. Katz <damien@apache.org>2008-11-20 04:42:43 +0000
commit2c260766864a56e10aa45c3b1782f640b21a0bac (patch)
treeba41373450b909079755103172fb14a7ed7944c6 /share/www/script/couch_tests.js
parent8ec0f5d5407ccd9a7cee0fc579ad08d8f4be5bd7 (diff)
Nearly completed security/validation work. Still needs replication testing.
git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@719160 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'share/www/script/couch_tests.js')
-rw-r--r--share/www/script/couch_tests.js223
1 files changed, 154 insertions, 69 deletions
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index 722d3113..36cf2c25 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -130,7 +130,7 @@ var tests = {
T(db.open(existingDoc._id, {rev: existingDoc._rev}) != null);
// make sure restart works
- T(restartServer().ok);
+ restartServer();
},
all_docs: function(debug) {
var db = new CouchDB("test_suite_db");
@@ -1845,6 +1845,7 @@ var tests = {
for(var i in docs) {
db.deleteDoc(docs[i]);
}
+ db.setAdmins(["Foo bar"]);
var deletesize = db.info().disk_size;
T(deletesize > originalsize);
@@ -1859,6 +1860,7 @@ var tests = {
T(xhr.getResponseHeader("Content-Type") == "text/plain")
T(db.info().doc_count == 1);
T(db.info().disk_size < deletesize);
+
},
purge: function(debug) {
@@ -1963,7 +1965,7 @@ var tests = {
// test that settings can be altered
xhr = CouchDB.request("PUT", "/_config/test/foo",{
- body : "bar"
+ body : JSON.stringify("bar")
});
T(xhr.status == 200);
xhr = CouchDB.request("GET", "/_config/test");
@@ -1975,78 +1977,139 @@ var tests = {
T(xhr.responseText == '"bar"');
},
- security : function(debug) {
+ security_validation : function(debug) {
+ // This tests couchdb's security and validation features. This does
+ // not test authentication, except to use test authentication code made
+ // specifically for this testing. It is a WWWW-Authenticate scheme named
+ // X-Couch-Test-Auth, and the user names and passwords are hard coded
+ // on the server-side.
+ //
+ // We could have used Basic authentication, however the XMLHttpRequest
+ // implementation for Firefox and Safari, and probably other browsers are
+ // broken (Firefox always prompts the user on 401 failures, Safari gives
+ // odd security errors when using different name/passwords, perhaps due
+ // to cross site scripting prevention). These problems essentially make Basic
+ // authentication testing in the browser impossible. But while hard to
+ // test automated in the browser, Basic auth may still useful for real
+ // world use where these bugs/behaviors don't matter.
+ //
+ // So for testing purposes we are using this custom X-Couch-Test-Auth.
+ // It's identical to Basic auth, except it doesn't even base64 encode
+ // the "username:password" string, it's sent completely plain text.
+ // Firefox and Safari both deal with this correctly (which is to say
+ // they correctly do nothing special).
+
+
var db = new CouchDB("test_suite_db");
db.deleteDb();
db.createDb();
if (debug) debugger;
-
- var designDoc = {
- _id:"_design/test",
- language: "javascript",
- validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) {
- // docs should have an author field.
- if (!newDoc.author) {
- throw {error:"forbidden",
- reason:"Documents must have an author field",
- http_status:403};
- }
-
- // 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:
- {"X-Couch-Foo": "bar"},
- http_status:401};
- }
- }).toString() + ")"
- }
-
- db.save(designDoc);
-
- 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(userDb.last_req.status == 403);
- }
-
- userDb.save({_id:"testdoc", foo:1, author:"test user"});
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "authentication_handler",
+ value: "{couch_httpd, special_test_authentication_handler}"},
+ {section:"httpd",
+ key: "WWW-Authenticate",
+ value: "X-Couch-Test-Auth"}],
+
+ function () {
- var doc = userDb.open("testdoc");
- doc.foo=2;
- userDb.save(doc);
+ // try saving document usin the wrong credentials
+ var wrongPasswordDb = new CouchDB("test_suite_db",
+ {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
+ );
- var user2Db = new CouchDB("test_suite_db", {"X-Couch-Username":"test user2"});
+ try {
+ wrongPasswordDb.save({foo:1,author:"Damien Katz"});
+ T(false && "Can't get here. Should have thrown an error 1");
+ } catch (e) {
+ T(e.error == "unauthorized");
+ T(wrongPasswordDb.last_req.status == 401);
+ }
+
+
+ // Create the design doc that will run custom validation code
+ var designDoc = {
+ _id:"_design/test",
+ language: "javascript",
+ validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) {
+ log("newDoc: " + newDoc.toSource());
+ if (oldDoc) {
+ log("oldDoc: " + oldDoc.toSource());
+ }
+ // docs should have an author field.
+ if (!newDoc.author) {
+ throw {forbidden:
+ "Documents must have an author field"};
+ }
+ if (oldDoc && oldDoc.author != userCtx.name) {
+ throw {unauthorized:
+ "You are not the author of this document. You jerk."};
+ }
+ }).toString() + ")"
+ }
+
+ // Save a document normally
+ var userDb = new CouchDB("test_suite_db",
+ {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
+ );
+
+ T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
+
+ // Attempt to save the design as a non-admin
+ try {
+ userDb.save(designDoc);
+ T(false && "Can't get here. Should have thrown an error on design doc");
+ } catch (e) {
+ T(e.error == "unauthorized");
+ T(userDb.last_req.status == 401);
+ }
+
+ // add user as admin
+ db.setAdmins(["Damien Katz"]);
+
+ T(userDb.save(designDoc).ok);
- var doc = user2Db.open("testdoc");
- doc.foo=3;
- try {
- user2Db.save(doc);
- T(false && "Can't get here. Should have thrown an error 2");
- } catch (e) {
- T(e.error == "unauthorized");
- T(user2Db.last_req.status == 401);
- T(user2Db.last_req.getResponseHeader("X-Couch-Foo") == "bar");
- }
+ // update the document
+ var doc = userDb.open("testdoc");
+ doc.foo=2;
+ T(userDb.save(doc).ok);
+
+ // Save a document that's missing an author field.
+ try {
+ userDb.save({foo:1});
+ T(false && "Can't get here. Should have thrown an error 2");
+ } catch (e) {
+ T(e.error == "forbidden");
+ T(userDb.last_req.status == 403);
+ }
+ // Now attempt to update the document as a different user, Jan
+ var user2Db = new CouchDB("test_suite_db",
+ {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
+ );
+ var doc = user2Db.open("testdoc");
+ doc.foo=3;
+ try {
+ user2Db.save(doc);
+ T(false && "Can't get here. Should have thrown an error 3");
+ } catch (e) {
+ T(e.error == "unauthorized");
+ T(user2Db.last_req.status == 401);
+ }
+
+ // Now have Damien change the author to Jan
+ doc = userDb.open("testdoc");
+ doc.author="Jan Lehnardt";
+ T(userDb.save(doc).ok);
+
+ // Now update the document as Jan
+ doc = user2Db.open("testdoc");
+ doc.foo = 3;
+ T(user2Db.save(doc).ok);
+ });
}
};
@@ -2067,10 +2130,32 @@ function makeDocs(start, end, templateDoc) {
return docs;
}
+function run_on_modified_server(settings, fun) {
+ try {
+ // set the settings
+ for(var i=0; i < settings.length; i++) {
+ var s = settings[i];
+ var xhr = CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, {
+ body: JSON.stringify(s.value),
+ headers: {"X-Couch-Persist": "false"}
+ });
+ CouchDB.maybeThrowError(xhr);
+ s.oldValue = xhr.responseText;
+ }
+ // run the thing
+ fun();
+ } finally {
+ // unset the settings
+ for(var j=0; j < i; j++) {
+ var s = settings[j];
+ CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, {
+ body: s.oldValue,
+ headers: {"X-Couch-Persist": "false"}
+ });
+ }
+ }
+}
+
function restartServer() {
- var reply = CouchDB.request("POST", "/_restart");
- do {
- var xhr = CouchDB.request("GET", "/");
- } while(xhr.status != 200);
- return JSON.parse(reply.responseText);
+ CouchDB.request("POST", "/_restart");
}