http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_simple.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_simple.js b/share/www/script/test/replicator_db_simple.js new file mode 100644 index 0000000..f7acedb --- /dev/null +++ b/share/www/script/test/replicator_db_simple.js @@ -0,0 +1,114 @@ +// 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_simple = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var waitForRep = replicator_db.waitForRep; + + function simple_replication() { + populate_db(dbA, docs1); + populate_db(dbB, []); + + var repDoc = { + _id: "foo_simple_rep", + source: dbA.name, + target: dbB.name + }; + T(repDb.save(repDoc).ok); + + waitForRep(repDb, repDoc, "completed"); + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + T(copy !== null); + T(copy.value === doc.value); + } + + var repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + T(repDoc1.source === repDoc.source); + T(repDoc1.target === repDoc.target); + T(repDoc1._replication_state === "completed", "simple"); + T(typeof repDoc1._replication_state_time === "string"); + T(typeof repDoc1._replication_id === "string"); + T(typeof repDoc1._replication_stats === "object", "doc has stats"); + var stats = repDoc1._replication_stats; + TEquals(docs1.length, stats.revisions_checked, + "right # of revisions_checked"); + TEquals(docs1.length, stats.missing_revisions_found, + "right # of missing_revisions_found"); + TEquals(docs1.length, stats.docs_read, "right # of docs_read"); + TEquals(docs1.length, stats.docs_written, "right # of docs_written"); + TEquals(0, stats.doc_write_failures, "right # of doc_write_failures"); + TEquals(dbA.info().update_seq, stats.checkpointed_source_seq, + "right checkpointed_source_seq"); + } + + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, simple_replication); + +/* + * Disabled, since error state would be set on the document only after + * the exponential backoff retry done by the replicator database listener + * terminates, which takes too much time for a unit test. + */ + /* + function error_state_replication() { + populate_db(dbA, docs1); + + var repDoc = { + _id: "foo_error_rep", + source: dbA.name, + target: "nonexistent_test_db" + }; + T(repDb.save(repDoc).ok); + + waitForRep(repDb, repDoc, "error"); + var repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + T(repDoc1._replication_state === "error"); + T(typeof repDoc1._replication_state_time === "string"); + T(typeof repDoc1._replication_id === "string"); + } + */ +/* + * repDb.deleteDb(); + * restartServer(); + * run_on_modified_server(server_config, error_state_replication); + */ + + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_successive.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_successive.js b/share/www/script/test/replicator_db_successive.js new file mode 100644 index 0000000..4898c33 --- /dev/null +++ b/share/www/script/test/replicator_db_successive.js @@ -0,0 +1,127 @@ +// 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_successive = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var wait = replicator_db.wait; + var waitForRep = replicator_db.waitForRep; + var waitForSeq = replicator_db.waitForSeq; + + function successive_identical_replications() { + populate_db(dbA, docs1); + populate_db(dbB, []); + + var repDoc1 = { + _id: "foo_ident_rep_1", + source: dbA.name, + target: dbB.name + }; + T(repDb.save(repDoc1).ok); + + waitForRep(repDb, repDoc1, "completed"); + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + T(copy !== null); + T(copy.value === doc.value); + } + + var repDoc1_copy = repDb.open(repDoc1._id); + T(repDoc1_copy !== null); + T(repDoc1_copy.source === repDoc1.source); + T(repDoc1_copy.target === repDoc1.target); + T(repDoc1_copy._replication_state === "completed"); + T(typeof repDoc1_copy._replication_state_time === "string"); + T(typeof repDoc1_copy._replication_id === "string"); + T(typeof repDoc1_copy._replication_stats === "object", "doc has stats"); + var stats = repDoc1_copy._replication_stats; + TEquals(docs1.length, stats.revisions_checked, + "right # of revisions_checked"); + TEquals(docs1.length, stats.missing_revisions_found, + "right # of missing_revisions_found"); + TEquals(docs1.length, stats.docs_read, "right # of docs_read"); + TEquals(docs1.length, stats.docs_written, "right # of docs_written"); + TEquals(0, stats.doc_write_failures, "right # of doc_write_failures"); + TEquals(dbA.info().update_seq, stats.checkpointed_source_seq, + "right checkpointed_source_seq"); + + var newDoc = { + _id: "doc666", + value: 666 + }; + T(dbA.save(newDoc).ok); + + wait(200); + var newDoc_copy = dbB.open(newDoc._id); + // not replicated because first replication is complete (not continuous) + T(newDoc_copy === null); + + var repDoc2 = { + _id: "foo_ident_rep_2", + source: dbA.name, + target: dbB.name + }; + T(repDb.save(repDoc2).ok); + + waitForRep(repDb, repDoc2, "completed"); + var newDoc_copy = dbB.open(newDoc._id); + T(newDoc_copy !== null); + T(newDoc_copy.value === newDoc.value); + + var repDoc2_copy = repDb.open(repDoc2._id); + T(repDoc2_copy !== null); + T(repDoc2_copy.source === repDoc1.source); + T(repDoc2_copy.target === repDoc1.target); + T(repDoc2_copy._replication_state === "completed"); + T(typeof repDoc2_copy._replication_state_time === "string"); + T(typeof repDoc2_copy._replication_id === "string"); + T(repDoc2_copy._replication_id === repDoc1_copy._replication_id); + T(typeof repDoc2_copy._replication_stats === "object", "doc has stats"); + stats = repDoc2_copy._replication_stats; + TEquals(1, stats.revisions_checked, "right # of revisions_checked"); + TEquals(1, stats.missing_revisions_found, + "right # of missing_revisions_found"); + TEquals(1, stats.docs_read, "right # of docs_read"); + TEquals(1, stats.docs_written, "right # of docs_written"); + TEquals(0, stats.doc_write_failures, "right # of doc_write_failures"); + TEquals(dbA.info().update_seq, stats.checkpointed_source_seq, + "right checkpointed_source_seq"); + } + + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, successive_identical_replications); + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_survives.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_survives.js b/share/www/script/test/replicator_db_survives.js new file mode 100644 index 0000000..dcaa101 --- /dev/null +++ b/share/www/script/test/replicator_db_survives.js @@ -0,0 +1,128 @@ +// 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_survives = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var usersDb = replicator_db.usersDb; + var wait = replicator_db.wait; + var waitForRep = replicator_db.waitForRep; + var waitForSeq = replicator_db.waitForSeq; + var waitForDocPos = replicator_db.waitForDocPos; + var wait_rep_doc = replicator_db.wait_rep_doc; + + function continuous_replication_survives_restart() { + var origRepDbName = CouchDB.request( + "GET", "/_config/replicator/db").responseText; + + repDb.deleteDb(); + + var xhr = CouchDB.request("PUT", "/_config/replicator/db", { + body : JSON.stringify(repDb.name), + headers: {"X-Couch-Persist": "false"} + }); + T(xhr.status === 200); + + repDb.createDb(); // the config put above should create this db + + populate_db(dbA, docs1); + populate_db(dbB, []); + + var repDoc = { + _id: "foo_cont_rep_survives_doc", + source: dbA.name, + target: dbB.name, + continuous: true + }; + + T(repDb.save(repDoc).ok); + + waitForSeq(dbA, dbB); + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + T(copy !== null); + T(copy.value === doc.value); + } + + repDb.ensureFullCommit(); + dbA.ensureFullCommit(); + + restartServer(); + + xhr = CouchDB.request("PUT", "/_config/replicator/db", { + body : JSON.stringify(repDb.name), + headers: {"X-Couch-Persist": "false"} + }); + + T(xhr.status === 200); + + // add another doc to source, it will be replicated to target + var docX = { + _id: "foo1000", + value: 1001 + }; + + T(dbA.save(docX).ok); + + waitForSeq(dbA, dbB); + var copy = dbB.open("foo1000"); + T(copy !== null); + T(copy.value === 1001); + + repDoc = waitForDocPos(repDb, "foo_cont_rep_survives_doc", 3); + T(repDoc !== null); + T(repDoc.continuous === true); + + // stop replication + T(repDb.deleteDoc(repDoc).ok); + + xhr = CouchDB.request("PUT", "/_config/replicator/db", { + body : origRepDbName, + headers: {"X-Couch-Persist": "false"} + }); + T(xhr.status === 200); + } + + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + }, + { + section: "couch_httpd_auth", + key: "authentication_db", + value: usersDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, continuous_replication_survives_restart); + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); + usersDb.deleteDb(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_swap_rep_db.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_swap_rep_db.js b/share/www/script/test/replicator_db_swap_rep_db.js new file mode 100644 index 0000000..04f4e9f --- /dev/null +++ b/share/www/script/test/replicator_db_swap_rep_db.js @@ -0,0 +1,170 @@ +// 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_swap_rep_db = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var usersDb = replicator_db.usersDb; + var wait = replicator_db.wait; + var waitForRep = replicator_db.waitForRep; + var waitForSeq = replicator_db.waitForSeq; + var wait_rep_doc = replicator_db.wait_rep_doc; + + function swap_rep_db() { + var repDb2 = new CouchDB("test_suite_rep_db_2"); + var dbA = new CouchDB("test_suite_rep_db_a"); + var dbA_copy = new CouchDB("test_suite_rep_db_a_copy"); + var dbB = new CouchDB("test_suite_rep_db_b"); + var dbB_copy = new CouchDB("test_suite_rep_db_b_copy"); + var dbC = new CouchDB("test_suite_rep_db_c"); + var dbC_copy = new CouchDB("test_suite_rep_db_c_copy"); + var repDoc1, repDoc2, repDoc3; + var xhr, i, doc, copy, new_doc; + + populate_db(dbA, docs1); + populate_db(dbB, docs1); + populate_db(dbC, docs1); + populate_db(dbA_copy, []); + populate_db(dbB_copy, []); + populate_db(dbC_copy, []); + populate_db(repDb2, []); + + repDoc1 = { + _id: "rep1", + source: CouchDB.protocol + CouchDB.host + "/" + dbA.name, + target: dbA_copy.name, + continuous: true + }; + repDoc2 = { + _id: "rep2", + source: CouchDB.protocol + CouchDB.host + "/" + dbB.name, + target: dbB_copy.name, + continuous: true + }; + repDoc3 = { + _id: "rep3", + source: CouchDB.protocol + CouchDB.host + "/" + dbC.name, + target: dbC_copy.name, + continuous: true + }; + + TEquals(true, repDb.save(repDoc1).ok); + TEquals(true, repDb.save(repDoc2).ok); + + waitForSeq(dbA, dbA_copy); + waitForSeq(dbB, dbB_copy); + + xhr = CouchDB.request("PUT", "/_config/replicator/db",{ + body : JSON.stringify(repDb2.name), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status); + + // Temporary band-aid, give the replicator db some + // time to make the switch + wait(500); + + new_doc = { + _id: "foo666", + value: 666 + }; + + TEquals(true, dbA.save(new_doc).ok); + TEquals(true, dbB.save(new_doc).ok); + waitForSeq(dbA, dbA_copy); + waitForSeq(dbB, dbB_copy); + + TEquals(true, repDb2.save(repDoc3).ok); + waitForSeq(dbC, dbC_copy); + + for (i = 0; i < docs1.length; i++) { + doc = docs1[i]; + copy = dbA_copy.open(doc._id); + T(copy !== null); + TEquals(doc.value, copy.value); + copy = dbB_copy.open(doc._id); + T(copy !== null); + TEquals(doc.value, copy.value); + copy = dbC_copy.open(doc._id); + T(copy !== null); + TEquals(doc.value, copy.value); + } + + // replications rep1 and rep2 should have been stopped when the replicator + // database was swapped + copy = dbA_copy.open(new_doc._id); + TEquals(null, copy); + copy = dbB_copy.open(new_doc._id); + TEquals(null, copy); + + xhr = CouchDB.request("PUT", "/_config/replicator/db",{ + body : JSON.stringify(repDb.name), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status); + + // after setting the replicator database to the former, replications rep1 + // and rep2 should have been resumed, while rep3 was stopped + TEquals(true, dbC.save(new_doc).ok); + wait(1000); + + waitForSeq(dbA, dbA_copy); + waitForSeq(dbB, dbB_copy); + + copy = dbA_copy.open(new_doc._id); + T(copy !== null); + TEquals(new_doc.value, copy.value); + copy = dbB_copy.open(new_doc._id); + T(copy !== null); + TEquals(new_doc.value, copy.value); + copy = dbC_copy.open(new_doc._id); + TEquals(null, copy); + } + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + }, + { + section: "couch_httpd_auth", + key: "authentication_db", + value: usersDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, swap_rep_db); + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); + usersDb.deleteDb(); + (new CouchDB("test_suite_rep_db_2")).deleteDb(); + (new CouchDB("test_suite_rep_db_c")).deleteDb(); + (new CouchDB("test_suite_rep_db_a_copy")).deleteDb(); + (new CouchDB("test_suite_rep_db_b_copy")).deleteDb(); + (new CouchDB("test_suite_rep_db_c_copy")).deleteDb(); + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_update_security.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_update_security.js b/share/www/script/test/replicator_db_update_security.js new file mode 100644 index 0000000..4651514 --- /dev/null +++ b/share/www/script/test/replicator_db_update_security.js @@ -0,0 +1,92 @@ +// 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_update_security = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var usersDb = replicator_db.usersDb; + var wait = replicator_db.wait; + var waitForRep = replicator_db.waitForRep; + var waitForSeq = replicator_db.waitForSeq; + var wait_rep_doc = replicator_db.wait_rep_doc; + + function test_rep_db_update_security() { + var dbA_copy = new CouchDB("test_suite_rep_db_a_copy"); + var dbB_copy = new CouchDB("test_suite_rep_db_b_copy"); + var repDoc1, repDoc2; + var xhr, i, doc, copy, new_doc; + var docs = makeDocs(1, 3); + + populate_db(dbA, docs); + populate_db(dbB, docs); + populate_db(dbA_copy, []); + populate_db(dbB_copy, []); + + repDoc1 = { + _id: "rep1", + source: CouchDB.protocol + CouchDB.host + "/" + dbA.name, + target: dbA_copy.name + }; + repDoc2 = { + _id: "rep2", + source: CouchDB.protocol + CouchDB.host + "/" + dbB.name, + target: dbB_copy.name + }; + + TEquals(true, repDb.save(repDoc1).ok); + waitForRep(repDb, repDoc1, "completed"); + + T(repDb.setSecObj({ + readers: { + names: ["joe"] + } + }).ok); + + TEquals(true, repDb.save(repDoc2).ok); + waitForRep(repDb, repDoc2, "completed"); + } + + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + }, + { + section: "couch_httpd_auth", + key: "authentication_db", + value: usersDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, test_rep_db_update_security); + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); + usersDb.deleteDb(); + (new CouchDB("test_suite_rep_db_a_copy")).deleteDb(); + (new CouchDB("test_suite_rep_db_b_copy")).deleteDb(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_user_ctx.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_user_ctx.js b/share/www/script/test/replicator_db_user_ctx.js new file mode 100644 index 0000000..570fc7d --- /dev/null +++ b/share/www/script/test/replicator_db_user_ctx.js @@ -0,0 +1,272 @@ +// 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_user_ctx = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var usersDb = replicator_db.usersDb; + var wait = replicator_db.wait; + var waitForRep = replicator_db.waitForRep; + var waitForSeq = replicator_db.waitForSeq; + var wait_rep_doc = replicator_db.wait_rep_doc; + + function test_user_ctx_validation() { + populate_db(dbA, docs1); + populate_db(dbB, []); + populate_db(usersDb, []); + + var joeUserDoc = CouchDB.prepareUserDoc({ + name: "joe", + roles: ["erlanger", "bar"] + }, "erly"); + var fdmananaUserDoc = CouchDB.prepareUserDoc({ + name: "fdmanana", + roles: ["a", "b", "c"] + }, "qwerty"); + + TEquals(true, usersDb.save(joeUserDoc).ok); + TEquals(true, usersDb.save(fdmananaUserDoc).ok); + + T(dbB.setSecObj({ + admins: { + names: [], + roles: ["god"] + }, + readers: { + names: [], + roles: ["foo"] + } + }).ok); + + TEquals(true, CouchDB.login("joe", "erly").ok); + TEquals("joe", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + var repDoc = { + _id: "foo_rep", + source: CouchDB.protocol + CouchDB.host + "/" + dbA.name, + target: dbB.name + }; + + try { + repDb.save(repDoc); + T(false, "Should have failed, user_ctx missing."); + } catch (x) { + TEquals("forbidden", x.error); + } + + repDoc.user_ctx = { + name: "john", + roles: ["erlanger"] + }; + + try { + repDb.save(repDoc); + T(false, "Should have failed, wrong user_ctx.name."); + } catch (x) { + TEquals("forbidden", x.error); + } + + repDoc.user_ctx = { + name: "joe", + roles: ["bar", "god", "erlanger"] + }; + + try { + repDb.save(repDoc); + T(false, "Should have failed, a bad role in user_ctx.roles."); + } catch (x) { + TEquals("forbidden", x.error); + } + + // user_ctx.roles might contain only a subset of the user's roles + repDoc.user_ctx = { + name: "joe", + roles: ["erlanger"] + }; + + TEquals(true, repDb.save(repDoc).ok); + CouchDB.logout(); + + waitForRep(repDb, repDoc, "error"); + var repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("error", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + TEquals(true, CouchDB.login("fdmanana", "qwerty").ok); + TEquals("fdmanana", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + try { + T(repDb.deleteDoc(repDoc1).ok); + T(false, "Shouldn't be able to delete replication document."); + } catch (x) { + TEquals("forbidden", x.error); + } + + CouchDB.logout(); + TEquals(true, CouchDB.login("joe", "erly").ok); + TEquals("joe", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + T(repDb.deleteDoc(repDoc1).ok); + CouchDB.logout(); + + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + + TEquals(null, copy); + } + + T(dbB.setSecObj({ + admins: { + names: [], + roles: ["god", "erlanger"] + }, + readers: { + names: [], + roles: ["foo"] + } + }).ok); + + TEquals(true, CouchDB.login("joe", "erly").ok); + TEquals("joe", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + repDoc = { + _id: "foo_rep_2", + source: CouchDB.protocol + CouchDB.host + "/" + dbA.name, + target: dbB.name, + user_ctx: { + name: "joe", + roles: ["erlanger"] + } + }; + + TEquals(true, repDb.save(repDoc).ok); + CouchDB.logout(); + + waitForRep(repDb, repDoc, "complete"); + repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("completed", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + + T(copy !== null); + TEquals(doc.value, copy.value); + } + + // Admins don't need to supply a user_ctx property in replication docs. + // If they do not, the implicit user_ctx "user_ctx": {name: null, roles: []} + // is used, meaning that design documents will not be replicated into + // local targets + T(dbB.setSecObj({ + admins: { + names: [], + roles: [] + }, + readers: { + names: [], + roles: [] + } + }).ok); + + var ddoc = { _id: "_design/foo" }; + TEquals(true, dbA.save(ddoc).ok); + + repDoc = { + _id: "foo_rep_3", + source: CouchDB.protocol + CouchDB.host + "/" + dbA.name, + target: dbB.name + }; + + TEquals(true, repDb.save(repDoc).ok); + waitForRep(repDb, repDoc, "complete"); + repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("completed", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + var ddoc_copy = dbB.open(ddoc._id); + T(ddoc_copy === null); + + repDoc = { + _id: "foo_rep_4", + source: CouchDB.protocol + CouchDB.host + "/" + dbA.name, + target: dbB.name, + user_ctx: { + roles: ["_admin"] + } + }; + + TEquals(true, repDb.save(repDoc).ok); + waitForRep(repDb, repDoc, "complete"); + repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("completed", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + ddoc_copy = dbB.open(ddoc._id); + T(ddoc_copy !== null); + } + + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + }, + { + section: "couch_httpd_auth", + key: "authentication_db", + value: usersDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, test_user_ctx_validation); + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); + usersDb.deleteDb(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/couchdb/blob/92795eb2/share/www/script/test/replicator_db_write_auth.js ---------------------------------------------------------------------- diff --git a/share/www/script/test/replicator_db_write_auth.js b/share/www/script/test/replicator_db_write_auth.js new file mode 100644 index 0000000..697abf3 --- /dev/null +++ b/share/www/script/test/replicator_db_write_auth.js @@ -0,0 +1,102 @@ +// 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_survives = function(debug) { + + if (debug) debugger; + + var populate_db = replicator_db.populate_db; + var docs1 = replicator_db.docs1; + var dbA = replicator_db.dbA; + var dbB = replicator_db.dbB; + var repDb = replicator_db.repDb; + var usersDb = replicator_db.usersDb; + var wait = replicator_db.wait; + var waitForRep = replicator_db.waitForRep; + var waitForSeq = replicator_db.waitForSeq; + var waitForDocPos = replicator_db.waitForDocPos; + var wait_rep_doc = replicator_db.wait_rep_doc; + + function rep_db_write_authorization() { + populate_db(dbA, docs1); + populate_db(dbB, []); + + var server_admins_config = [ + { + section: "admins", + key: "fdmanana", + value: "qwerty" + } + ]; + + run_on_modified_server(server_admins_config, function() { + var repDoc = { + _id: "foo_rep_doc", + source: dbA.name, + target: dbB.name, + continuous: true + }; + + T(CouchDB.login("fdmanana", "qwerty").ok); + T(CouchDB.session().userCtx.name === "fdmanana"); + T(CouchDB.session().userCtx.roles.indexOf("_admin") !== -1); + + T(repDb.save(repDoc).ok); + + waitForRep(repDb, repDoc, "completed"); + + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + + T(copy !== null); + T(copy.value === doc.value); + } + + repDoc = repDb.open("foo_rep_doc"); + T(repDoc !== null); + repDoc.target = "test_suite_foo_db"; + repDoc.create_target = true; + + // Only the replicator can update replication documents. + // Admins can only add and delete replication documents. + try { + repDb.save(repDoc); + T(false && "Should have thrown an exception"); + } catch (x) { + T(x["error"] === "forbidden"); + } + }); + } + + var server_config = [ + { + section: "couch_httpd_auth", + key: "iterations", + value: "1" + }, + { + section: "replicator", + key: "db", + value: repDb.name + } + ]; + + repDb.deleteDb(); + run_on_modified_server(server_config, rep_db_write_authorization); + + // cleanup + repDb.deleteDb(); + dbA.deleteDb(); + dbB.deleteDb(); + usersDb.deleteDb(); +} \ No newline at end of file
