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}
]