You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/07/31 16:56:17 UTC

[couchdb] 27/34: Reinitialize chttpd_auth_cache on config change

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

davisp pushed a commit to branch prototype/fdb-layer
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 858c947394623006db17ac28015458e7d0920c6d
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Fri Jul 12 12:42:17 2019 -0500

    Reinitialize chttpd_auth_cache on config change
    
    The old test got around this by using couch_httpd_auth cache in its
    tests which is fairly odd given that we run chttpd_auth_cache in
    production. This fixes that mistake and upgrades chttpd_auth_cache so
    that it works in the test scenario of changing the authentication_db
    configuration.
---
 src/chttpd/src/chttpd_auth_cache.erl          | 58 +++++++++++++++++++++++----
 src/fabric/src/fabric2_db.erl                 |  6 ++-
 test/elixir/test/security_validation_test.exs |  4 +-
 3 files changed, 56 insertions(+), 12 deletions(-)

diff --git a/src/chttpd/src/chttpd_auth_cache.erl b/src/chttpd/src/chttpd_auth_cache.erl
index e986af6..9eee196 100644
--- a/src/chttpd/src/chttpd_auth_cache.erl
+++ b/src/chttpd/src/chttpd_auth_cache.erl
@@ -12,16 +12,19 @@
 
 -module(chttpd_auth_cache).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 -export([start_link/0, get_user_creds/2, update_user_creds/3]).
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
     code_change/3]).
 -export([listen_for_changes/1, changes_callback/2]).
+-export([handle_config_change/5, handle_config_terminate/3]).
 
 -include_lib("couch/include/couch_db.hrl").
 -include_lib("couch/include/couch_js_functions.hrl").
 
 -define(CACHE, chttpd_auth_cache_lru).
+-define(RELISTEN_DELAY, 5000).
 
 -record(state, {
     changes_pid,
@@ -101,17 +104,28 @@ maybe_increment_auth_cache_miss(UserName) ->
 %% gen_server callbacks
 
 init([]) ->
-    try
-        fabric2_db:open(dbname(), [?ADMIN_CTX])
-    catch error:database_does_not_exist ->
-        case fabric2_db:create(dbname(), [?ADMIN_CTX]) of
-            {ok, _} -> ok;
-            {error, file_exists} -> ok
-        end
-    end,
+    ensure_auth_db(),
+    ok = config:listen_for_changes(?MODULE, nil),
     self() ! {start_listener, 0},
     {ok, #state{}}.
 
+handle_call(reinit_cache, _From, State) ->
+    #state{
+        changes_pid = Pid
+    } = State,
+
+    % The database may currently be cached. This
+    % ensures that we've removed it so that the
+    % system db callbacks are installed.
+    fabric2_server:remove(dbname()),
+
+    ensure_auth_db(),
+    ets_lru:clear(?CACHE),
+    exit(Pid, shutdown),
+    self() ! {start_listener, 0},
+
+    {reply, ok, State#state{changes_pid = undefined}};
+
 handle_call(_Call, _From, State) ->
     {noreply, State}.
 
@@ -130,6 +144,9 @@ handle_info({'DOWN', _, _, Pid, Reason}, #state{changes_pid=Pid} = State) ->
     {noreply, State#state{last_seq=Seq}};
 handle_info({start_listener, Seq}, State) ->
     {noreply, State#state{changes_pid = spawn_changes(Seq)}};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info(_Msg, State) ->
     {noreply, State}.
 
@@ -181,6 +198,19 @@ changes_callback({timeout, _ResponseType}, Acc) ->
 changes_callback({error, _}, EndSeq) ->
     exit({seq, EndSeq}).
 
+
+handle_config_change("chttpd_auth", "authentication_db", _DbName, _, _) ->
+    {ok, gen_server:call(?MODULE, reinit_cache, infinity)};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
+handle_config_terminate(_, stop, _) ->
+    ok;
+handle_config_terminate(_Server, _Reason, _State) ->
+    Dst = whereis(?MODULE),
+    erlang:send_after(?RELISTEN_DELAY, Dst, restart_config_listener).
+
+
 load_user_from_db(UserName) ->
     {ok, Db} = fabric2_db:open(dbname(), [?ADMIN_CTX]),
     try fabric2_db:open_doc(Db, docid(UserName), [conflicts]) of
@@ -194,6 +224,18 @@ load_user_from_db(UserName) ->
         nil
     end.
 
+
+ensure_auth_db() ->
+    try
+        fabric2_db:open(dbname(), [?ADMIN_CTX])
+    catch error:database_does_not_exist ->
+        case fabric2_db:create(dbname(), [?ADMIN_CTX]) of
+            {ok, _} -> ok;
+            {error, file_exists} -> ok
+        end
+    end.
+
+
 dbname() ->
     DbNameStr = config:get("chttpd_auth", "authentication_db", "_users"),
     iolist_to_binary(DbNameStr).
diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index 43d555c..7114903 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -736,10 +736,14 @@ fold_changes(Db, SinceSeq, UserFun, UserAcc, Options) ->
 maybe_add_sys_db_callbacks(Db) ->
     IsReplicatorDb = fabric2_util:dbname_ends_with(Db, <<"_replicator">>),
 
+    AuthenticationDb = config:get("chttpd_auth", "authentication_db"),
+    IsAuthCache = if AuthenticationDb == undefined -> false; true ->
+        name(Db) == ?l2b(AuthenticationDb)
+    end,
     CfgUsersSuffix = config:get("couchdb", "users_db_suffix", "_users"),
     IsCfgUsersDb = fabric2_util:dbname_ends_with(Db, ?l2b(CfgUsersSuffix)),
     IsGlobalUsersDb = fabric2_util:dbname_ends_with(Db, <<"_users">>),
-    IsUsersDb = IsCfgUsersDb orelse IsGlobalUsersDb,
+    IsUsersDb = IsAuthCache orelse IsCfgUsersDb orelse IsGlobalUsersDb,
 
     {BDU, ADR} = if
         IsReplicatorDb ->
diff --git a/test/elixir/test/security_validation_test.exs b/test/elixir/test/security_validation_test.exs
index 0df3a78..e103314 100644
--- a/test/elixir/test/security_validation_test.exs
+++ b/test/elixir/test/security_validation_test.exs
@@ -53,9 +53,6 @@ defmodule SecurityValidationTest do
     on_exit(fn -> delete_db(auth_db_name) end)
 
     configs = [
-      {"httpd", "authentication_handlers",
-       "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
-      {"couch_httpd_auth", "authentication_db", auth_db_name},
       {"chttpd_auth", "authentication_db", auth_db_name}
     ]
 
@@ -72,6 +69,7 @@ defmodule SecurityValidationTest do
     Enum.each(users, fn {name, pass} ->
       doc = %{
         :_id => "org.couchdb.user:#{name}",
+        :type => "user",
         :name => name,
         :roles => [],
         :password => pass