blob: b57e7c5c00bb272b6b6457d7f0af74dba6b60ddd [file]
defmodule PasswordCacheTest do
use CouchTestCase
@moduletag :authentication
@users_db "_users"
@moduletag config: [
{
"chttpd_auth",
"authentication_db",
@users_db
}
]
setup do
reset_db(@users_db)
wait_for_design_auth(@users_db)
on_exit(&tear_down/0)
end
defp tear_down do
reset_db(@users_db)
end
defp fast() do
hits = request_stats(["couchdb", "password_hashing_fast"], true)
hits["value"] || 0
end
defp slow() do
misses = request_stats(["couchdb", "password_hashing_slow"], 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, :fail)
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 assert_cache(event, user, password, expect \\ :expect_login_success) do
slow_before = slow()
fast_before = fast()
session =
case expect do
:expect_login_success -> login(user, password)
:expect_login_fail -> login_fail(user, password)
_ -> assert false
end
slow_after = slow()
fast_after = fast() - 3 # ignore the 3 fast bumps for previous _stats calls
if expect == :expect_success do
logout(session)
end
case event do
:expect_fast ->
assert fast_after == fast_before + 1,
"Fast hash is expected for #{user} during login"
assert slow_after == slow_before,
"No slow hash is expected for #{user} during login"
:expect_slow ->
assert slow_after == slow_before + 1,
"Slow hash is expected for #{user} during login"
case expect do
:expect_login_success ->
assert fast_after == fast_before + 1,
"Fast hash is expected after successful auth after slow hash for #{user} during login"
:expect_login_fail ->
assert fast_after == fast_before
assert fast_after == fast_before,
"No fast hash is expected after unsuccessful auth after slow hash for #{user} during login"
_ ->
assert false
end
_ ->
assert false
end
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"]
Map.put(body, "_rev", resp.body["rev"])
end
def make_user(db_name, user_name) do
result = prepare_user_doc([
{:name, user_name},
{:password, user_name}
])
{:ok, resp} = create_doc(db_name, result)
Map.put(result, "_rev", resp.body["rev"])
end
test "password hash cache" do
user1 = make_user(@users_db, "user1")
# cache misses mean slow auth
assert_cache(:expect_slow, "user1", "wrong_password", :expect_login_fail)
assert_cache(:expect_slow, "user1", "wrong_password", :expect_login_fail)
# last slow auth that populates the cache
assert_cache(:expect_slow, "user1", "user1")
# Fast while cached
assert_cache(:expect_fast, "user1", "user1")
assert_cache(:expect_fast, "user1", "user1")
# change password
user1 = Map.replace!(user1, "password", "new_password")
save_doc(@users_db, user1)
# Slow rejection for wrong password
assert_cache(:expect_slow, "user1", "wrong_password", :expect_login_fail)
# Slow acceptance for new, uncached password
assert_cache(:expect_slow, "user1", "new_password")
end
end