You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2026/10/04 17:00:57 UTC

(couchdb) 10/12: Use short names for authentication_handlers

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

nickva pushed a commit to branch 3.5.x-a-few-more-fixes
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit ec7ef3a53a13e08ee7bd07cdc6b9e05d97d98083
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Sat Oct 3 15:52:40 2026 -0400

    Use short names for authentication_handlers
    
    Move away from erlang terms and use simpler names them. For compatibility we
    still accept existing tuple forms. Entries that are not one of these are
    ignored.
    
    Co-Authored-By: Jan Lehnardt <ja...@apache.org>
    Co-Authored-By: Robert Newson <rn...@apache.org>
    (cherry picked from commit af673227cf77a093bf89ce01279525827ca897dd)
---
 rel/overlay/etc/default.ini                   |  8 ++--
 src/chttpd/src/chttpd.erl                     | 46 ++++++++++---------
 src/chttpd/test/eunit/chttpd_auth_tests.erl   |  2 +-
 src/chttpd/test/eunit/chttpd_util_test.erl    |  3 +-
 src/couch/src/couch_httpd.erl                 | 64 ++++++++++++++++++---------
 src/couch/src/couch_httpd_auth.erl            |  4 --
 src/couch/src/couch_util.erl                  |  5 ++-
 src/docs/src/api/server/authn.rst             |  8 ++--
 src/docs/src/api/server/configuration.rst     |  4 +-
 src/docs/src/config/http.rst                  | 14 +++---
 test/elixir/test/auth_cache_test.exs          |  2 +-
 test/elixir/test/config/test-config.ini       |  2 +-
 test/elixir/test/jwt_roles_claim_test.exs     |  6 +--
 test/elixir/test/security_validation_test.exs |  2 +-
 14 files changed, 95 insertions(+), 75 deletions(-)

diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini
index 781e55654..11dab43c0 100644
--- a/rel/overlay/etc/default.ini
+++ b/rel/overlay/etc/default.ini
@@ -234,11 +234,11 @@ bind_address = 127.0.0.1
 ;buffer_response = false
 
 ; authentication handlers
-;authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
+;authentication_handlers = cookie, default
 ; uncomment the next line to enable proxy authentication
