summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsebastianro <sebastianro@apache.org>2016-04-15 12:37:47 +0200
committerJan Lehnardt <jan@apache.org>2016-04-23 12:26:44 +0200
commit17bbf26199aa9d62bee572d42007a1bf50bb9c10 (patch)
treea3b9a2eb010a883b40f8e4a033d95028b3e2708d
parent82d2eb1209f506be7034bdf0ef2644068e0f389d (diff)
downloadcouchdb-17bbf26199aa9d62bee572d42007a1bf50bb9c10.tar.gz
Bring security validation back
-rw-r--r--test/javascript/tests/security_validation.js262
1 files changed, 123 insertions, 139 deletions
diff --git a/test/javascript/tests/security_validation.js b/test/javascript/tests/security_validation.js
index 619ba341d..5c50ff50e 100644
--- a/test/javascript/tests/security_validation.js
+++ b/test/javascript/tests/security_validation.js
@@ -11,51 +11,44 @@
// the License.
couchTests.security_validation = function(debug) {
- return console.log('TODO: config not available on cluster');
-
- // 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 WWW-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_name = get_random_db_name();
var db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"});
db.createDb();
+ var authDb_name = get_random_db_name() + "_authdb";
+ var authDb = new CouchDB(authDb_name, {"X-Couch-Full-Commit":"false"});
+ authDb.createDb();
+ var adminDbA, adminDbB; // used later
if (debug) debugger;
run_on_modified_server(
[{section: "httpd",
key: "authentication_handlers",
- value: "{couch_httpd_auth, special_test_authentication_handler}"},
- {section:"httpd",
- key: "WWW-Authenticate",
- value: "X-Couch-Test-Auth"}],
+ value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
+ {section: "couch_httpd_auth",
+ key: "authentication_db", value: authDb_name},
+ {section: "chttpd_auth",
+ key: "authentication_db", value: authDb_name}],
function () {
+ // the special case handler does not exist (any longer) in clusters, so we have
+ // to replicate the behavior using a "normal" DB even though tests might no more
+ // run universally (why the "X-Couch-Test-Auth" header was introduced).
+ // btw: this needs to be INSIDE configured server to propagate correctly ;-)
+ // At least they'd run in the build, though
+ T(authDb.save(CouchDB.prepareUserDoc({name: "tom"}, "cat")).ok); // Basic dG9tOmNhdA==
+ T(authDb.save(CouchDB.prepareUserDoc({name: "jerry"}, "mouse")).ok); // Basic amVycnk6bW91c2U=
+ T(authDb.save(CouchDB.prepareUserDoc({name: "spike"}, "dog")).ok); // Basic c3Bpa2U6ZG9n
+ authDb.ensureFullCommit();
+
// try saving document using the wrong credentials
var wrongPasswordDb = new CouchDB(db_name + "",
- {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
+ {"Authorization": "Basic c3Bpa2U6Y2F0"} // spike:cat - which is wrong
);
try {
wrongPasswordDb.save({foo:1,author:"Damien Katz"});
- T(false && "Can't get here. Should have thrown an error 1");
+ T(false, "Can't get here. Should have thrown an error 1");
} catch (e) {
T(e.error == "unauthorized");
T(wrongPasswordDb.last_req.status == 401);
@@ -85,36 +78,41 @@ couchTests.security_validation = function(debug) {
}
if (oldDoc && oldDoc.author != userCtx.name) {
throw {unauthorized:
- "You are not the author of this document. You jerk."};
+ "You are '" + userCtx.name + "', not the author '" + oldDoc.author + "' of this document. You jerk."};
}
})
}
// Save a document normally
var userDb = new CouchDB("" + db_name + "",
- {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
+ {"Authorization": "Basic amVycnk6bW91c2U="} // jerry
);
+ // test session
+ TEquals("jerry", JSON.parse(userDb.request("GET", "/_session").responseText).userCtx.name);
- T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
+ T(userDb.save({_id:"testdoc", foo:1, author:"jerry"}).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);
+ // cluster changes from 401 unauthorized to 403 forbidden
+ TEquals("forbidden", e.error);
+ TEquals(403, userDb.last_req.status);
}
// set user as the admin
T(db.setSecObj({
- admins : {names : ["Damien Katz"]}
+ admins : {names : ["jerry"]}
}).ok);
- T(userDb.save(designDoc).ok);
+ // TODO: when _security is correctly honored (COUCHDB-2990), switch back
+ //T(userDb.save(designDoc).ok);
+ T(db.save(designDoc).ok);
var user2Db = new CouchDB("" + db_name + "",
- {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
+ {"Authorization": "Basic dG9tOmNhdA=="} // tom
);
// Attempt to save the design as a non-admin (in replication scenario)
designDoc.foo = "bar";
@@ -123,14 +121,15 @@ couchTests.security_validation = function(debug) {
user2Db.save(designDoc, {new_edits : false});
T(false && "Can't get here. Should have thrown an error on design doc");
} catch (e) {
- T(e.error == "unauthorized");
- T(user2Db.last_req.status == 401);
+ // cluster changes from 401 unauthorized to 403 forbidden
+ TEquals("forbidden", e.error);
+ TEquals(403, userDb.last_req.status);
}
// test the _session API
var resp = userDb.request("GET", "/_session");
var user = JSON.parse(resp.responseText).userCtx;
- T(user.name == "Damien Katz");
+ T(user.name == "jerry");
// test that the roles are listed properly
TEquals(user.roles, []);
@@ -149,11 +148,11 @@ couchTests.security_validation = function(debug) {
T(e.error == "forbidden");
T(userDb.last_req.status == 403);
}
- // compact.
- T(db.compact().ok);
+ // compact. - no more available on clusters (but: test is still valid w/out compaction)
+ /*T(db.compact().ok);
T(db.last_req.status == 202);
// compaction isn't instantaneous, loop until done
- while (db.info().compact_running) {};
+ while (db.info().compact_running) {};*/
}
// Now attempt to update the document as a different user, Jan
@@ -167,17 +166,17 @@ couchTests.security_validation = function(debug) {
T(user2Db.last_req.status == 401);
}
- // Now have Damien change the author to Jan
+ // Now have jerry change the author to tom
doc = userDb.open("testdoc");
- doc.author="Jan Lehnardt";
+ doc.author="tom";
T(userDb.save(doc).ok);
- // Now update the document as Jan
+ // Now update the document as tom
doc = user2Db.open("testdoc");
doc.foo = 3;
T(user2Db.save(doc).ok);
- // Damien can't delete it
+ // jerry can't delete it
try {
userDb.deleteDoc(doc);
T(false && "Can't get here. Should have thrown an error 4");
@@ -206,7 +205,8 @@ couchTests.security_validation = function(debug) {
// now turn on admin override
T(db.setDbProperty("_security", {admin_override : true}).ok);
- T(db.save(doc).ok);
+ // TODO: re-include after COUCHDB-2990
+ //T(db.save(doc).ok);
// try to do something lame
try {
@@ -221,7 +221,7 @@ couchTests.security_validation = function(debug) {
T(user2Db.deleteDoc(doc).ok);
// now test bulk docs
- var docs = [{_id:"bahbah",author:"Damien Katz",foo:"bar"},{_id:"fahfah",foo:"baz"}];
+ var docs = [{_id:"bahbah",author:"jerry",foo:"bar"},{_id:"fahfah",foo:"baz"}];
// Create the docs
var results = db.bulkSave(docs);
@@ -235,8 +235,8 @@ couchTests.security_validation = function(debug) {
T(db.open("fahfah") == null);
- // now all or nothing with a failure
- var docs = [{_id:"booboo",author:"Damien Katz",foo:"bar"},{_id:"foofoo",foo:"baz"}];
+ // now all or nothing with a failure - no more available on cluster
+/* var docs = [{_id:"booboo",author:"Damien Katz",foo:"bar"},{_id:"foofoo",foo:"baz"}];
// Create the docs
var results = db.bulkSave(docs, {all_or_nothing:true});
@@ -245,99 +245,83 @@ couchTests.security_validation = function(debug) {
T(results.errors[0].error == "forbidden");
T(db.open("booboo") == null);
T(db.open("foofoo") == null);
+*/
// Now test replication
- var AuthHeaders = {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"};
- var host = CouchDB.host;
- var dbPairs = [
- {source:"" + db_name + "_a",
- target:"" + db_name + "_b"},
-
- {source:"" + db_name + "_a",
- target:{url: CouchDB.protocol + host + "/" + db_name + "_b",
- headers: AuthHeaders}},
-
- {source:{url:CouchDB.protocol + host + "/" + db_name + "_a",
- headers: AuthHeaders},
- target:"" + db_name + "_b"},
-
- {source:{url:CouchDB.protocol + host + "/" + db_name + "_a",
- headers: AuthHeaders},
- target:{url:CouchDB.protocol + host + "/" + db_name + "_b",
- headers: AuthHeaders}},
- ]
- var adminDbA = new CouchDB("" + db_name + "_a", {"X-Couch-Full-Commit":"false"});
- var adminDbB = new CouchDB("" + db_name + "_b", {"X-Couch-Full-Commit":"false"});
- var dbA = new CouchDB("" + db_name + "_a",
- {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
- var dbB = new CouchDB("" + db_name + "_b",
- {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
- var xhr;
- for (var testPair = 0; testPair < dbPairs.length; testPair++) {
- var A = dbPairs[testPair].source
- var B = dbPairs[testPair].target
-
- adminDbA.deleteDb();
- adminDbA.createDb();
- adminDbB.deleteDb();
- adminDbB.createDb();
-
- // save and replicate a documents that will and will not pass our design
- // doc validation function.
- dbA.save({_id:"foo1",value:"a",author:"Noah Slater"});
- dbA.save({_id:"foo2",value:"a",author:"Christopher Lenz"});
- dbA.save({_id:"bad1",value:"a"});
-
- T(CouchDB.replicate(A, B, {headers:AuthHeaders}).ok);
- T(CouchDB.replicate(B, A, {headers:AuthHeaders}).ok);
-
- T(dbA.open("foo1"));
- T(dbB.open("foo1"));
- T(dbA.open("foo2"));
- T(dbB.open("foo2"));
-
- // save the design doc to dbA
- delete designDoc._rev; // clear rev from previous saves
- adminDbA.save(designDoc);
-
- // no affect on already saved docs
- T(dbA.open("bad1"));
-
- // Update some docs on dbB. Since the design hasn't replicated, anything
- // is allowed.
-
- // this edit will fail validation on replication to dbA (no author)
- T(dbB.save({_id:"bad2",value:"a"}).ok);
-
- // this edit will fail security on replication to dbA (wrong author
- // replicating the change)
- var foo1 = dbB.open("foo1");
- foo1.value = "b";
- dbB.save(foo1);
-
- // this is a legal edit
- var foo2 = dbB.open("foo2");
- foo2.value = "b";
- dbB.save(foo2);
-
- var results = CouchDB.replicate(B, A, {headers:AuthHeaders});
-
- T(results.ok);
-
- T(results.history[0].docs_written == 1);
- T(results.history[0].doc_write_failures == 2);
-
- // bad2 should not be on dbA
- T(dbA.open("bad2") == null);
-
- // The edit to foo1 should not have replicated.
- T(dbA.open("foo1").value == "a");
-
- // The edit to foo2 should have replicated.
- T(dbA.open("foo2").value == "b");
- }
+ var AuthHeaders = {"Authorization": "Basic c3Bpa2U6ZG9n"}; // spike
+ adminDbA = new CouchDB("" + db_name + "_a", {"X-Couch-Full-Commit":"false"});
+ adminDbB = new CouchDB("" + db_name + "_b", {"X-Couch-Full-Commit":"false"});
+ var dbA = new CouchDB("" + db_name + "_a", AuthHeaders);
+ var dbB = new CouchDB("" + db_name + "_b", AuthHeaders);
+ // looping does not really add value as the scenario is the same anyway (there's nothing 2 be gained from it)
+ var A = CouchDB.protocol + CouchDB.host + "/" + db_name + "_a";
+ var B = CouchDB.protocol + CouchDB.host + "/" + db_name + "_b";
+
+ adminDbA.deleteDb();
+ adminDbA.createDb();
+ adminDbB.deleteDb();
+ adminDbB.createDb();
+
+ // save and replicate a documents that will and will not pass our design
+ // doc validation function.
+ T(dbA.save({_id:"foo1",value:"a",author:"tom"}).ok);
+ T(dbA.save({_id:"foo2",value:"a",author:"spike"}).ok);
+ T(dbA.save({_id:"bad1",value:"a"}).ok);
+
+ T(CouchDB.replicate(A, B, {headers:AuthHeaders}).ok);
+ T(CouchDB.replicate(B, A, {headers:AuthHeaders}).ok);
+
+ T(dbA.open("foo1"));
+ T(dbB.open("foo1"));
+ T(dbA.open("foo2"));
+ T(dbB.open("foo2"));
+
+ // save the design doc to dbA
+ delete designDoc._rev; // clear rev from previous saves
+ T(adminDbA.save(designDoc).ok);
+
+ // no affect on already saved docs
+ T(dbA.open("bad1"));
+
+ // Update some docs on dbB. Since the design hasn't replicated, anything
+ // is allowed.
+
+ // this edit will fail validation on replication to dbA (no author)
+ T(dbB.save({_id:"bad2",value:"a"}).ok);
+
+ // this edit will fail security on replication to dbA (wrong author
+ // replicating the change)
+ var foo1 = dbB.open("foo1");
+ foo1.value = "b";
+ T(dbB.save(foo1).ok);
+
+ // this is a legal edit
+ var foo2 = dbB.open("foo2");
+ foo2.value = "b";
+ T(dbB.save(foo2).ok);
+
+ var results = CouchDB.replicate({"url": B, "headers": AuthHeaders}, {"url": A, "headers": AuthHeaders}, {headers:AuthHeaders});
+ T(results.ok);
+ TEquals(1, results.history[0].docs_written);
+ TEquals(2, results.history[0].doc_write_failures);
+
+ // bad2 should not be on dbA
+ T(dbA.open("bad2") == null);
+
+ // The edit to foo1 should not have replicated.
+ T(dbA.open("foo1").value == "a");
+
+ // The edit to foo2 should have replicated.
+ T(dbA.open("foo2").value == "b");
});
// cleanup
db.deleteDb();
+ if(adminDbA){
+ adminDbA.deleteDb();
+ }
+ if(adminDbB){
+ adminDbB.deleteDb();
+ }
+ authDb.deleteDb();
};