aboutsummaryrefslogtreecommitdiff
path: root/src/mod_privilege.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_privilege.erl')
-rw-r--r--src/mod_privilege.erl111
1 files changed, 70 insertions, 41 deletions
diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl
index abb38456a..b6e56ead4 100644
--- a/src/mod_privilege.erl
+++ b/src/mod_privilege.erl
@@ -41,9 +41,19 @@
-include("logger.hrl").
-include("xmpp.hrl").
+-include("translate.hrl").
--record(state, {server_host = <<"">> :: binary(),
- permissions = dict:new() :: dict:dict()}).
+-type roster_permission() :: both | get | set.
+-type presence_permission() :: managed_entity | roster.
+-type message_permission() :: outgoing.
+-type roster_permissions() :: [{roster_permission(), acl:acl()}].
+-type presence_permissions() :: [{presence_permission(), acl:acl()}].
+-type message_permissions() :: [{message_permission(), acl:acl()}].
+-type access() :: [{roster, roster_permissions()} |
+ {presence, presence_permissions()} |
+ {message, message_permissions()}].
+-type permissions() :: #{binary() => access()}.
+-record(state, {server_host = <<"">> :: binary()}).
%%%===================================================================
%%% API
@@ -57,9 +67,15 @@ stop(Host) ->
reload(_Host, _NewOpts, _OldOpts) ->
ok.
-mod_opt_type({roster, _}) -> fun acl:access_rules_validator/1;
-mod_opt_type({message, _}) -> fun acl:access_rules_validator/1;
-mod_opt_type({presence, _}) -> fun acl:access_rules_validator/1.
+mod_opt_type(roster) ->
+ econf:options(
+ #{both => econf:acl(), get => econf:acl(), set => econf:acl()});
+mod_opt_type(message) ->
+ econf:options(
+ #{outgoing => econf:acl()});
+mod_opt_type(presence) ->
+ econf:options(
+ #{managed_entity => econf:acl(), roster => econf:acl()}).
mod_options(_) ->
[{roster, [{both, none}, {get, none}, {set, none}]},
@@ -75,7 +91,7 @@ component_connected(Host) ->
fun(ServerHost) ->
Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
gen_server:cast(Proc, {component_connected, Host})
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec component_disconnected(binary(), binary()) -> ok.
component_disconnected(Host, _Reason) ->
@@ -83,7 +99,7 @@ component_disconnected(Host, _Reason) ->
fun(ServerHost) ->
Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
gen_server:cast(Proc, {component_disconnected, Host})
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec process_message(stanza()) -> stop | ok.
process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From,
@@ -92,13 +108,13 @@ process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From,
Host = From#jid.lserver,
ServerHost = To#jid.lserver,
Permissions = get_permissions(ServerHost),
- case dict:find(Host, Permissions) of
+ case maps:find(Host, Permissions) of
{ok, Access} ->
case proplists:get_value(message, Access, none) of
outgoing ->
forward_message(Msg);
_ ->
- Txt = <<"Insufficient privilege">>,
+ Txt = ?T("Insufficient privilege"),
Err = xmpp:err_forbidden(Txt, Lang),
ejabberd_router:route_error(Msg, Err)
end,
@@ -117,7 +133,7 @@ roster_access(false, #iq{from = From, to = To, type = Type}) ->
Host = From#jid.lserver,
ServerHost = To#jid.lserver,
Permissions = get_permissions(ServerHost),
- case dict:find(Host, Permissions) of
+ case maps:find(Host, Permissions) of
{ok, Access} ->
Permission = proplists:get_value(roster, Access, none),
(Permission == both)
@@ -146,7 +162,7 @@ process_presence_out({#presence{
true ->
ok
end
- end, dict:to_list(Permissions)),
+ end, maps:to_list(Permissions)),
{Pres, C2SState};
process_presence_out(Acc) ->
Acc.
@@ -174,7 +190,7 @@ process_presence_in({#presence{
_ ->
ok
end
- end, dict:to_list(Permissions)),
+ end, maps:to_list(Permissions)),
{Pres, C2SState};
process_presence_in(Acc) ->
Acc.
@@ -184,6 +200,9 @@ process_presence_in(Acc) ->
%%%===================================================================
init([Host, _Opts]) ->
process_flag(trap_exit, true),
+ catch ets:new(?MODULE,
+ [named_table, public,
+ {heir, erlang:group_leader(), none}]),
ejabberd_hooks:add(component_connected, ?MODULE,
component_connected, 50),
ejabberd_hooks:add(component_disconnected, ?MODULE,
@@ -198,11 +217,9 @@ init([Host, _Opts]) ->
process_presence_in, 50),
{ok, #state{server_host = Host}}.
-handle_call(get_permissions, _From, State) ->
- {reply, {ok, State#state.permissions}, State};
-handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
+handle_call(Request, From, State) ->
+ ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
+ {noreply, State}.
handle_cast({component_connected, Host}, State) ->
ServerHost = State#state.server_host,
@@ -224,23 +241,31 @@ handle_cast({component_connected, Host}, State) ->
[Host, RosterPerm, PresencePerm, MessagePerm]),
Msg = #message{from = From, to = To, sub_els = [Priv]},
ejabberd_router:route(Msg),
- Permissions = dict:store(Host, [{roster, RosterPerm},
- {presence, PresencePerm},
- {message, MessagePerm}],
- State#state.permissions),
- {noreply, State#state{permissions = Permissions}};
+ Permissions = maps:put(Host, [{roster, RosterPerm},
+ {presence, PresencePerm},
+ {message, MessagePerm}],
+ get_permissions(ServerHost)),
+ ets:insert(?MODULE, {ServerHost, Permissions}),
+ {noreply, State};
true ->
?INFO_MSG("Granting no permissions to external component '~s'",
[Host]),
{noreply, State}
end;
handle_cast({component_disconnected, Host}, State) ->
- Permissions = dict:erase(Host, State#state.permissions),
- {noreply, State#state{permissions = Permissions}};
-handle_cast(_Msg, State) ->
+ ServerHost = State#state.server_host,
+ Permissions = maps:remove(Host, get_permissions(ServerHost)),
+ case maps:size(Permissions) of
+ 0 -> ets:delete(?MODULE, ServerHost);
+ _ -> ets:insert(?MODULE, {ServerHost, Permissions})
+ end,
+ {noreply, State};
+handle_cast(Msg, State) ->
+ ?WARNING_MSG("Unexpected cast: ~p", [Msg]),
{noreply, State}.
-handle_info(_Info, State) ->
+handle_info(Info, State) ->
+ ?WARNING_MSG("Unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, State) ->
@@ -254,7 +279,8 @@ terminate(_Reason, State) ->
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
process_presence_out, 50),
ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE,
- process_presence_in, 50).
+ process_presence_in, 50),
+ ets:delete(?MODULE, Host).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
@@ -262,20 +288,17 @@ code_change(_OldVsn, State, _Extra) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
+-spec get_permissions(binary()) -> permissions().
get_permissions(ServerHost) ->
- Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
- try gen_server:call(Proc, get_permissions) of
- {ok, Permissions} ->
- Permissions
- catch exit:{noproc, _} ->
- %% No module is loaded for this virtual host
- dict:new()
+ try ets:lookup_element(?MODULE, ServerHost, 2)
+ catch _:badarg -> #{}
end.
+-spec forward_message(message()) -> ok.
forward_message(#message{to = To} = Msg) ->
ServerHost = To#jid.lserver,
Lang = xmpp:get_lang(Msg),
- CodecOpts = ejabberd_config:codec_options(ServerHost),
+ CodecOpts = ejabberd_config:codec_options(),
try xmpp:try_subtag(Msg, #privilege{}) of
#privilege{forwarded = #forwarded{sub_els = [SubEl]}} ->
try xmpp:decode(SubEl, ?NS_CLIENT, CodecOpts) of
@@ -285,12 +308,12 @@ forward_message(#message{to = To} = Msg) ->
ejabberd_router:route(NewMsg);
_ ->
Lang = xmpp:get_lang(Msg),
- Txt = <<"Invalid 'from' attribute in forwarded message">>,
+ Txt = ?T("Invalid 'from' attribute in forwarded message"),
Err = xmpp:err_forbidden(Txt, Lang),
ejabberd_router:route_error(Msg, Err)
end;
_ ->
- Txt = <<"Message not found in forwarded payload">>,
+ Txt = ?T("Message not found in forwarded payload"),
Err = xmpp:err_bad_request(Txt, Lang),
ejabberd_router:route_error(Msg, Err)
catch _:{xmpp_codec, Why} ->
@@ -299,7 +322,7 @@ forward_message(#message{to = To} = Msg) ->
ejabberd_router:route_error(Msg, Err)
end;
_ ->
- Txt = <<"No <forwarded/> element found">>,
+ Txt = ?T("No <forwarded/> element found"),
Err = xmpp:err_bad_request(Txt, Lang),
ejabberd_router:route_error(Msg, Err)
catch _:{xmpp_codec, Why} ->
@@ -308,8 +331,9 @@ forward_message(#message{to = To} = Msg) ->
ejabberd_router:route_error(Msg, Err)
end.
+-spec get_roster_permission(binary(), binary()) -> roster_permission() | none.
get_roster_permission(ServerHost, Host) ->
- Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, roster),
+ Perms = mod_privilege_opt:roster(ServerHost),
case match_rule(ServerHost, Host, Perms, both) of
allow ->
both;
@@ -323,15 +347,17 @@ get_roster_permission(ServerHost, Host) ->
end
end.
+-spec get_message_permission(binary(), binary()) -> message_permission() | none.
get_message_permission(ServerHost, Host) ->
- Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, message),
+ Perms = mod_privilege_opt:message(ServerHost),
case match_rule(ServerHost, Host, Perms, outgoing) of
allow -> outgoing;
deny -> none
end.
+-spec get_presence_permission(binary(), binary()) -> presence_permission() | none.
get_presence_permission(ServerHost, Host) ->
- Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, presence),
+ Perms = mod_privilege_opt:presence(ServerHost),
case match_rule(ServerHost, Host, Perms, roster) of
allow ->
roster;
@@ -342,6 +368,9 @@ get_presence_permission(ServerHost, Host) ->
end
end.
+-spec match_rule(binary(), binary(), roster_permissions(), roster_permission()) -> allow | deny;
+ (binary(), binary(), presence_permissions(), presence_permission()) -> allow | deny;
+ (binary(), binary(), message_permissions(), message_permission()) -> allow | deny.
match_rule(ServerHost, Host, Perms, Type) ->
Access = proplists:get_value(Type, Perms, none),
acl:match_rule(ServerHost, Access, jid:make(Host)).