-;authentication_handlers = {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
+;authentication_handlers = proxy, cookie, default
 ; uncomment the next line to enable JWT authentication
-;authentication_handlers = {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
+;authentication_handlers = jwt, cookie, default
 
 ; prevent non-admins from accessing /_all_dbs and /_dbs_info
 ;admin_only_all_dbs = true
@@ -366,7 +366,7 @@ bind_address = 127.0.0.1
 [httpd]
 port = {{backend_port}}
 bind_address = 127.0.0.1
-;authentication_handlers = {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}
+;authentication_handlers = cookie, default
 
 ; Options for the MochiWeb HTTP server.
 ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 13fad2ffe..b2df8a1e4 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -648,31 +648,35 @@ extract_cookie(#httpd{mochi_req = MochiReq}) ->
 
 %% erlfmt-ignore
 set_auth_handlers() ->
-    AuthenticationDefault =  "{chttpd_auth, cookie_authentication_handler},
-      {chttpd_auth, default_authentication_handler}",
-    AuthenticationSrcs = couch_httpd:make_fun_spec_strs(
-        config:get("chttpd", "authentication_handlers", AuthenticationDefault)),
-    AuthHandlers = lists:map(
-        fun(A) -> {auth_handler_name(A), couch_httpd:make_arity_1_fun(A)} end, AuthenticationSrcs),
-    AuthenticationFuns = AuthHandlers ++ [
+    AuthenticationHandlers0 = config:get("chttpd", "authentication_handlers", "cookie, default"),
+    AuthenticationHandlers1 = couch_httpd:authentication_handler_match(AuthenticationHandlers0),
+    AuthenticationHandlers2 = lists:filtermap(fun authentication_handler_map/1, AuthenticationHandlers1),
+    AuthenticationFuns = AuthenticationHandlers2 ++ [
         fun chttpd_auth:party_mode_handler/1 %% must be last
     ],
     ok = application:set_env(chttpd, auth_handlers, AuthenticationFuns).
 
-% SpecStr is a string like "{my_module, my_fun}"
-% Takes the first token of the function name in front '_' as auth handler name
-% e.g.
-% chttpd_auth:default_authentication_handler: default
-% chttpd_auth_cookie_authentication_handler: cookie
-% couch_http_auth:proxy_authentication_handler: proxy
-%
-% couch_http:auth_handler_name can't be used here, since it assumes the name
-% of the auth handler to be the 6th token split by [\\W_]
-% - this only works for modules with exactly two underscores in their name
-% - is not very robust (a space after the ',' is assumed)
-auth_handler_name(SpecStr) ->
-    {ok, {_, Fun}} = couch_util:parse_term(SpecStr),
-    hd(binary:split(atom_to_binary(Fun, latin1), <<"_">>)).
+% aliases
+authentication_handler_map("cookie") ->
+    {true, {~"cookie", fun chttpd_auth:cookie_authentication_handler/1}};
+authentication_handler_map("default") ->
+    {true, {~"default", fun chttpd_auth:default_authentication_handler/1}};
+authentication_handler_map("jwt") ->
+    {true, {~"jwt", fun chttpd_auth:jwt_authentication_handler/1}};
+authentication_handler_map("proxy") ->
+    {true, {~"proxy", fun chttpd_auth:proxy_authentication_handler/1}};
+% deprecated
+authentication_handler_map("{chttpd_auth,jwt_authentication_handler}") ->
+    {true, {~"jwt", fun chttpd_auth:jwt_authentication_handler/1}};
+authentication_handler_map("{chttpd_auth,cookie_authentication_handler}") ->
+    {true, {~"cookie", fun chttpd_auth:cookie_authentication_handler/1}};
+authentication_handler_map("{chttpd_auth,default_authentication_handler}") ->
+    {true, {~"default", fun chttpd_auth:default_authentication_handler/1}};
+authentication_handler_map("{chttpd_auth,proxy_authentication_handler}") ->
+    {true, {~"proxy", fun chttpd_auth:proxy_authentication_handler/1}};
+% reject everything else
+authentication_handler_map(_) ->
+    false.
 
 authenticate_request(Req) ->
     {ok, AuthenticationFuns} = application:get_env(chttpd, auth_handlers),
diff --git a/src/chttpd/test/eunit/chttpd_auth_tests.erl b/src/chttpd/test/eunit/chttpd_auth_tests.erl
index 6745bb31c..81291564b 100644
--- a/src/chttpd/test/eunit/chttpd_auth_tests.erl
+++ b/src/chttpd/test/eunit/chttpd_auth_tests.erl
@@ -28,7 +28,7 @@ teardown(_Url) ->
     ok.
 
 setup_proxy_auth() ->
-    {StartCtx, ProxyCfgFile} = start_couch_with_cfg("{chttpd_auth, proxy_authentication_handler}"),
+    {StartCtx, ProxyCfgFile} = start_couch_with_cfg("proxy"),
     config:set("chttpd", "require_valid_user", "false", false),
     config:set("chttpd_auth", "hash_algorithms", ?WORKING_HASHES, false),
     config:set("chttpd_auth", "proxy_use_secret", "true", false),
diff --git a/src/chttpd/test/eunit/chttpd_util_test.erl b/src/chttpd/test/eunit/chttpd_util_test.erl
index c649393a1..dc06e1075 100644
--- a/src/chttpd/test/eunit/chttpd_util_test.erl
+++ b/src/chttpd/test/eunit/chttpd_util_test.erl
@@ -26,8 +26,7 @@ setup() ->
     ok = config:set(
         "httpd",
         "authentication_handlers",
-        "{couch_httpd_auth, cookie_authentication_handler}, "
-        "{couch_httpd_auth, default_authentication_handler}",
+        "cookie, default",
         Persist
     ),
     ok = config:set("chttpd", "require_valid_user", "false", Persist),
diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl
index f38375959..f02bd052b 100644
--- a/src/couch/src/couch_httpd.erl
+++ b/src/couch/src/couch_httpd.erl
@@ -21,7 +21,6 @@
 -export([header_value/2, header_value/3, qs_value/2, qs_value/3, qs/1, qs_json_value/3]).
 -export([path/1, absolute_uri/2, body_length/1]).
 -export([verify_is_server_admin/1, unquote/1, quote/1, recv/2, recv_chunked/4, error_info/1]).
--export([make_fun_spec_strs/1]).
 -export([make_arity_1_fun/1, make_arity_2_fun/1, make_arity_3_fun/1]).
 -export([parse_form/1, json_body/1, json_body_obj/1, body/1]).
 -export([doc_etag/1, doc_etag/3, make_etag/1, etag_match/2, etag_respond/3, etag_maybe/2]).
@@ -46,15 +45,13 @@
 -export([handle_request/1]).
 -export([set_auth_handlers/0]).
 -export([maybe_decompress/2, maybe_decompress/3]).
+-export([authentication_handler_match/1]).
 
 -define(HANDLER_NAME_IN_MODULE_POS, 6).
 -define(MAX_DRAIN_BYTES, 1048576).
 -define(MAX_DRAIN_TIME_MSEC, 1000).
 -define(DEFAULT_SOCKET_OPTIONS, "[{sndbuf, 262144}]").
--define(DEFAULT_AUTHENTICATION_HANDLERS,
-    "{couch_httpd_auth, cookie_authentication_handler}, "
-    "{couch_httpd_auth, default_authentication_handler}"
-).
+-define(DEFAULT_AUTHENTICATION_HANDLERS, "cookie, default").
 
 start_link() ->
     start_link(http).
@@ -185,26 +182,48 @@ stop() ->
     catch mochiweb_http:stop(https).
 
 set_auth_handlers() ->
-    AuthenticationSrcs = make_fun_spec_strs(
-        config:get(
-            "httpd",
-            "authentication_handlers",
-            ?DEFAULT_AUTHENTICATION_HANDLERS
-        )
+    AuthenticationHandlers0 = config:get(
+        "httpd", "authentication_handlers", ?DEFAULT_AUTHENTICATION_HANDLERS
     ),
-    AuthHandlers = lists:map(
-        fun(A) -> {auth_handler_name(A), make_arity_1_fun(A)} end, AuthenticationSrcs
+    AuthenticationHandlers1 = authentication_handler_match(AuthenticationHandlers0),
+    AuthenticationHandlers2 = lists:filtermap(
+        fun authentication_handler_map/1, AuthenticationHandlers1
     ),
     AuthenticationFuns =
-        AuthHandlers ++
+        AuthenticationHandlers2 ++
             [
                 %% must be last
                 fun couch_httpd_auth:party_mode_handler/1
             ],
     ok = application:set_env(couch, auth_handlers, AuthenticationFuns).
 
-auth_handler_name(SpecStr) ->
-    lists:nth(?HANDLER_NAME_IN_MODULE_POS, re:split(SpecStr, "[\\W_]", [])).
+authentication_handler_match(Str) ->
+    case
+        re:run(couch_util:remove_whitespace(Str), "({[a-z0-9_]+,[a-z0-9_]+}|[a-z0-9_]+)", [
+            global, {capture, first, list}
+        ])
+    of
+        {match, Matches} ->
+            [M || [M] <- Matches];
+        nomatch ->
+            []
+    end.
+
+% aliases
+authentication_handler_map("cookie") ->
+    {true, {~"cookie", fun couch_httpd_auth:cookie_authentication_handler/1}};
+authentication_handler_map("default") ->
+    {true, {~"default", fun couch_httpd_auth:default_authentication_handler/1}};
+% deprecated or tests
+authentication_handler_map("{couch_httpd_auth,cookie_authentication_handler}") ->
+    {true, {~"cookie", fun couch_httpd_auth:cookie_authentication_handler/1}};
+authentication_handler_map("{couch_httpd_auth,default_authentication_handler}") ->
+    {true, {~"default", fun couch_httpd_auth:default_authentication_handler/1}};
+authentication_handler_map("{couch_httpd_auth,special_test_authentication_handler}") ->
+    {true, {~"special_test", fun couch_httpd_auth:special_test_authentication_handler/1}};
+% reject everything else
+authentication_handler_map(_) ->
+    false.
 
 get_httpd_handlers() ->
     {ok, HttpdGlobalHandlers} = application:get_env(couch, httpd_global_handlers),
@@ -266,10 +285,6 @@ make_arity_3_fun(SpecStr) ->
             fun(Arg1, Arg2, Arg3) -> Mod:Fun(Arg1, Arg2, Arg3) end
     end.
 
-% SpecStr is "{my_module, my_fun}, {my_module2, my_fun2}"
-make_fun_spec_strs(SpecStr) ->
-    re:split(SpecStr, "(?<=})\\s*,\\s*(?={)", [{return, list}]).
-
 handle_request(MochiReq) ->
     Body = proplists:get_value(body, MochiReq:get(opts)),
     erlang:put(mochiweb_request_body, Body),
@@ -1525,4 +1540,13 @@ should_accept_code_and_message(DontLogFlag) ->
             ?_assertEqual(ok, log_response(500, {json, {[{error, undef}]}}))}
     ]}.
 
+authentication_handler_match_test() ->
+    ?assertEqual("", authentication_handler_match("")),
+    ?assertEqual(["foo", "bar"], authentication_handler_match(" foo , bar ")),
+    ?assertEqual(["foo", "bar"], authentication_handler_match("foo,bar")),
+    ?assertEqual(["{foo,bar}"], authentication_handler_match("{foo,bar}")),
+    ?assertEqual(["{foo,bar}"], authentication_handler_match("{foo, bar}")),
+    ?assertEqual(["foo", "{foo,bar}", "bar"], authentication_handler_match("foo, {foo, bar}, bar")),
+    ?assertEqual(["{foo,bar}", "baz"], authentication_handler_match("{foo , bar}, baz")).
+
 -endif.
diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl
index b55892244..21b96d525 100644
--- a/src/couch/src/couch_httpd_auth.erl
+++ b/src/couch/src/couch_httpd_auth.erl
@@ -23,7 +23,6 @@
     special_test_authentication_handler/1
 ]).
 -export([cookie_authentication_handler/1, cookie_authentication_handler/2]).
--export([null_authentication_handler/1]).
 -export([proxy_authentication_handler/1, proxy_authentification_handler/1]).
 -export([cookie_auth_header/2]).
 -export([handle_session_req/1, handle_session_req/2]).
@@ -154,9 +153,6 @@ default_authentication_handler(Req, AuthModule) ->
             end
     end.
 
-null_authentication_handler(Req) ->
-    Req#httpd{user_ctx = ?ADMIN_USER}.
-
 %% @doc proxy auth handler.
 %
 % This handler allows creation of a userCtx object from a user authenticated remotly.
diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl
index 7e3dffd61..7a82888f2 100644
--- a/src/couch/src/couch_util.erl
+++ b/src/couch/src/couch_util.erl
@@ -15,7 +15,7 @@
 -export([priv_dir/0, normpath/1, fold_files/5]).
 -export([should_flush/0, should_flush/1, to_existing_atom/1]).
 -export([rand32/0, implode/2]).
--export([abs_pathname/1, abs_pathname/2, trim/1, drop_dot_couch_ext/1]).
+-export([abs_pathname/1, abs_pathname/2, trim/1, remove_whitespace/1, drop_dot_couch_ext/1]).
 -export([encodeBase64Url/1, decodeBase64Url/1]).
 -export([validate_utf8/1, to_hex/1, to_hex_bin/1, parse_term/1, dict_find/3]).
 -export([get_nested_json_value/2, json_user_ctx/1]).
@@ -351,6 +351,9 @@ trim(String) ->
     String2 = lists:dropwhile(fun is_whitespace/1, String),
     lists:reverse(lists:dropwhile(fun is_whitespace/1, lists:reverse(String2))).
 
+remove_whitespace(Str) ->
+    re:replace(Str, "\\s+", "", [global, {return, list}]).
+
 drop_dot_couch_ext(DbName) when is_binary(DbName) ->
     PrefixLen = size(DbName) - 6,
     case DbName of
diff --git a/src/docs/src/api/server/authn.rst b/src/docs/src/api/server/authn.rst
index 7b89a87bd..354dbdebc 100644
--- a/src/docs/src/api/server/authn.rst
+++ b/src/docs/src/api/server/authn.rst
@@ -283,13 +283,13 @@ Proxy Authentication
 
 .. note::
     To use this authentication method make sure that the
-    ``{chttpd_auth, proxy_authentication_handler}`` value is added to the
+    ``proxy`` value is added to the
     list of the active :config:option:`chttpd/authentication_handlers`:
 
     .. code-block:: ini
 
         [chttpd]
-        authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, default_authentication_handler}
+        authentication_handlers = cookie, proxy, default
 
 `Proxy authentication` is very useful in case your application already uses
 some external authentication service and you don't want to duplicate users and
