You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ju...@apache.org on 2020/10/07 07:13:49 UTC

[couchdb] 05/07: Complete the port of security_validation tests to Elixir

This is an automated email from the ASF dual-hosted git repository.

juanjo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 2a0a525c6c0692636298782c2f7d51ab66a8ca92
Author: Juanjo Rodriguez <ju...@apache.org>
AuthorDate: Tue Sep 29 11:24:39 2020 +0200

    Complete the port of security_validation tests to Elixir
---
 test/elixir/test/security_validation_test.exs | 248 ++++++++++++--------------
 test/javascript/tests/security_validation.js  |   2 +-
 2 files changed, 118 insertions(+), 132 deletions(-)

diff --git a/test/elixir/test/security_validation_test.exs b/test/elixir/test/security_validation_test.exs
index fda928c..dddf7a7 100644
--- a/test/elixir/test/security_validation_test.exs
+++ b/test/elixir/test/security_validation_test.exs
@@ -21,6 +21,10 @@ defmodule SecurityValidationTest do
     spike_cat: [
       # spike:cat - which is wrong
       authorization: "Basic c3Bpa2U6Y2F0"
+    ],
+    spike: [
+      # spike:dog
+      authorization: "Basic c3Bpa2U6ZG9n"
     ]
   }
 
@@ -148,6 +152,15 @@ defmodule SecurityValidationTest do
   end
 
   @tag :with_db
+  test "try to set a wrong value for _security", context do
+    db_name = context[:db_name]
+    # try to do something lame
+    resp = Couch.put("/#{db_name}/_security", body: ["foo"])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+  end
+
+  @tag :with_db
   test "Author presence and user security", context do
     db_name = context[:db_name]
     sec_obj = %{admin_override: false, admins: %{names: ["jerry"]}}
@@ -179,6 +192,12 @@ defmodule SecurityValidationTest do
     assert resp.status_code == 403
     assert resp.body["error"] == "forbidden"
 
+    # Admin cannot write the document (admin_override = false)
+    test_doc = Map.put(test_doc, "foo", 3)
+    resp = Couch.put("/#{db_name}/test_doc", body: test_doc)
+    assert resp.status_code == 401
+    assert resp.body["error"] == "unauthorized"
+
     # Enable admin override for changing author values
     assert Couch.put("/#{db_name}/_security", body: %{sec_obj | admin_override: true}).body[
              "ok"
@@ -202,136 +221,103 @@ defmodule SecurityValidationTest do
       resp = Couch.delete("/#{db_name}/test_doc?rev=#{test_doc["_rev"]}", opts)
       resp.status_code == 401 and resp.body["error"] == "unauthorized"
     end)
+
+    # Admin can write the document (admin_override = true)
+    test_doc = Map.put(test_doc, "foo", 4)
+    resp = Couch.put("/#{db_name}/test_doc", body: test_doc)
+    assert resp.body["ok"]
+
+    # Disable admin override
+    assert Couch.put("/#{db_name}/_security", body: %{sec_obj | admin_override: false}).body[
+             "ok"
+           ]
+
+    docs = [%{_id: "bahbah", author: "jerry", foo: "bar"}, %{_id: "fahfah", foo: "baz"}]
+
+    resp =
+      Couch.post(
+        "/#{db_name}/_bulk_docs",
+        body: %{
+          docs: docs
+        },
+        headers: jerry
+      )
+
+    assert Enum.at(resp.body, 0)["rev"]
+    assert !Enum.at(resp.body, 0)["error"]
+    assert !Enum.at(resp.body, 1)["rev"]
+    assert Enum.at(resp.body, 1)["error"] == "forbidden"
+
+    resp = Couch.get("/#{db_name}/bahbah")
+    assert resp.status_code == 200
+
+    resp = Couch.get("/#{db_name}/fahfah")
+    assert resp.status_code == 404
   end
-end
 
