Skip to content

Commit

Permalink
Merge pull request #4148 from esl/cets/auth_anonymous
Browse files Browse the repository at this point in the history
Anonymous auth supports mnesia and cets backends
  • Loading branch information
arcusfelis authored Oct 19, 2023
2 parents 2447bd2 + 6c27108 commit f989a92
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 47 deletions.
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),
ok.

-spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
Expand All @@ -92,11 +85,13 @@ hooks(HostType) ->
-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 @@ config_spec() ->
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 @@ register_connection(Acc,
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 @@ register_connection(Acc, _Params, _Extra) ->
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 @@ purge_hook(true, HostType, LUser, LServer) ->
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 @@ login(HostType, LUser, LServer) ->
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 @@ login(HostType, LUser, LServer) ->
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
true ->
ok;
false ->
Expand All @@ -257,7 +237,7 @@ get_registered_users(_HostType, LServer, _) ->
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
%% 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]).

-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.

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.
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

0 comments on commit f989a92

Please sign in to comment.