@@ -372,13 +372,13 @@ JWT Authentication
 
 .. note::
     To use this authentication method, make sure that the
-    ``{chttpd_auth, jwt_authentication_handler}`` value is added to the
+    ``jwt`` value is added to the
     list of the active :config:option:`chttpd/authentication_handlers`:
 
     .. code-block:: ini
 
         [chttpd]
-        authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, default_authentication_handler}
+        authentication_handlers = cookie, jwt, default
 
 ``JWT authentication`` enables CouchDB to use externally-generated JWT tokens
 instead of defining users or roles in the ``_users`` database.
diff --git a/src/docs/src/api/server/configuration.rst b/src/docs/src/api/server/configuration.rst
index d05bdd8af..b938c422d 100644
--- a/src/docs/src/api/server/configuration.rst
+++ b/src/docs/src/api/server/configuration.rst
@@ -88,7 +88,7 @@ interact with the local node's configuration.
                 "secure_rewrites": "true"
             },
             "httpd": {
-                "authentication_handlers": "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}",
+                "authentication_handlers": "cookie, default",
                 "bind_address": "192.168.0.2",
                 "max_connections": "2048",
                 "port": "5984",
@@ -156,7 +156,7 @@ interact with the local node's configuration.
         Server: CouchDB (Erlang/OTP)
 
         {
-            "authentication_handlers": "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}",
+            "authentication_handlers": "cookie, default",
             "bind_address": "127.0.0.1",
             "default_handler": "{couch_httpd_db, handle_request}",
             "port": "5984"
diff --git a/src/docs/src/config/http.rst b/src/docs/src/config/http.rst
index 1ea18f204..e14a97444 100644
--- a/src/docs/src/config/http.rst
+++ b/src/docs/src/config/http.rst
@@ -81,17 +81,15 @@ HTTP Server Options
     .. config:option:: authentication_handlers :: Authentication handlers
 
         List of authentication handlers used by CouchDB. You may
-        extend them via third-party plugins or remove some of them if you won't
-        let users to use one of provided methods::
+        remove some of them if you won't let users to use one of provided methods::
 
             [chttpd]
-            authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
+            authentication_handlers = cookie, default
 
-        - ``{chttpd_auth, cookie_authentication_handler}``: used for Cookie auth;
-        - ``{chttpd_auth, proxy_authentication_handler}``: used for Proxy auth;
-        - ``{chttpd_auth, jwt_authentication_handler}``: used for JWT auth;
-        - ``{chttpd_auth, default_authentication_handler}``: used for Basic auth;
-        - ``{couch_httpd_auth, null_authentication_handler}``: disables auth, breaks CouchDB.
+        - ``cookie``: used for Cookie auth;
+        - ``proxy``: used for Proxy auth;
+        - ``jwt``: used for JWT auth;
+        - ``default``: used for Basic auth.
 
     .. config:option:: buffer_response :: Buffered response
 
diff --git a/test/elixir/test/auth_cache_test.exs b/test/elixir/test/auth_cache_test.exs
index 8b7c29c71..62d08ad9b 100644
--- a/test/elixir/test/auth_cache_test.exs
+++ b/test/elixir/test/auth_cache_test.exs
@@ -22,7 +22,7 @@ defmodule AuthCacheTest do
       %{
         :section => "httpd",
         :key => "authentication_handlers",
-        :value => "{couch_httpd_auth, default_authentication_handler}"
+        :value => "default"
       },
       %{
         :section => "chttpd_auth",
diff --git a/test/elixir/test/config/test-config.ini b/test/elixir/test/config/test-config.ini
index 423cc492c..9baf53a70 100644
--- a/test/elixir/test/config/test-config.ini
+++ b/test/elixir/test/config/test-config.ini
@@ -2,7 +2,7 @@
 level = warn
 
 [chttpd]
-authentication_handlers = {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
+authentication_handlers = jwt, proxy, cookie, default
 
 [dreyfus]
 name = clouseau@127.0.0.1
diff --git a/test/elixir/test/jwt_roles_claim_test.exs b/test/elixir/test/jwt_roles_claim_test.exs
index ec3c7e53e..c3ff93e8a 100644
--- a/test/elixir/test/jwt_roles_claim_test.exs
+++ b/test/elixir/test/jwt_roles_claim_test.exs
@@ -5,11 +5,7 @@ defmodule JwtRolesClaimTest do
     %{
       :section => "chttpd",
       :key => "authentication_handlers",
-      :value => [
-                  "{chttpd_auth, jwt_authentication_handler}, ",
-                  "{chttpd_auth, cookie_authentication_handler}, ",
-                  "{chttpd_auth, default_authentication_handler})"
-                ] |> Enum.join
+      :value => "jwt, cookie, default"
     },
     %{
       :section => "jwt_keys",
diff --git a/test/elixir/test/security_validation_test.exs b/test/elixir/test/security_validation_test.exs
index 8834e17f5..7120a4a69 100644
--- a/test/elixir/test/security_validation_test.exs
+++ b/test/elixir/test/security_validation_test.exs
@@ -61,7 +61,7 @@ defmodule SecurityValidationTest do
 
     configs = [
       {"chttpd", "authentication_handlers",
-       "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
+       "cookie, default"},
       {"chttpd_auth", "authentication_db", auth_db_name}
     ]