Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anonymous auth supports mnesia and cets backends #4148

Merged
merged 2 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions big_tests/test.config
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@
{muc_online_backend, cets},
{jingle_sip_backend, cets},
{keystore_backend, cets},
{auth_anonymous_backend, cets},
{auth_method, "rdbms"},
{internal_databases, "[internal_databases.cets]
cluster_name = \"{{cluster_name}}\""},
Expand Down
7 changes: 7 additions & 0 deletions doc/authentication-methods/anonymous.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ Specifies the SASL mechanisms supported by the `anonymous` authentication method
* `login_anon` - support the non-anonymous mechanisms (`PLAIN`, `DIGEST-MD5`, `SCRAM-*`),
* `both` - support both types of mechanisms.

### `auth.anonymous.backend`
* **Syntax:** string, one of `mnesia`, `cets`
* **Default:** `mnesia`
* **Example:** `backend = cets`

Sets the backend where anonymous sessions will be stored in-memory. See [internal databases](../configuration/internal-databases.md)

### Example

```toml
Expand Down
3 changes: 3 additions & 0 deletions rel/mim1.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
[host_config.auth.anonymous]
allow_multiple_connections = true
protocol = \"both\"
{{#auth_anonymous_backend}}
backend = \"{{{auth_anonymous_backend}}}\"
{{/auth_anonymous_backend}}

[[host_config]]
host_type = \"anonymous\"
Expand Down
10 changes: 10 additions & 0 deletions rel/mim2.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@

{host_config,
"[[host_config]]
host = \"anonymous.localhost\"

[host_config.auth.anonymous]
allow_multiple_connections = true
protocol = \"both\"
{{#auth_anonymous_backend}}
backend = \"{{{auth_anonymous_backend}}}\"
{{/auth_anonymous_backend}}

[[host_config]]
host_type = \"dummy auth\"
[host_config.modules.mod_presence]
[host_config.auth.dummy]"}.
Expand Down
11 changes: 11 additions & 0 deletions rel/mim3.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,16 @@
{c2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}.
{s2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}.

{host_config,
"[[host_config]]
host = \"anonymous.localhost\"

[host_config.auth.anonymous]
allow_multiple_connections = true
protocol = \"both\"
{{#auth_anonymous_backend}}
backend = \"{{{auth_anonymous_backend}}}\"
{{/auth_anonymous_backend}}
"}.
%% Include common vars shared by all profiles
"./vars-toml.config".
70 changes: 25 additions & 45 deletions src/auth/ejabberd_auth_anonymous.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,27 +58,20 @@
-include("session.hrl").
-include("mongoose_config_spec.hrl").

-record(anonymous, {us :: jid:simple_bare_jid(),
sid :: ejabberd_sm:sid()
}).

%% @doc Create the anonymous table if at least one host type has anonymous
%% features enabled. Register to login / logout events
-spec start(mongooseim:host_type()) -> ok.
start(HostType) ->
%% TODO: Check cluster mode
%% TODO: Add CETS backend, use mongoose_mnesia
mnesia:create_table(anonymous,
[{ram_copies, [node()]}, {type, bag},
{attributes, record_info(fields, anonymous)}]),
mnesia:add_table_copy(anonymous, node(), ram_copies),
ejabberd_auth_anonymous_backend:init(HostType),
%% The hooks are needed to add / remove users from the anonymous tables
gen_hook:add_handlers(hooks(HostType)),
ok.

-spec stop(mongooseim:host_type()) -> ok.
stop(HostType) ->
gen_hook:delete_handlers(hooks(HostType)),
ejabberd_auth_anonymous_backend:stop(HostType),

Check warning on line 74 in src/auth/ejabberd_auth_anonymous.erl

View check run for this annotation

Codecov / codecov/patch

src/auth/ejabberd_auth_anonymous.erl#L74

Added line #L74 was not covered by tests
ok.

-spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
Expand All @@ -92,11 +85,13 @@
-spec config_spec() -> mongoose_config_spec:config_section().
config_spec() ->
#section{
items = #{<<"allow_multiple_connections">> => #option{type = boolean},
items = #{<<"backend">> => #option{type = atom, validate = {module, ?MODULE}},
<<"allow_multiple_connections">> => #option{type = boolean},
<<"protocol">> => #option{type = atom,
validate = {enum, [sasl_anon, login_anon, both]}}
},
defaults = #{<<"allow_multiple_connections">> => false,
defaults = #{<<"backend">> => mnesia,
<<"allow_multiple_connections">> => false,
<<"protocol">> => sasl_anon}
}.

Expand All @@ -106,34 +101,21 @@
allow_multiple_connections(HostType) ->
mongoose_config:get_opt([{auth, HostType}, anonymous, allow_multiple_connections]).

does_user_exist(_, LUser, LServer) ->
does_anonymous_user_exist(LUser, LServer).
does_user_exist(HostType, LUser, LServer) ->
does_anonymous_user_exist(HostType, LUser, LServer).

%% @doc Check if user exist in the anonymous database
-spec does_anonymous_user_exist(LUser :: jid:luser(),
LServer :: jid:lserver()) -> boolean().
does_anonymous_user_exist(LUser, LServer) ->
-spec does_anonymous_user_exist(mongooseim:host_type(), jid:luser(), jid:lserver()) -> boolean().
does_anonymous_user_exist(HostType, LUser, LServer) ->
US = {LUser, LServer},
case catch mnesia:dirty_read({anonymous, US}) of
[] ->
false;
[_H|_T] ->
true
end.

ejabberd_auth_anonymous_backend:does_anonymous_user_exist(HostType, US).

%% @doc Remove connection from Mnesia tables
-spec remove_connection(SID :: ejabberd_sm:sid(),
LUser :: jid:luser(),
LServer :: jid:lserver()
) -> {atomic|aborted|error, _}.
remove_connection(SID, LUser, LServer) ->
-spec remove_connection(
mongooseim:host_type(), ejabberd_sm:sid(), jid:luser(), jid:lserver()) -> ok.
remove_connection(HostType, SID, LUser, LServer) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete_object({anonymous, US, SID})
end,
mnesia:transaction(F).

ejabberd_auth_anonymous_backend:remove_connection(HostType, SID, US).

%% @doc Register connection
-spec register_connection(Acc, Params, Extra) -> {ok, Acc} when
Expand All @@ -149,7 +131,7 @@
AuthModule =:= cyrsasl_anonymous -> % sasl_anon
mongoose_hooks:register_user(HostType, LServer, LUser),
US = {LUser, LServer},
mnesia:sync_dirty(fun() -> mnesia:write(#anonymous{us = US, sid = SID}) end),
ejabberd_auth_anonymous_backend:add_connection(HostType, SID, US),
{ok, Acc};
register_connection(Acc, _Params, _Extra) ->
{ok, Acc}.
Expand All @@ -159,11 +141,9 @@
Acc :: mongoose_acc:t(),
Params :: map(),
Extra :: map().
unregister_connection(Acc, #{sid := SID, jid := #jid{luser = LUser, lserver = LServer}}, _Extra) ->
purge_hook(does_anonymous_user_exist(LUser, LServer),
mongoose_acc:host_type(Acc),
LUser, LServer),
remove_connection(SID, LUser, LServer),
unregister_connection(Acc, #{sid := SID, jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
purge_hook(does_anonymous_user_exist(HostType, LUser, LServer), HostType, LUser, LServer),
remove_connection(HostType, SID, LUser, LServer),
{ok, Acc}.

%% @doc Launch the hook to purge user data only for anonymous users.
Expand All @@ -181,8 +161,8 @@
Acc :: mongoose_acc:t(),
Params :: map(),
Extra :: map().
session_cleanup(Acc, #{sid := SID, jid := #jid{luser = LUser, lserver = LServer}}, _Extra) ->
remove_connection(SID, LUser, LServer),
session_cleanup(Acc, #{sid := SID, jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
remove_connection(HostType, SID, LUser, LServer),
{ok, Acc}.

%% ---------------------------------
Expand Down Expand Up @@ -222,7 +202,7 @@
case is_protocol_enabled(HostType, login_anon) of
false -> false;
true ->
case does_anonymous_user_exist(LUser, LServer) of
case does_anonymous_user_exist(HostType, LUser, LServer) of
%% Reject the login if an anonymous user with the same login
%% is already logged and if multiple login has not been enable
%% in the config file.
Expand All @@ -239,8 +219,8 @@
LUser :: jid:luser(),
LServer :: jid:lserver(),
Password :: binary()) -> ok | {error, not_allowed}.
set_password(_HostType, LUser, LServer, _Password) ->
case does_anonymous_user_exist(LUser, LServer) of
set_password(HostType, LUser, LServer, _Password) ->
case does_anonymous_user_exist(HostType, LUser, LServer) of

Check warning on line 223 in src/auth/ejabberd_auth_anonymous.erl

View check run for this annotation

Codecov / codecov/patch

src/auth/ejabberd_auth_anonymous.erl#L223

Added line #L223 was not covered by tests
true ->
ok;
false ->
Expand All @@ -257,7 +237,7 @@
LUser :: jid:luser(),
LServer :: jid:lserver()) -> binary() | false.
get_password(HostType, LUser, LServer) ->
case does_anonymous_user_exist(LUser, LServer) orelse login(HostType, LUser, LServer) of
case does_anonymous_user_exist(HostType, LUser, LServer) orelse login(HostType, LUser, LServer) of

Check warning on line 240 in src/auth/ejabberd_auth_anonymous.erl

View check run for this annotation

Codecov / codecov/patch

src/auth/ejabberd_auth_anonymous.erl#L240

Added line #L240 was not covered by tests
%% We return the default value if the user is anonymous
true ->
<<>>;
Expand Down
41 changes: 41 additions & 0 deletions src/auth/ejabberd_auth_anonymous_backend.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-module(ejabberd_auth_anonymous_backend).

-export([init/1, stop/1, does_anonymous_user_exist/2, remove_connection/3, add_connection/3]).

-define(MAIN_MODULE, ejabberd_auth_anonymous).

-callback init(mongooseim:host_type()) -> ok.

-callback stop(mongooseim:host_type()) -> ok.

-callback does_anonymous_user_exist(mongooseim:host_type(), jid:simple_bare_jid()) -> boolean().

-callback remove_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.

-callback add_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.

-spec init(mongooseim:host_type()) -> ok.
init(HostType) ->
TrackedFuns = [does_anonymous_user_exist],
Backend = mongoose_config:get_opt([{auth, HostType}, anonymous, backend]),
mongoose_backend:init(HostType, ?MAIN_MODULE, TrackedFuns, #{backend => Backend}),
mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, [HostType]).

-spec stop(mongooseim:host_type()) -> ok.
stop(HostType) ->
mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, [HostType]).

Check warning on line 26 in src/auth/ejabberd_auth_anonymous_backend.erl

View check run for this annotation

Codecov / codecov/patch

src/auth/ejabberd_auth_anonymous_backend.erl#L26

Added line #L26 was not covered by tests

-spec does_anonymous_user_exist(mongooseim:host_type(), jid:simple_bare_jid()) -> boolean().
does_anonymous_user_exist(HostType, US) ->
Args = [HostType, US],
mongoose_backend:call_tracked(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args).

-spec add_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.
add_connection(HostType, SID, US) ->
Args = [HostType, SID, US],
mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args).

-spec remove_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.
remove_connection(HostType, SID, US) ->
Args = [HostType, SID, US],
mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args).
37 changes: 37 additions & 0 deletions src/auth/ejabberd_auth_anonymous_cets.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
-module(ejabberd_auth_anonymous_cets).

-behaviour(ejabberd_auth_anonymous_backend).

-export([init/1, stop/1, does_anonymous_user_exist/2, add_connection/3, remove_connection/3]).

-spec init(mongooseim:host_type()) -> ok.
init(HostType) ->
Tab = table_name(HostType),
cets:start(Tab, #{type => bag}),
cets_discovery:add_table(mongoose_cets_discovery, Tab),
ok.

-spec does_anonymous_user_exist(mongooseim:host_type(), jid:simple_bare_jid()) -> boolean().
does_anonymous_user_exist(HostType, US) ->
Tab = table_name(HostType),
[] =/= ets:lookup(Tab, US).

-spec add_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.
add_connection(HostType, SID, US) ->
Tab = table_name(HostType),
cets:insert(Tab, {US, SID}).

-spec remove_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.
remove_connection(HostType, SID, US) ->
Tab = table_name(HostType),
cets:delete_object(Tab, {US, SID}).

-spec stop(mongooseim:host_type()) -> ok.
stop(HostType) ->
Tab = table_name(HostType),
cets_discovery:delete_table(mongoose_cets_discovery, Tab),
cets:stop(Tab),
ok.

Check warning on line 34 in src/auth/ejabberd_auth_anonymous_cets.erl

View check run for this annotation

Codecov / codecov/patch

src/auth/ejabberd_auth_anonymous_cets.erl#L31-L34

Added lines #L31 - L34 were not covered by tests

table_name(HostType) ->
binary_to_atom(<<"cets_auth_anonymous_", HostType/binary>>).
32 changes: 32 additions & 0 deletions src/auth/ejabberd_auth_anonymous_mnesia.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-module(ejabberd_auth_anonymous_mnesia).

-behaviour(ejabberd_auth_anonymous_backend).

-export([init/1, stop/1, does_anonymous_user_exist/2, add_connection/3, remove_connection/3]).

-record(anonymous, {us :: jid:simple_bare_jid(), sid :: ejabberd_sm:sid()}).

-spec init(mongooseim:host_type()) -> ok.
init(_HostType) ->
mnesia:create_table(anonymous,
[{ram_copies, [node()]}, {type, bag},
{attributes, record_info(fields, anonymous)}]),
mnesia:add_table_copy(anonymous, node(), ram_copies),
ok.

-spec does_anonymous_user_exist(mongooseim:host_type(), jid:simple_bare_jid()) -> boolean().
does_anonymous_user_exist(_HostType, US) ->
[] =/= catch mnesia:dirty_read({anonymous, US}).

-spec add_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.
add_connection(_HostType, SID, US) ->
mnesia:sync_dirty(fun() -> mnesia:write(#anonymous{us = US, sid = SID}) end).

-spec remove_connection(mongooseim:host_type(), ejabberd_sm:sid(), jid:simple_bare_jid()) -> ok.
remove_connection(_HostType, SID, US) ->
mnesia:transaction(fun() -> mnesia:delete_object({anonymous, US, SID}) end),
ok.

-spec stop(mongooseim:host_type()) -> ok.
stop(_HostType) ->
ok.

Check warning on line 32 in src/auth/ejabberd_auth_anonymous_mnesia.erl

View check run for this annotation

Codecov / codecov/patch

src/auth/ejabberd_auth_anonymous_mnesia.erl#L32

Added line #L32 was not covered by tests
6 changes: 4 additions & 2 deletions test/common/config_parser_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ options("mongooseim-pgsql") ->
{component_backend, mnesia},
{s2s_backend, mnesia},
{{auth, <<"anonymous.localhost">>},
(default_auth())#{anonymous => #{allow_multiple_connections => true,
(default_auth())#{anonymous => #{backend => mnesia,
allow_multiple_connections => true,
protocol => both},
methods => [anonymous]}},
{{auth, <<"localhost">>},
Expand Down Expand Up @@ -690,7 +691,8 @@ custom_auth() ->
maps:merge(default_auth(), extra_auth()).

extra_auth() ->
#{anonymous => #{allow_multiple_connections => true,
#{anonymous => #{backend => mnesia,
allow_multiple_connections => true,
protocol => sasl_anon},
http => #{basic_auth => "admin:admin"},
external => #{instances => 1,
Expand Down
1 change: 1 addition & 0 deletions test/mongoose_cleanup_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ opts() ->
host_types => [],
all_metrics_are_global => false,
s2s_backend => mnesia,
{auth, ?HOST} => config_parser_helper:extra_auth(),
{modules, ?HOST} => #{}}.

meck_mods(bosh) -> [exometer, mod_bosh_socket];
Expand Down