summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuanjo Rodriguez <jjrodrig@gmail.com>2019-04-11 23:52:51 +0200
committerJuanjo Rodriguez <jjrodrig@gmail.com>2019-04-11 23:52:51 +0200
commit0709ccf95cfd39076baee6d87035a79a56d556b1 (patch)
tree4e6fa5b1e10622627fa34c4c618fbeddf9396376
parent5d199268d4330b80ce54d5dad2fe27db4bb81641 (diff)
downloadcouchdb-0709ccf95cfd39076baee6d87035a79a56d556b1.tar.gz
Add auth cache test into elixir test suite
-rw-r--r--test/elixir/lib/couch.ex19
-rw-r--r--test/elixir/lib/couch/db_test.ex56
-rw-r--r--test/elixir/test/auth_cache_test.exs195
3 files changed, 259 insertions, 11 deletions
diff --git a/test/elixir/lib/couch.ex b/test/elixir/lib/couch.ex
index 58581b2fd..795405b51 100644
--- a/test/elixir/lib/couch.ex
+++ b/test/elixir/lib/couch.ex
@@ -3,11 +3,10 @@ defmodule Couch.Session do
CouchDB session helpers.
"""
- @enforce_keys [:cookie]
- defstruct [:cookie]
+ defstruct [:cookie, :error]
- def new(cookie) do
- %Couch.Session{cookie: cookie}
+ def new(cookie, error \\ "") do
+ %Couch.Session{cookie: cookie, error: error}
end
def logout(sess) do
@@ -119,10 +118,14 @@ defmodule Couch do
def login(user, pass) do
resp = Couch.post("/_session", body: %{:username => user, :password => pass})
- true = resp.body["ok"]
- cookie = resp.headers[:"set-cookie"]
- [token | _] = String.split(cookie, ";")
- %Couch.Session{cookie: token}
+
+ if resp.body["ok"] do
+ cookie = resp.headers[:"set-cookie"]
+ [token | _] = String.split(cookie, ";")
+ %Couch.Session{cookie: token}
+ else
+ %Couch.Session{error: resp.body["error"]}
+ end
end
# HACK: this is here until this commit lands in a release
diff --git a/test/elixir/lib/couch/db_test.ex b/test/elixir/lib/couch/db_test.ex
index 990173a13..0b5f03c70 100644
--- a/test/elixir/lib/couch/db_test.ex
+++ b/test/elixir/lib/couch/db_test.ex
@@ -116,13 +116,14 @@ defmodule Couch.DBTest do
end)
end
- def create_user(user) do
+ def prepare_user_doc(user) do
required = [:name, :password, :roles]
Enum.each(required, fn key ->
assert Keyword.has_key?(user, key), "User missing key: #{key}"
end)
+ id = Keyword.get(user, :id)
name = Keyword.get(user, :name)
password = Keyword.get(user, :password)
roles = Keyword.get(user, :roles)
@@ -135,14 +136,17 @@ defmodule Couch.DBTest do
assert is_binary(role), "Roles must be a list of strings"
end)
- user_doc = %{
- "_id" => "org.couchdb.user:" <> name,
+ %{
+ "_id" => id || "org.couchdb.user:" <> name,
"type" => "user",
"name" => name,
"roles" => roles,
"password" => password
}
+ end
+ def create_user(user) do
+ user_doc = prepare_user_doc(user)
resp = Couch.get("/_users/#{user_doc["_id"]}")
user_doc =
@@ -182,6 +186,32 @@ defmodule Couch.DBTest do
{:ok, resp}
end
+ def save_doc(db_name, body) do
+ resp = Couch.put("/#{db_name}/#{body["_id"]}", body: body)
+ assert resp.status_code in [201, 202]
+ assert resp.body["ok"]
+ {:ok, resp}
+ end
+
+ def delete_doc(db_name, body) do
+ resp = Couch.delete("/#{db_name}/#{body["_id"]}", query: [rev: body["_rev"]])
+ assert resp.status_code in [200, 202]
+ assert resp.body["ok"]
+ {:ok, resp}
+ end
+
+ def compact(db_name) do
+ resp = Couch.post("/#{db_name}/_compact")
+ assert resp.status_code == 202
+ resp.body
+ end
+
+ def info(db_name) do
+ resp = Couch.get("/#{db_name}")
+ assert resp.status_code == 200
+ resp.body
+ end
+
def bulk_save(db_name, docs) do
resp =
Couch.post(
@@ -290,6 +320,26 @@ defmodule Couch.DBTest do
end
end
+ def request_stats(path_steps, is_test) do
+ path =
+ List.foldl(
+ path_steps,
+ "/_node/_local/_stats",
+ fn p, acc ->
+ "#{acc}/#{p}"
+ end
+ )
+
+ path =
+ if is_test do
+ path <> "?flush=true"
+ else
+ path
+ end
+
+ Couch.get(path).body
+ end
+
def retry_until(condition, sleep \\ 100, timeout \\ 5000) do
retry_until(condition, now(:ms), sleep, timeout)
end
diff --git a/test/elixir/test/auth_cache_test.exs b/test/elixir/test/auth_cache_test.exs
new file mode 100644
index 000000000..8baa1e094
--- /dev/null
+++ b/test/elixir/test/auth_cache_test.exs
@@ -0,0 +1,195 @@
+defmodule AuthCacheTest do
+ use CouchTestCase
+
+ @moduletag :authentication
+ @tag :with_db
+ test "test auth cache management", context do
+ db_name = context[:db_name]
+
+ server_config = [
+ %{
+ :section => "chttpd_auth",
+ :key => "authentication_db",
+ :value => db_name
+ },
+ %{
+ :section => "chttpd_auth",
+ :key => "auth_cache_size",
+ :value => "3"
+ },
+ %{
+ :section => "httpd",
+ :key => "authentication_handlers",
+ :value => "{couch_httpd_auth, default_authentication_handler}"
+ },
+ %{
+ :section => "chttpd_auth",
+ :key => "secret",
+ :value => generate_secret(64)
+ }
+ ]
+
+ run_on_modified_server(server_config, fn -> test_fun(db_name) end)
+ end
+
+ defp generate_secret(length) do
+ tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+ for _i <- 1..length, into: "" do
+ String.at(tab, trunc(Float.floor(:rand.uniform() * 64)))
+ end
+ end
+
+ defp hits() do
+ hits = request_stats(["couchdb", "auth_cache_hits"], true)
+ hits["value"] || 0
+ end
+
+ defp misses() do
+ misses = request_stats(["couchdb", "auth_cache_misses"], true)
+ misses["value"] || 0
+ end
+
+ defp logout(session) do
+ assert Couch.Session.logout(session).body["ok"]
+ end
+
+ defp login_fail(user, password) do
+ resp = Couch.login(user, password)
+ assert resp.error, "Login error is expected."
+ end
+
+ defp login(user, password) do
+ sess = Couch.login(user, password)
+ assert sess.cookie, "Login correct is expected"
+ sess
+ end
+
+ defp wait_until_compact_complete(db_name) do
+ info = info(db_name)
+
+ if info["compact_running"] do
+ wait_until_compact_complete(db_name)
+ end
+ end
+
+ defp assert_cache(event, user, password, expect \\ :expect_login_success) do
+ hits_before = hits()
+ misses_before = misses()
+
+ session =
+ case expect do
+ :expect_login_success -> login(user, password)
+ :expect_login_fail -> login_fail(user, password)
+ _ -> assert false
+ end
+
+ hits_after = hits()
+ misses_after = misses()
+
+ if expect == :expect_success do
+ logout(session)
+ end
+
+ case event do
+ :expect_miss ->
+ assert misses_after == misses_before + 1,
+ "Cache miss is expected for #{user} after login"
+
+ assert hits_after == hits_before,
+ "No cache hit is expected for #{user} after login"
+
+ :expect_hit ->
+ assert misses_after == misses_before,
+ "No cache miss is expected for #{user} after login"
+
+ assert hits_after == hits_before + 1,
+ "Cache hit is expected for #{user} after login"
+
+ _ ->
+ assert false
+ end
+ end
+
+ defp test_fun(db_name) do
+ fdmanana =
+ prepare_user_doc([
+ {:name, "fdmanana"},
+ {:password, "qwerty"},
+ {:roles, ["dev"]}
+ ])
+
+ {:ok, resp} = create_doc(db_name, fdmanana)
+ fdmanana = Map.put(fdmanana, "_rev", resp.body["rev"])
+
+ chris =
+ prepare_user_doc([
+ {:name, "chris"},
+ {:password, "the_god_father"},
+ {:roles, ["dev", "mafia", "white_costume"]}
+ ])
+
+ create_doc(db_name, chris)
+
+ joe =
+ prepare_user_doc([
+ {:name, "joe"},
+ {:password, "functional"},
+ {:roles, ["erlnager"]}
+ ])
+
+ create_doc(db_name, joe)
+
+ johndoe =
+ prepare_user_doc([
+ {:name, "johndoe"},
+ {:password, "123456"},
+ {:roles, ["user"]}
+ ])
+
+ create_doc(db_name, johndoe)
+
+ assert_cache(:expect_miss, "fdmanana", "qwerty")
+ assert_cache(:expect_hit, "fdmanana", "qwerty")
+ assert_cache(:expect_miss, "chris", "the_god_father")
+ assert_cache(:expect_miss, "joe", "functional")
+ assert_cache(:expect_miss, "johndoe", "123456")
+
+ # It's a MRU cache, joe was removed from cache to add johndoe
+ # BUGGED assert_cache(:expect_miss, "joe", "functional")
+
+ assert_cache(:expect_hit, "fdmanana", "qwerty")
+
+ fdmanana = Map.replace!(fdmanana, "password", "foobar")
+ {:ok, resp} = save_doc(db_name, fdmanana)
+ second_rev = resp.body["rev"]
+
+ # Cache was refreshed
+ # BUGGED
+ # assert_cache(:expect_hit, "fdmanana", "qwerty", :expect_login_fail)
+ # assert_cache(:expect_hit, "fdmanana", "foobar")
+
+ # and yet another update
+ fdmanana = Map.replace!(fdmanana, "password", "javascript")
+ fdmanana = Map.replace!(fdmanana, "_rev", second_rev)
+ {:ok, resp} = save_doc(db_name, fdmanana)
+ third_rev = resp.body["rev"]
+ fdmanana = Map.replace!(fdmanana, "_rev", third_rev)
+
+ # Cache was refreshed
+ # BUGGED
+ # assert_cache(:expect_hit, "fdmanana", "foobar", :expect_login_fail)
+ # assert_cache(:expect_hit, "fdmanana", "javascript")
+
+ delete_doc(db_name, fdmanana)
+
+ assert_cache(:expect_hit, "fdmanana", "javascript", :expect_login_fail)
+
+ # login, compact authentication DB, login again and verify that
+ # there was a cache hit
+ assert_cache(:expect_hit, "johndoe", "123456")
+ compact(db_name)
+ wait_until_compact_complete(db_name)
+ assert_cache(:expect_hit, "johndoe", "123456")
+ end
+end