You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2012/01/03 20:33:21 UTC
[4/11] git commit: JavaScript tests for System Database Security
JavaScript tests for System Database Security
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/554959ca
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/554959ca
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/554959ca
Branch: refs/heads/1.2.x
Commit: 554959cad656b8a909f3e499565b2a3dc926f2b0
Parents: 1386bfe
Author: Jan Lehnardt <ja...@apache.org>
Authored: Thu Dec 22 15:52:56 2011 +0100
Committer: Jan Lehnardt <ja...@apache.org>
Committed: Tue Jan 3 19:23:59 2012 +0100
----------------------------------------------------------------------
share/Makefile.am | 2 +
share/www/script/couch.js | 12 +-
share/www/script/couch_test_runner.js | 5 +
share/www/script/couch_tests.js | 2 +
share/www/script/test/cookie_auth.js | 82 +++--
share/www/script/test/replicator_db_security.js | 395 ++++++++++++++++++
share/www/script/test/users_db_security.js | 187 ++++++++-
7 files changed, 647 insertions(+), 38 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/Makefile.am
----------------------------------------------------------------------
diff --git a/share/Makefile.am b/share/Makefile.am
index 6ea910e..2ef679a 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -174,6 +174,7 @@ nobase_dist_localdata_DATA = \
www/script/test/reduce_false_temp.js \
www/script/test/replication.js \
www/script/test/replicator_db.js \
+ www/script/test/replicator_db_security.js \
www/script/test/rev_stemming.js \
www/script/test/rewrite.js \
www/script/test/security_validation.js \
@@ -181,6 +182,7 @@ nobase_dist_localdata_DATA = \
www/script/test/stats.js \
www/script/test/update_documents.js \
www/script/test/users_db.js \
+ www/script/test/users_db_security.js \
www/script/test/utf8.js \
www/script/test/uuids.js \
www/script/test/view_collation.js \
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/www/script/couch.js
----------------------------------------------------------------------
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index 75cae8e..2ee5cbf 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -45,14 +45,14 @@ function CouchDB(name, httpHeaders) {
};
// Save a document to the database
- this.save = function(doc, options) {
+ this.save = function(doc, options, http_headers) {
if (doc._id == undefined) {
doc._id = CouchDB.newUuids(1)[0];
}
-
+ http_headers = http_headers || {};
this.last_req = this.request("PUT", this.uri +
encodeURIComponent(doc._id) + encodeOptions(options),
- {body: JSON.stringify(doc)});
+ {body: JSON.stringify(doc), headers: http_headers});
CouchDB.maybeThrowError(this.last_req);
var result = JSON.parse(this.last_req.responseText);
doc._rev = result.rev;
@@ -60,9 +60,9 @@ function CouchDB(name, httpHeaders) {
};
// Open a document from the database
- this.open = function(docId, options) {
+ this.open = function(docId, url_params, http_headers) {
this.last_req = this.request("GET", this.uri + encodeURIComponent(docId)
- + encodeOptions(options));
+ + encodeOptions(url_params), {headers:http_headers});
if (this.last_req.status == 404) {
return null;
}
@@ -218,7 +218,7 @@ function CouchDB(name, httpHeaders) {
};
this.changes = function(options) {
- this.last_req = this.request("GET", this.uri + "_changes"
+ this.last_req = this.request("GET", this.uri + "_changes"
+ encodeOptions(options));
CouchDB.maybeThrowError(this.last_req);
return JSON.parse(this.last_req.responseText);
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/www/script/couch_test_runner.js
----------------------------------------------------------------------
diff --git a/share/www/script/couch_test_runner.js b/share/www/script/couch_test_runner.js
index 61823c0..db0b8de 100644
--- a/share/www/script/couch_test_runner.js
+++ b/share/www/script/couch_test_runner.js
@@ -313,6 +313,11 @@ function T(arg1, arg2, testName) {
}
}
+function TIsnull(actual, testName) {
+ T(actual === null, "expected 'null', got '"
+ + repr(actual) + "'", testName);
+}
+
function TEquals(expected, actual, testName) {
T(equals(expected, actual), "expected '" + repr(expected) +
"', got '" + repr(actual) + "'", testName);
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/www/script/couch_tests.js
----------------------------------------------------------------------
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index c890f68..a6d4b56 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -68,6 +68,7 @@ loadTest("reduce_false.js");
loadTest("reduce_false_temp.js");
loadTest("replication.js");
loadTest("replicator_db.js");
+loadTest("replicator_db_security.js");
loadTest("rev_stemming.js");
loadTest("rewrite.js");
loadTest("security_validation.js");
@@ -75,6 +76,7 @@ loadTest("show_documents.js");
loadTest("stats.js");
loadTest("update_documents.js");
loadTest("users_db.js");
+loadTest("users_db_security.js");
loadTest("utf8.js");
loadTest("uuids.js");
loadTest("view_collation.js");
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/www/script/test/cookie_auth.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/cookie_auth.js b/share/www/script/test/cookie_auth.js
index 180d42e..59459d5 100644
--- a/share/www/script/test/cookie_auth.js
+++ b/share/www/script/test/cookie_auth.js
@@ -18,6 +18,41 @@ couchTests.cookie_auth = function(debug) {
db.createDb();
if (debug) debugger;
+ var password = "3.141592653589";
+
+ var loginUser = function(username) {
+ var pws = {
+ jan: "apple",
+ "Jason Davies": password,
+ jchris: "funnybone"
+ };
+ var username1 = username.replace(/[0-9]$/, "");
+ var password = pws[username];
+ //console.log("Logging in '" + username1 + "' with password '" + password + "'");
+ T(CouchDB.login(username1, pws[username]).ok);
+ };
+
+ var open_as = function(db, docId, username) {
+ loginUser(username);
+ try {
+ return db.open(docId, {"anti-cache": Math.round(Math.random() * 100000)});
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ var save_as = function(db, doc, username)
+ {
+ loginUser(username);
+ try {
+ return db.save(doc);
+ } catch (ex) {
+ return ex;
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
// Simple secret key generator
function generateSecret(length) {
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -31,27 +66,20 @@ couchTests.cookie_auth = function(debug) {
// this function will be called on the modified server
var testFun = function () {
try {
- // try using an invalid cookie
- var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
- usersDb.deleteDb();
- usersDb.createDb();
// test that the users db is born with the auth ddoc
- var ddoc = usersDb.open("_design/_auth");
+ var ddoc = open_as(usersDb, "_design/_auth", "jan");
T(ddoc.validate_doc_update);
// TODO test that changing the config so an existing db becomes the users db installs the ddoc also
- var password = "3.141592653589";
-
// Create a user
var jasonUserDoc = CouchDB.prepareUserDoc({
- name: "Jason Davies",
- roles: ["dev"]
+ name: "Jason Davies"
}, password);
T(usersDb.save(jasonUserDoc).ok);
- var checkDoc = usersDb.open(jasonUserDoc._id);
+ var checkDoc = open_as(usersDb, jasonUserDoc._id, "jan");
T(checkDoc.name == "Jason Davies");
var jchrisUserDoc = CouchDB.prepareUserDoc({
@@ -182,21 +210,16 @@ couchTests.cookie_auth = function(debug) {
}
T(CouchDB.logout().ok);
- T(CouchDB.session().userCtx.roles[0] == "_admin");
jchrisUserDoc.foo = ["foo"];
- T(usersDb.save(jchrisUserDoc).ok);
+ T(save_as(usersDb, jchrisUserDoc, "jan"));
// test that you can't save system (underscore) roles even if you are admin
jchrisUserDoc.roles = ["_bar"];
- try {
- usersDb.save(jchrisUserDoc);
- T(false && "Can't add system roles to user's db. Should have thrown an error.");
- } catch (e) {
- T(e.error == "forbidden");
- T(usersDb.last_req.status == 403);
- }
+ var res = save_as(usersDb, jchrisUserDoc, "jan");
+ T(res.error == "forbidden");
+ T(usersDb.last_req.status == 403);
// make sure the foo role has been applied
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
@@ -206,11 +229,11 @@ couchTests.cookie_auth = function(debug) {
// now let's make jchris a server admin
T(CouchDB.logout().ok);
- T(CouchDB.session().userCtx.roles[0] == "_admin");
- T(CouchDB.session().userCtx.name == null);
// set the -hashed- password so the salt matches
// todo ask on the ML about this
+
+ TEquals(true, CouchDB.login("jan", "apple").ok);
run_on_modified_server([{section: "admins",
key: "jchris@apache.org", value: "funnybone"}], function() {
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
@@ -240,16 +263,21 @@ couchTests.cookie_auth = function(debug) {
// Make sure we erase any auth cookies so we don't affect other tests
T(CouchDB.logout().ok);
}
+ // log in one last time so run_on_modified_server can clean up the admin account
+ TEquals(true, CouchDB.login("jan", "apple").ok);
};
+ var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
+ usersDb.deleteDb();
+ usersDb.createDb();
+
run_on_modified_server(
- [{section: "httpd",
- key: "authentication_handlers",
- value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
- {section: "couch_httpd_auth",
- key: "secret", value: generateSecret(64)},
+ [
{section: "couch_httpd_auth",
- key: "authentication_db", value: "test_suite_users"}],
+ key: "authentication_db", value: "test_suite_users"},
+ {section: "admins",
+ key: "jan", value: "apple"}
+ ],
testFun
);
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/www/script/test/replicator_db_security.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_security.js b/share/www/script/test/replicator_db_security.js
new file mode 100644
index 0000000..67390cf
--- /dev/null
+++ b/share/www/script/test/replicator_db_security.js
@@ -0,0 +1,395 @@
+// 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.
+
+couchTests.replicator_db_security = function(debug) {
+ var dbs = ["couch_test_rep_db", "couch_test_users_db",
+ "test_suite_db_a", "test_suite_db_b", "test_suite_db_c"]
+ .map(function(db_name) {
+ var db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ return db;
+ });
+
+ var repDb = dbs[0];
+ var usersDb = dbs[1];
+ var dbA = dbs[2];
+ var dbB = dbs[3];
+ var dbC = dbs[4];
+
+ if (debug) debugger;
+
+ var loginUser = function(username) {
+ var pws = {
+ jan: "apple",
+ jchris: "mp3",
+ fdmanana: "foobar",
+ benoitc: "test"
+ };
+ T(CouchDB.login(username, pws[username]).ok);
+ };
+
+ var repChanges = function(username) {
+ var pws = {
+ jan: "apple",
+ jchris: "mp3",
+ fdmanana: "foobar",
+ benoitc: "test"
+ };
+ T(CouchDB.login(username, pws[username]).ok);
+ var changes = CouchDB.request(
+ "GET",
+ "/" + repDb.name + "/_changes?include_docs=true" +
+ "&anti-cache=" + String(Math.round(Math.random() * 100000)));
+ return changes = JSON.parse(changes.responseText);
+ };
+
+ var save_as = function(db, doc, username)
+ {
+ loginUser(username);
+ try {
+ return db.save(doc);
+ } catch (ex) {
+ return ex;
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ var open_as = function(db, docId, username) {
+ loginUser(username);
+ try {
+ return db.open(docId);
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ // from test replicator_db.js
+ function waitForDocPos(db, docId, pos) {
+ var doc, curPos, t0, t1,
+ maxWait = 3000;
+
+ doc = db.open(docId);
+ curPos = Number(doc._rev.split("-", 1));
+ t0 = t1 = new Date();
+
+ while ((curPos < pos) && ((t1 - t0) <= maxWait)) {
+ doc = db.open(docId);
+ curPos = Number(doc._rev.split("-", 1));
+ t1 = new Date();
+ }
+
+ return doc;
+ }
+
+ var testFun = function()
+ {
+ // _replicator db
+ // in admin party mode, anonymous should be able to create a replication
+ var repDoc = {
+ _id: "null-owner-rep",
+ source: dbA.name,
+ target: dbB.name
+ };
+ var result = repDb.save(repDoc);
+ TEquals(true, result.ok, "should allow anonymous replication docs in admin party");
+ // new docs should get an owner field enforced. In admin party mode owner is null
+ repDoc = repDb.open(repDoc._id);
+ TIsnull(repDoc.owner, "owner should be null in admin party");
+
+// Uncomment when _users database security changes are implemented.
+//
+// var jchrisDoc = {
+// _id: "org.couchdb.user:jchris",
+// type: "user",
+// name: "jchris",
+// password: "mp3",
+// roles: []
+// };
+ var jchrisDoc = CouchDB.prepareUserDoc({
+ name: "jchris",
+ roles: []
+ }, "mp3");
+ usersDb.save(jchrisDoc); // set up a non-admin user
+
+// Uncomment when _users database security changes are implemented.
+//
+// var jchrisDoc = {
+// _id: "org.couchdb.user:fdmanana",
+// type: "user",
+// name: "fdmanana",
+// password: "foobar",
+// roles: []
+// };
+ var fdmananaDoc = CouchDB.prepareUserDoc({
+ name: "fdmanana",
+ roles: []
+ }, "foobar");
+ usersDb.save(fdmananaDoc); // set up a non-admin user
+
+// Uncomment when _users database security changes are implemented.
+//
+// var benoitcDoc = {
+// _id: "org.couchdb.user:fdmanana",
+// type: "user",
+// name: "fdmanana",
+// password: "foobar",
+// roles: []
+// };
+ var benoitcDoc = CouchDB.prepareUserDoc({
+ name: "benoitc",
+ roles: []
+ }, "test");
+ usersDb.save(benoitcDoc); // set up a non-admin user
+
+ T(repDb.setSecObj({
+ "admins" : {
+ roles : [],
+ names : ["benoitc"]
+ }
+ }).ok);
+
+ run_on_modified_server([
+ {
+ section: "admins",
+ key: "jan",
+ value: "apple"
+ }
+ ], function() {
+ // replication docs from admin-party mode in non-admin party mode can not
+ // be edited by non-admins (non-server admins)
+ repDoc = repDb.open(repDoc._id);
+ repDoc.target = dbC.name;
+ var result = save_as(repDb, repDoc, "jchris");
+ TEquals("forbidden", result.error, "should forbid editing null-owner docs");
+
+ // replication docs from admin-party mode in non-admin party mode can only
+ // be edited by admins (server admins)
+ repDoc = waitForDocPos(repDb, repDoc._id, 3);
+ repDoc.target = dbC.name;
+ var result = save_as(repDb, repDoc, "jan");
+ repDoc = open_as(repDb, repDoc._id, "jchris");
+ TEquals(true, result.ok, "should allow editing null-owner docs to admins");
+ TEquals("jan", repDoc.owner, "owner should be the admin now");
+
+ // user can update their own replication docs (repDoc.owner)
+ var jchrisRepDoc = {
+ _id: "jchris-rep-doc",
+ source: dbC.name,
+ target: dbA.name,
+ user_ctx: { name: "jchris", roles: [] }
+ };
+
+ var result = save_as(repDb, jchrisRepDoc, "jchris");
+ TEquals(true, result.ok, "should create rep doc");
+ jchrisRepDoc = repDb.open(jchrisRepDoc._id);
+ TEquals("jchris", jchrisRepDoc.owner, "should assign correct owner");
+ jchrisRepDoc = waitForDocPos(repDb, jchrisRepDoc._id, 3);
+ jchrisRepDoc = open_as(repDb, jchrisRepDoc._id, "jchris");
+ jchrisRepDoc.target = dbB.name;
+ var result = save_as(repDb, jchrisRepDoc, "jchris");
+ TEquals(true, result.ok, "should allow update of rep doc");
+
+ // user should not be able to read from any view
+ var ddoc = {
+ _id: "_design/reps",
+ views: {
+ test: {
+ map: "function(doc) {" +
+ "if (doc._replication_state) { " +
+ "emit(doc._id, doc._replication_state);" +
+ "}" +
+ "}"
+ }
+ }
+ };
+
+ save_as(repDb, ddoc, "jan");
+
+ try {
+ repDb.view("reps/test");
+ T(false, "non-admin had view read access");
+ } catch (ex) {
+ TEquals("forbidden", ex.error,
+ "non-admins should not be able to read a view");
+ }
+
+ // admin should be able to read from any view
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ var result = repDb.view("reps/test");
+ CouchDB.logout();
+ TEquals(2, result.total_rows, "should allow access and list two users");
+
+ // test _all_docs, only available for _admins
+ try {
+ repDb.allDocs({include_docs: true});
+ T(false, "non-admin had _all_docs access");
+ } catch (ex) {
+ TEquals("forbidden", ex.error,
+ "non-admins should not be able to access _all_docs");
+ }
+
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ try {
+ repDb.allDocs({include_docs: true});
+ } catch (ex) {
+ T(false, "admin couldn't access _all_docs");
+ }
+ CouchDB.logout();
+
+ try {
+ repDb.view("reps/test");
+ T(false, "non-admin had view read access");
+ } catch (ex) {
+ TEquals("forbidden", ex.error,
+ "non-admins should not be able to read a view");
+ }
+
+ // admin should be able to read from any view
+ TEquals(true, CouchDB.login("benoitc", "test").ok);
+ var result = repDb.view("reps/test");
+ CouchDB.logout();
+ TEquals(2, result.total_rows, "should allow access and list two users");
+
+ // test _all_docs, only available for _admins
+ try {
+ repDb.allDocs({include_docs: true});
+ T(false, "non-admin had _all_docs access");
+ } catch (ex) {
+ TEquals("forbidden", ex.error,
+ "non-admins should not be able to access _all_docs");
+ }
+
+ TEquals(true, CouchDB.login("benoitc", "test").ok);
+ try {
+ repDb.allDocs({include_docs: true});
+ } catch (ex) {
+ T(false, "admin couldn't access _all_docs");
+ }
+ CouchDB.logout();
+
+ // Verify that users can't access credentials in the "source" and
+ // "target" fields of replication documents owned by other users.
+ var fdmananaRepDoc = {
+ _id: "fdmanana-rep-doc",
+ source: "http://fdmanana:foobar@" + CouchDB.host + "/" + dbC.name,
+ target: dbA.name,
+ user_ctx: { name: "fdmanana", roles: [] }
+ };
+
+ var result = save_as(repDb, fdmananaRepDoc, "fdmanana");
+ TEquals(true, result.ok, "should create rep doc");
+ waitForDocPos(repDb, fdmananaRepDoc._id, 3);
+ fdmananaRepDoc = open_as(repDb, fdmananaRepDoc._id, "fdmanana");
+ TEquals("fdmanana", fdmananaRepDoc.owner, "should assign correct owner");
+ TEquals("http://fdmanana:foobar@" + CouchDB.host + "/" + dbC.name,
+ fdmananaRepDoc.source, "source field has credentials");
+
+ fdmananaRepDoc = open_as(repDb, fdmananaRepDoc._id, "jchris");
+ TEquals("fdmanana", fdmananaRepDoc.owner, "should assign correct owner");
+ TEquals("http://" + CouchDB.host + "/" + dbC.name,
+ fdmananaRepDoc.source, "source field doesn't contain credentials");
+
+ // _changes?include_docs=true, users shouldn't be able to see credentials
+ // in documents owned by other users.
+ var changes = repChanges("jchris");
+ var doc = changes.results[changes.results.length - 1].doc;
+ TEquals(fdmananaRepDoc._id, doc._id, "Got the right doc from _changes");
+ TEquals("http://" + CouchDB.host + "/" + dbC.name,
+ doc.source, "source field doesn't contain credentials (doc from _changes)");
+ CouchDB.logout();
+
+ // _changes?include_docs=true, user should be able to see credentials
+ // in documents they own.
+ var changes = repChanges("fdmanana");
+ var doc = changes.results[changes.results.length - 1].doc;
+ TEquals(fdmananaRepDoc._id, doc._id, "Got the right doc from _changes");
+ TEquals("http://fdmanana:foobar@" + CouchDB.host + "/" + dbC.name,
+ doc.source, "source field contains credentials (doc from _changes)");
+ CouchDB.logout();
+
+ // _changes?include_docs=true, admins should be able to see credentials
+ // from all documents.
+ var changes = repChanges("jan");
+ var doc = changes.results[changes.results.length - 1].doc;
+ TEquals(fdmananaRepDoc._id, doc._id, "Got the right doc from _changes");
+ TEquals("http://fdmanana:foobar@" + CouchDB.host + "/" + dbC.name,
+ doc.source, "source field contains credentials (doc from _changes)");
+ CouchDB.logout();
+
+ // _changes?include_docs=true, db admins should be able to see credentials
+ // from all documents.
+ var changes = repChanges("benoitc");
+ var doc = changes.results[changes.results.length - 1].doc;
+ TEquals(fdmananaRepDoc._id, doc._id, "Got the right doc from _changes");
+ TEquals("http://fdmanana:foobar@" + CouchDB.host + "/" + dbC.name,
+ doc.source, "source field contains credentials (doc from _changes)");
+ CouchDB.logout();
+
+ var fdmananaRepDocOAuth = {
+ _id: "fdmanana-rep-doc-oauth",
+ source: dbC.name,
+ target: {
+ url: "http://" + CouchDB.host + "/" + dbA.name,
+ oauth: {
+ token: "abc",
+ token_secret: "foo",
+ consumer_key: "123",
+ consumer_secret: "321"
+ }
+ },
+ user_ctx: { name: "fdmanana", roles: [] }
+ };
+
+ var result = save_as(repDb, fdmananaRepDocOAuth, "fdmanana");
+ TEquals(true, result.ok, "should create rep doc");
+ waitForDocPos(repDb, fdmananaRepDocOAuth._id, 3);
+ fdmananaRepDocOAuth = open_as(repDb, fdmananaRepDocOAuth._id, "fdmanana");
+ TEquals("fdmanana", fdmananaRepDocOAuth.owner, "should assign correct owner");
+ TEquals("object", typeof fdmananaRepDocOAuth.target.oauth,
+ "target field has oauth credentials");
+
+ fdmananaRepDocOAuth = open_as(repDb, fdmananaRepDocOAuth._id, "jchris");
+ TEquals("fdmanana", fdmananaRepDocOAuth.owner, "should assign correct owner");
+ TEquals("undefined", typeof fdmananaRepDocOAuth.target.oauth,
+ "target field doesn't have oauth credentials");
+
+ // ensure "old" replicator docs still work
+ // done in replicator_db.js?
+
+ // Login as admin so run_on_modified_server can do its cleanup.
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ });
+ };
+
+ usersDb.deleteDb();
+ repDb.deleteDb();
+
+ run_on_modified_server([
+ {
+ section: "couch_httpd_auth",
+ key: "authentication_db",
+ value: usersDb.name
+ },
+ {
+ section: "replicator",
+ key: "db",
+ value: repDb.name
+ }],
+ testFun
+ );
+
+ // cleanup
+ usersDb.deleteDb();
+ repDb.deleteDb();
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/554959ca/share/www/script/test/users_db_security.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/users_db_security.js b/share/www/script/test/users_db_security.js
index 8ea93db..811ea7f 100644
--- a/share/www/script/test/users_db_security.js
+++ b/share/www/script/test/users_db_security.js
@@ -14,6 +14,7 @@ couchTests.users_db_security = function(debug) {
var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
if (debug) debugger;
+
var loginUser = function(username) {
var pws = {
jan: "apple",
@@ -61,8 +62,53 @@ couchTests.users_db_security = function(debug) {
var testFun = function()
{
usersDb.deleteDb();
- usersDb.createDb();
+ if (debug) debugger;
+
+ var loginUser = function(username) {
+ var pws = {
+ jan: "apple",
+ jchris: "mp3",
+ jchris1: "couch",
+ fdmanana: "foobar"
+ var username1 = username.replace(/[0-9]$/, "");
+ var password = pws[username];
+ //console.log("Logging in '" + username1 + "' with password '" + password + "'");
+ T(CouchDB.login(username1, pws[username]).ok);
+ };
+
+ var open_as = function(db, docId, username) {
+ loginUser(username);
+ try {
+ return db.open(docId, {"anti-cache": Math.round(Math.random() * 100000)});
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ var view_as = function(db, viewname, username) {
+ loginUser(username);
+ try {
+ return db.view(viewname);
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ var save_as = function(db, doc, username)
+ {
+ loginUser(username);
+ try {
+ return db.save(doc);
+ } catch (ex) {
+ return ex;
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ var testFun = function()
+ {
// _users db
// a doc with a field 'password' should be hashed to 'password_sha'
// with salt and salt stored in 'salt', 'password' is set to null.
@@ -80,6 +126,7 @@ couchTests.users_db_security = function(debug) {
// jan's gonna be admin as he's the first user
TEquals(true, usersDb.save(userDoc).ok, "should save document");
userDoc = usersDb.open("org.couchdb.user:jchris");
+ console.log(userDoc);
TEquals(undefined, userDoc.password, "password field should be null 1");
TEquals(40, userDoc.password_sha.length, "password_sha should exist");
TEquals(32, userDoc.salt.length, "salt should exist");
@@ -140,12 +187,11 @@ couchTests.users_db_security = function(debug) {
type: "user",
name: "benoitc",
password: "test",
- roles: []
+ roles: ["user_admin"]
};
- usersDb.save(benoitcDoc);
+ save_as(usersDb, benoitcDoc, "jan");
TEquals(true, CouchDB.login("jan", "apple").ok);
-
T(usersDb.setSecObj({
"admins" : {
roles : [],
@@ -198,6 +244,132 @@ couchTests.users_db_security = function(debug) {
// db admin should be able to read and edit any user doc
fdmananaDoc.password = "mobile1";
var result = save_as(usersDb, fdmananaDoc, "benoitc");
+ TEquals(true, result.ok, "db admin by role should be able to update any user doc");
+
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ T(usersDb.setSecObj({
+ "admins" : {
+ roles : ["user_admin"],
+ names : []
+ }
+ }).ok);
+ CouchDB.logout();
+
+ // db admin should be able to read and edit any user doc
+ fdmananaDoc.password = "mobile2";
+ var result = save_as(usersDb, fdmananaDoc, "benoitc");
+ TEquals(true, result.ok, "db admin should be able to update any user doc");
+
+ // ensure creation of old-style docs still works
+ var robertDoc = CouchDB.prepareUserDoc({ name: "robert" }, "anchovy");
+ var result = usersDb.save(robertDoc);
+ TEquals(true, result.ok, "old-style user docs should still be accepted");
+
+ // log in one last time so run_on_modified_server can clean up the admin account
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ });
+
+ userDoc = usersDb.open("org.couchdb.user:jchris");
+ TEquals(undefined, userDoc.password, "password field should be null 1");
+ TEquals(40, userDoc.password_sha.length, "password_sha should exist");
+ TEquals(32, userDoc.salt.length, "salt should exist");
+
+ // create server admin
+ run_on_modified_server([
+ {
+ section: "admins",
+ key: "jan",
+ value: "apple"
+ }
+ ], function() {
+
+ // anonymous should not be able to read an existing user's user document
+ var res = usersDb.open("org.couchdb.user:jchris");
+ TEquals(null, res, "anonymous user doc read should be not found");
+
+ // user should be able to read their own document
+
+ var jchrisDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris");
+ TEquals("org.couchdb.user:jchris", jchrisDoc._id);
+
+ // user should bt able to update their own document
+ // new 'password' fields should trigger new hashing routine
+ jchrisDoc.password = "couch";
+
+ TEquals(true, save_as(usersDb, jchrisDoc, "jchris").ok);
+ var jchrisDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris1");
+
+ TEquals(undefined, jchrisDoc.password, "password field should be null 2");
+ TEquals(40, jchrisDoc.password_sha.length, "password_sha should exist");
+ TEquals(32, jchrisDoc.salt.length, "salt should exist");
+
+ TEquals(true, userDoc.salt != jchrisDoc.salt, "should have new salt");
+ TEquals(true, userDoc.password_sha != jchrisDoc.password_sha,
+ "should have new password_sha");
+
+ // user should not be able to read another user's user document
+ var fdmananaDoc = {
+ _id: "org.couchdb.user:fdmanana",
+ type: "user",
+ name: "fdmanana",
+ password: "foobar",
+ roles: []
+ };
+
+ usersDb.save(fdmananaDoc);
+
+ var fdmananaDocAsReadByjchris =
+ open_as(usersDb, "org.couchdb.user:fdmanana", "jchris1");
+ TEquals(null, fdmananaDocAsReadByjchris,
+ "should not_found opening another user's user doc");
+
+ // user should not be able to read from any view
+ var ddoc = {
+ _id: "_design/user_db_auth",
+ views: {
+ test: {
+ map: "function(doc) { emit(doc._id, null); }"
+ }
+ }
+ };
+
+ save_as(usersDb, ddoc, "jan");
+
+ try {
+ usersDb.view("user_db_auth/test");
+ T(false, "user had access to view in admin db");
+ } catch(e) {
+ TEquals("forbidden", e.error,
+ "non-admins should not be able to read a view");
+ }
+
+ // admin should be able to read from any view
+ var result = view_as(usersDb, "user_db_auth/test", "jan");
+ TEquals(3, result.total_rows, "should allow access and list two users");
+
+ // db admin should be able to read from any view
+ var result = view_as(usersDb, "user_db_auth/test", "benoitc");
+ TEquals(3, result.total_rows, "should allow access and list two users to db admin");
+
+
+ // non-admins can't read design docs
+ try {
+ open_as(usersDb, "_design/user_db_auth", "jchris1");
+ T(false, "non-admin read design doc, should not happen");
+ } catch(e) {
+ TEquals("forbidden", e.error, "non-admins can't read design docs");
+ }
+
+ console.log(fdmananaDoc);
+ // admin should be able to read and edit any user doc
+ fdmananaDoc.password = "mobile";
+ var result = save_as(usersDb, fdmananaDoc, "jan");
+ TEquals(true, result.ok, "admin should be able to update any user doc");
+
+ console.log(fdmananaDoc);
+ // admin should be able to read and edit any user doc
+ fdmananaDoc.password = "mobile1";
+ var result = save_as(usersDb, fdmananaDoc, "benoitc");
TEquals(true, result.ok, "db admin should be able to update any user doc");
// ensure creation of old-style docs still works
@@ -205,12 +377,17 @@ couchTests.users_db_security = function(debug) {
var result = usersDb.save(robertDoc);
TEquals(true, result.ok, "old-style user docs should still be accepted");
+ // ensure creation of old-style docs still works
+ var robertDoc = CouchDB.prepareUserDoc({ name: "robert" }, "anchovy");
+ var result = userDb.save(robertDoc);
+ TEquals(true, result.ok, "old-style user docs should still be accepted");
+
// log in one last time so run_on_modified_server can clean up the admin account
TEquals(true, CouchDB.login("jan", "apple").ok);
});
};
- usersDb.deleteDb();
+ usersDb.deleteDb();
run_on_modified_server(
[{section: "couch_httpd_auth",
key: "authentication_db", value: usersDb.name}],