summaryrefslogtreecommitdiff
path: root/src/chttpd/src/chttpd_auth_cache.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/chttpd/src/chttpd_auth_cache.erl')
-rw-r--r--src/chttpd/src/chttpd_auth_cache.erl86
1 files changed, 71 insertions, 15 deletions
diff --git a/src/chttpd/src/chttpd_auth_cache.erl b/src/chttpd/src/chttpd_auth_cache.erl
index fdae27b79..c5a56bddb 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,
@@ -52,7 +55,8 @@ get_user_creds(_Req, UserName) when is_binary(UserName) ->
update_user_creds(_Req, UserDoc, _Ctx) ->
{_, Ref} = spawn_monitor(fun() ->
- case fabric:update_doc(dbname(), UserDoc, []) of
+ {ok, Db} = fabric2_db:open(dbname(), [?ADMIN_CTX]),
+ case fabric2_db:update_doc(Db, UserDoc) of
{ok, _} ->
exit(ok);
Else ->
@@ -100,9 +104,28 @@ maybe_increment_auth_cache_miss(UserName) ->
%% gen_server callbacks
init([]) ->
+ 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}.
@@ -124,6 +147,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}.
@@ -142,7 +168,8 @@ spawn_changes(Since) ->
Pid.
listen_for_changes(Since) ->
- ensure_auth_ddoc_exists(dbname(), <<"_design/_auth">>),
+ {ok, Db} = fabric2_db:open(dbname(), [?ADMIN_CTX]),
+ ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
CBFun = fun ?MODULE:changes_callback/2,
Args = #changes_args{
feed = "continuous",
@@ -150,7 +177,8 @@ listen_for_changes(Since) ->
heartbeat = true,
filter = {default, main_only}
},
- fabric:changes(dbname(), CBFun, Since, Args).
+ ChangesFun = chttpd_changes:handle_db_changes(Args, nil, Db),
+ ChangesFun({CBFun, Since}).
changes_callback(waiting_for_updates, Acc) ->
{ok, Acc};
@@ -159,7 +187,7 @@ changes_callback(start, Since) ->
changes_callback({stop, EndSeq, _Pending}, _) ->
exit({seq, EndSeq});
changes_callback({change, {Change}}, _) ->
- case couch_util:get_value(id, Change) of
+ case couch_util:get_value(<<"id">>, Change) of
<<"_design/", _/binary>> ->
ok;
DocId ->
@@ -168,13 +196,27 @@ changes_callback({change, {Change}}, _) ->
ets_lru:remove(?CACHE, UserName)
end,
{ok, couch_util:get_value(seq, Change)};
-changes_callback(timeout, Acc) ->
+changes_callback({timeout, _ResponseType}, Acc) ->
{ok, 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) ->
- try fabric:open_doc(dbname(), docid(UserName), [?ADMIN_CTX, ejson_body, conflicts]) of
+ {ok, Db} = fabric2_db:open(dbname(), [?ADMIN_CTX]),
+ try fabric2_db:open_doc(Db, docid(UserName), [conflicts]) of
{ok, Doc} ->
{Props} = couch_doc:to_json_obj(Doc, []),
Props;
@@ -185,8 +227,21 @@ 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() ->
- config:get("chttpd_auth", "authentication_db", "_users").
+ DbNameStr = config:get("chttpd_auth", "authentication_db", "_users"),
+ iolist_to_binary(DbNameStr).
docid(UserName) ->
<<"org.couchdb.user:", UserName/binary>>.
@@ -194,11 +249,11 @@ docid(UserName) ->
username(<<"org.couchdb.user:", UserName/binary>>) ->
UserName.
-ensure_auth_ddoc_exists(DbName, DDocId) ->
- case fabric:open_doc(DbName, DDocId, [?ADMIN_CTX, ejson_body]) of
+ensure_auth_ddoc_exists(Db, DDocId) ->
+ case fabric2_db:open_doc(Db, DDocId) of
{not_found, _Reason} ->
{ok, AuthDesign} = couch_auth_cache:auth_design_doc(DDocId),
- update_doc_ignoring_conflict(DbName, AuthDesign, [?ADMIN_CTX]);
+ update_doc_ignoring_conflict(Db, AuthDesign);
{ok, Doc} ->
{Props} = couch_doc:to_json_obj(Doc, []),
case couch_util:get_value(<<"validate_doc_update">>, Props, []) of
@@ -208,19 +263,20 @@ ensure_auth_ddoc_exists(DbName, DDocId) ->
Props1 = lists:keyreplace(<<"validate_doc_update">>, 1, Props,
{<<"validate_doc_update">>,
?AUTH_DB_DOC_VALIDATE_FUNCTION}),
- update_doc_ignoring_conflict(DbName, couch_doc:from_json_obj({Props1}), [?ADMIN_CTX])
+ NewDoc = couch_doc:from_json_obj({Props1}),
+ update_doc_ignoring_conflict(Db, NewDoc)
end;
{error, Reason} ->
- couch_log:notice("Failed to ensure auth ddoc ~s/~s exists for reason: ~p", [DbName, DDocId, Reason]),
+ couch_log:notice("Failed to ensure auth ddoc ~s/~s exists for reason: ~p", [dbname(), DDocId, Reason]),
ok
end,
ok.
-update_doc_ignoring_conflict(DbName, Doc, Options) ->
+update_doc_ignoring_conflict(DbName, Doc) ->
try
- fabric:update_doc(DbName, Doc, Options)
+ fabric2_db:update_doc(DbName, Doc)
catch
- throw:conflict ->
+ error:conflict ->
ok
end.