You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2014/06/02 13:09:05 UTC

chttpd commit: updated refs/heads/master to 018837c

Repository: couchdb-chttpd
Updated Branches:
  refs/heads/master 002a90813 -> 018837c95


Add auth cache for clustered _users


Project: http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/commit/018837c9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/tree/018837c9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/diff/018837c9

Branch: refs/heads/master
Commit: 018837c957b20786cb1ecba80e1af33c31128890
Parents: 002a908
Author: Robert Newson <rn...@apache.org>
Authored: Mon Jun 2 11:58:54 2014 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Jun 2 12:00:07 2014 +0100

----------------------------------------------------------------------
 src/chttpd.app.src        |   6 +-
 src/chttpd.erl            |   6 +-
 src/chttpd_auth.erl       |  26 ++++++++
 src/chttpd_auth_cache.erl | 138 +++++++++++++++++++++++++++++++++++++++++
 src/chttpd_sup.erl        |  26 +++++++-
 5 files changed, 197 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/blob/018837c9/src/chttpd.app.src
----------------------------------------------------------------------
diff --git a/src/chttpd.app.src b/src/chttpd.app.src
index 3ce0c72..f24716d 100644
--- a/src/chttpd.app.src
+++ b/src/chttpd.app.src
@@ -16,6 +16,7 @@
     {modules, [
         chttpd,
         chttpd_app,
+        chttpd_auth_cache,
         chttpd_config_listener,
         chttpd_db,
         chttpd_external,
@@ -27,7 +28,9 @@
     ]},
     {registered, [
         chttpd_sup,
-        chttpd
+        chttpd,
+        chttpd_auth_cache,
+        chttpd_auth_cache_lru
     ]},
     {applications, [
         kernel,
@@ -35,6 +38,7 @@
         couch_log,
         config,
         couch,
+        ets_lru,
         fabric
     ]},
     {mod, {chttpd_app,[]}}

http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/blob/018837c9/src/chttpd.erl
----------------------------------------------------------------------
diff --git a/src/chttpd.erl b/src/chttpd.erl
index 8618811..c556fa7 100644
--- a/src/chttpd.erl
+++ b/src/chttpd.erl
@@ -132,8 +132,8 @@ handle_request(MochiReq) ->
     end,
 
     AuthenticationFuns = [
-        fun couch_httpd_auth:cookie_authentication_handler/1,
-        fun couch_httpd_auth:default_authentication_handler/1
+        fun chttpd_auth:cookie_authentication_handler/1,
+        fun chttpd_auth:default_authentication_handler/1
     ],
 
     % for the path, use the raw path with the query string and fragment
@@ -357,7 +357,7 @@ url_handler("_replicate") ->    fun chttpd_misc:handle_replicate_req/1;
 url_handler("_uuids") ->        fun chttpd_misc:handle_uuids_req/1;
 url_handler("_log") ->          fun chttpd_misc:handle_log_req/1;
 url_handler("_sleep") ->        fun chttpd_misc:handle_sleep_req/1;
-url_handler("_session") ->      fun couch_httpd_auth:handle_session_req/1;
+url_handler("_session") ->      fun chttpd_auth:handle_session_req/1;
 url_handler("_oauth") ->        fun couch_httpd_oauth:handle_oauth_req/1;
 url_handler("_up") ->           fun chttpd_misc:handle_up_req/1;
 url_handler("_membership") ->   fun mem3_httpd:handle_membership_req/1;

http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/blob/018837c9/src/chttpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/chttpd_auth.erl b/src/chttpd_auth.erl
new file mode 100644
index 0000000..1046538
--- /dev/null
+++ b/src/chttpd_auth.erl
@@ -0,0 +1,26 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License.  You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(chttpd_auth).
+
+-export([default_authentication_handler/1]).
+-export([cookie_authentication_handler/1]).
+-export([handle_session_req/1]).
+
+default_authentication_handler(Req) ->
+    couch_httpd_auth:default_authentication_handler(Req, chttpd_auth_cache).
+
+cookie_authentication_handler(Req) ->
+    couch_httpd_auth:cookie_authentication_handler(Req, chttpd_auth_cache).
+
+handle_session_req(Req) ->
+    couch_httpd_auth:handle_session_req(Req, chttpd_auth_cache).