-# TODO: port remainder of security_validation.js suite
-# remaining bits reproduced below:
-#
-#      // try to do something lame
-#      try {
-#        db.setDbProperty("_security", ["foo"]);
-#        T(false && "can't do this");
-#      } catch(e) {}
-#
-#      // go back to normal
-#      T(db.setDbProperty("_security", {admin_override : false}).ok);
-#
-#      // Now delete document
-#      T(user2Db.deleteDoc(doc).ok);
-#
-#      // now test bulk docs
-#      var docs = [{_id:"bahbah",author:"jerry",foo:"bar"},{_id:"fahfah",foo:"baz"}];
-#
-#      // Create the docs
-#      var results = db.bulkSave(docs);
-#
-#      T(results[0].rev)
-#      T(results[0].error == undefined)
-#      T(results[1].rev === undefined)
-#      T(results[1].error == "forbidden")
-#
-#      T(db.open("bahbah"));
-#      T(db.open("fahfah") == null);
-#
-#
-#      // 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});
-#
-#      T(results.errors.length == 1);
-#      T(results.errors[0].error == "forbidden");
-#      T(db.open("booboo") == null);
-#      T(db.open("foofoo") == null);
-# */
-#
-#      // Now test replication
-#      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";
-#
-#      // (the databases never exist b4 - and we made sure they're deleted below)
-#      //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();
-#  // have to clean up authDb on the backside :(
-#  var req = CouchDB.newXhr();
-#  req.open("DELETE", "http://127.0.0.1:15986/" + authDb_name, false);
-#  req.send("");
-#  CouchDB.maybeThrowError(req);
-# };
+  test "Author presence and user security when replicated", _context do
+    db_name = random_db_name()
+    db_name_a = "#{db_name}_a"
+    db_name_b = "#{db_name}_b"
+    create_db(db_name_a)
+    create_db(db_name_b)
+    on_exit(fn -> delete_db(db_name_a) end)
+    on_exit(fn -> delete_db(db_name_b) end)
+
+    spike = @auth_headers[:spike]
+
+    # save and replicate a documents that will and will not pass our design
+    # doc validation function.
+    {:ok, _} = create_doc(db_name_a, %{_id: "foo1", value: "a", author: "tom"})
+    {:ok, _} = create_doc(db_name_a, %{_id: "foo2", value: "a", author: "spike"})
+    {:ok, _} = create_doc(db_name_a, %{_id: "bad1", value: "a"})
+    replicate(db_name_a, db_name_b, headers: spike)
+    replicate(db_name_b, db_name_a, headers: spike)
+
+    assert Couch.get("/#{db_name_a}/foo1").status_code == 200
+    assert Couch.get("/#{db_name_b}/foo1").status_code == 200
+    assert Couch.get("/#{db_name_a}/foo2").status_code == 200
+    assert Couch.get("/#{db_name_b}/foo2").status_code == 200
+
+    {:ok, _} = create_doc(db_name_a, @ddoc)
+
+    # no affect on already saved docs
+    assert Couch.get("/#{db_name_a}/bad1").status_code == 200
+
+    # 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)
+    assert Couch.post(
+             "/#{db_name_b}",
+             body: %{id: "bad2", value: "a"},
+             headers: spike
+           ).body["ok"]
+
+    # this edit will fail security on replication to dbA (wrong author
+    # replicating the change)
+    foo1 = Couch.get("/#{db_name_b}/foo1").body
+    foo1 = Map.put(foo1, "value", "b")
+    assert Couch.put("/#{db_name_b}/foo1", body: foo1, headers: spike).body["ok"]
+
+    # this is a legal edit
+    foo2 = Couch.get("/#{db_name_b}/foo2").body
+    foo2 = Map.put(foo2, "value", "b")
+    assert Couch.put("/#{db_name_b}/foo2", body: foo2, headers: spike).body["ok"]
+
+    result = replicate(db_name_b, db_name_a, headers: spike)
+    assert Enum.at(result["history"], 0)["docs_written"] == 1
+    assert Enum.at(result["history"], 0)["doc_write_failures"] == 2
+
+    # bad2 should not be on dbA
+    assert Couch.get("/#{db_name_a}/bad2").status_code == 404
+
+    # The edit to foo1 should not have replicated.
+    resp = Couch.get("/#{db_name_a}/foo1")
+    assert resp.body["value"] == "a"
+
+    # The edit to foo2 should have replicated.
+    resp = Couch.get("/#{db_name_a}/foo2")
+    assert resp.body["value"] == "b"
+  end
+end
diff --git a/test/javascript/tests/security_validation.js b/test/javascript/tests/security_validation.js
index 6f0bd0f..365f716 100644
--- a/test/javascript/tests/security_validation.js
+++ b/test/javascript/tests/security_validation.js
@@ -9,7 +9,7 @@
 // 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.elixir = true;
 couchTests.security_validation = function(debug) {
 
   var db_name = get_random_db_name();