From 17b5b34e3c2f2077e923113d7e03afecfb674571 Mon Sep 17 00:00:00 2001 From: Marcos de Vera Piquero Date: Fri, 22 Nov 2024 08:30:55 +0100 Subject: [PATCH] feat: support loading Elixir modules for auth Allow to specify an Elixir module name in `auth_method`. If the referenced module, `M`, cannot be loaded as `ejabberd_auth_M`, try to load it as `Elixir.M`. --- lib/ejabberd_auth_example.ex | 39 ++++++++++++++++++++++++++++++++++++ src/econf.erl | 13 ++++++++---- src/ejabberd.erl | 9 +++++++++ src/ejabberd_auth.erl | 2 +- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 lib/ejabberd_auth_example.ex diff --git a/lib/ejabberd_auth_example.ex b/lib/ejabberd_auth_example.ex new file mode 100644 index 000000000..2198b0e4d --- /dev/null +++ b/lib/ejabberd_auth_example.ex @@ -0,0 +1,39 @@ +defmodule ModAuthExample do + @moduledoc """ + + This is a dummy auth module to demonstrate the usage of Elixir to + create Ejabberd Auth modules. + + """ + import Ejabberd.Logger + @behaviour :ejabberd_auth + + @impl true + def start(host) do + info("Using mod_auth_example to authenticate #{host} users") + nil + end + + @impl true + def stop(host) do + info("Stop using mod_auth_example to authenticate #{host} users") + nil + end + + @impl true + def check_password("alice", _authz_id, _host, "secret"), do: {:nocache, true} + def check_password(_username, _authz_id, _host, _secret), do: {:nocache, false} + + @impl true + def user_exists("alice", _host), do: {:nocache, true} + def user_exists(_username, _host), do: {:nocache, false} + + @impl true + def plain_password_required(_binary), do: true + + @impl true + def store_type(_host), do: :external + + @impl true + def use_cache(_host), do: false +end diff --git a/src/econf.erl b/src/econf.erl index 8501356e9..0ffd05e4e 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -505,10 +505,15 @@ db_type(M) -> and_then( atom(), fun(T) -> - case code:ensure_loaded(db_module(M, T)) of - {module, _} -> T; - {error, _} -> fail({bad_db_type, M, T}) - end + case code:ensure_loaded(db_module(M, T)) of + {module, _} -> T; + {error, _} -> + ElixirModule = "Elixir." ++ atom_to_list(T), + case code:ensure_loaded(list_to_atom(ElixirModule)) of + {module, _} -> list_to_atom(ElixirModule); + {error, _} -> fail({bad_db_type, M, T}) + end + end end). -spec queue_type() -> yconf:validator(ram | file). diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 0b23188d9..a251003b3 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -177,6 +177,15 @@ module_name([Dir, _, <> | _] = Mod) when H >= 65, H =< 90 -> Lib -> <<"Elixir.Ejabberd.", Lib/binary, ".">> end, misc:binary_to_atom(<>); + +module_name([<<"auth">> | T] = Mod) -> + case hd(T) of + %% T already starts with "Elixir" if an Elixir module is + %% loaded with that name, as per `econf:db_type/1` + <<"Elixir", _/binary>> -> misc:binary_to_atom(hd(T)); + _ -> module_name([<<"ejabberd">>] ++ Mod) + end; + module_name([<<"ejabberd">> | _] = Mod) -> Module = str:join([erlang_name(M) || M<-Mod], $_), misc:binary_to_atom(Module); diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 3f2b65b3e..998dbe10f 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -893,7 +893,7 @@ auth_modules() -> auth_modules(Server) -> LServer = jid:nameprep(Server), Methods = ejabberd_option:auth_method(LServer), - [ejabberd:module_name([<<"ejabberd">>, <<"auth">>, + [ejabberd:module_name([<<"auth">>, misc:atom_to_binary(M)]) || M <- Methods].