http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/blob/018837c9/src/chttpd_auth_cache.erl
----------------------------------------------------------------------
diff --git a/src/chttpd_auth_cache.erl b/src/chttpd_auth_cache.erl
new file mode 100644
index 0000000..e0c403b
--- /dev/null
+++ b/src/chttpd_auth_cache.erl
@@ -0,0 +1,138 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License.  You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(chttpd_auth_cache).
+-behaviour(gen_server).
+
+-export([start_link/0, get_user_creds/1]).
+-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]).
+
+-include_lib("couch/include/couch_db.hrl").
+
+-define(ADMIN_CTX, {user_ctx, #user_ctx{roles = [<<"_admin">>]}}).
+-define(CACHE, chttpd_auth_cache_lru).
+
+-record(state, {
+    changes_pid,
+    last_seq="0"
+}).
+
+%% public functions
+
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+get_user_creds(UserName) when is_list(UserName) ->
+    get_user_creds(?l2b(UserName));
+get_user_creds(UserName) when is_binary(UserName) ->
+    try ets_lru:lookup_d(?CACHE, UserName) of
+	{ok, Props} ->
+	    couch_stats_collector:increment({chttpd, auth_cache_hits}),
+	    couch_log:debug("cache hit for ~s", [UserName]),
+	    Props;
+	_ ->
+	    Props = load_user_from_db(UserName),
+	    couch_stats_collector:increment({chttpd, auth_cache_misses}),
+	    couch_log:debug("cache miss for ~s", [UserName]),
+	    ets_lru:insert(?CACHE, UserName, Props),
+	    Props
+    catch
+	error:badarg ->
+	    couch_stats_collector:increment({chttpd, auth_cache_misses}),
+	    couch_log:debug("cache miss for ~s", [UserName]),
+	    load_user_from_db(UserName)
+    end.
+
+%% gen_server callbacks
+
+init([]) ->
+    {ok, #state{changes_pid = spawn_changes(0)}}.
+
+handle_call(_Call, _From, State) ->
+    {noreply, State}.
+
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+handle_info({'DOWN', _, _, Pid, Reason}, #state{changes_pid=Pid} = State) ->
+    Seq = case Reason of
+        {seq, EndSeq} ->
+            EndSeq;
+        _ ->
+            couch_log:notice("~p changes listener died ~p", [?MODULE, Reason]),
+            0
+    end,
+    erlang:send_after(5000, self(), {start_listener, Seq}),
+    {noreply, State#state{last_seq=Seq}};
+handle_info({start_listener, Seq}, State) ->
+    {noreply, State#state{changes_pid = spawn_changes(Seq)}};
+handle_info(_Msg, State) ->
+    {noreply, State}.
+
+terminate(_Reason, #state{changes_pid = Pid}) ->
+    exit(Pid, kill).
+
+code_change(_OldVsn, #state{}=State, _Extra) ->
+    {ok, State}.
+
+%% private functions
+
+spawn_changes(Since) ->
+    {Pid, _} = spawn_monitor(?MODULE, listen_for_changes, [Since]),
+    Pid.
+
+listen_for_changes(Since) ->
+    CBFun = fun ?MODULE:changes_callback/2,
+    Args = #changes_args{
+        feed = "continuous",
+        since = Since,
+        heartbeat = true,
+        filter = {default, main_only}
+    },
+    fabric:changes(dbname(), CBFun, Since, Args).
+
+changes_callback(start, Since) ->
+    {ok, Since};
+changes_callback({stop, EndSeq, _Pending}, _) ->
+    exit({seq, EndSeq});
+changes_callback({change, {Change}}, _) ->
+    UserName = username(couch_util:get_value(id, Change)),
+    couch_log:debug("Invalidating cached credentials for ~s", [UserName]),
+    ets_lru:remove(?CACHE, UserName),
+    {ok, couch_util:get_value(seq, Change)};
+changes_callback(timeout, EndSeq) ->
+    exit({seq, EndSeq});
+changes_callback({error, _}, EndSeq) ->
+    exit({seq, EndSeq}).
+
+load_user_from_db(UserName) ->
+    try fabric:open_doc(dbname(), docid(UserName), [?ADMIN_CTX, ejson_body]) of
+	{ok, Doc} ->
+	    {Props} = couch_doc:to_json_obj(Doc, []),
+	    Props;
+	_Else ->
+	    couch_log:warning("no record of user ~s", [UserName]),
+	    nil
+    catch error:database_does_not_exist ->
+	    nil
+    end.
+
+dbname() ->
+    config:get("chttpd_auth", "authentication_db", "_users").
+
+docid(UserName) ->
+    <<"org.couchdb.user:", UserName/binary>>.
+
+username(<<"org.couchdb.user:", UserName/binary>>) ->
+    UserName.

http://git-wip-us.apache.org/repos/asf/couchdb-chttpd/blob/018837c9/src/chttpd_sup.erl
----------------------------------------------------------------------
diff --git a/src/chttpd_sup.erl b/src/chttpd_sup.erl
index 69283a9..498491a 100644
--- a/src/chttpd_sup.erl
+++ b/src/chttpd_sup.erl
@@ -25,5 +25,29 @@ start_link(Args) ->
 init([]) ->
     {ok, {{one_for_one, 3, 10}, [
         ?CHILD(chttpd, worker),
-        ?CHILD(chttpd_config_listener, worker)
+        ?CHILD(chttpd_config_listener, worker),
+        ?CHILD(chttpd_auth_cache, worker),
+        {chttpd_auth_cache_lru,
+	 {ets_lru, start_link, [chttpd_auth_cache_lru, lru_opts()]},
+	 permanent, 5000, worker, [ets_lru]}
     ]}}.
+
+lru_opts() ->
+    case config:get("chttpd_auth_cache", "max_objects") of
+        MxObjs when is_integer(MxObjs), MxObjs > 0 ->
+            [{max_objects, MxObjs}];
+        _ ->
+            []
+    end ++
+    case config:get("chttpd_auth_cache", "max_size", "104857600") of
+        MxSize when is_integer(MxSize), MxSize > 0 ->
+            [{max_size, MxSize}];
+        _ ->
+            []
+    end ++
+    case config:get("chttpd_auth_cache", "max_lifetime", "600000") of
+        MxLT when is_integer(MxLT), MxLT > 0 ->
+            [{max_lifetime, MxLT}];
+        _ ->
+            []
+    end.