mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Remove elixir tests
Our regular suite cover most of those cases, and it get trickier and tricker to keep those working after changes in ejabberd.
This commit is contained in:
parent
740ea3a047
commit
51cbbf313f
@ -1,389 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule ACLTest do
|
||||
@author "mremond@process-one.net"
|
||||
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
setup_all do
|
||||
:ok = :mnesia.start
|
||||
:ejabberd_mnesia.start
|
||||
:jid.start
|
||||
:ejabberd_hooks.start_link
|
||||
:stringprep.start
|
||||
:ok = :ejabberd_config.start(["domain1", "domain2"], [])
|
||||
{:ok, _} = :acl.start_link
|
||||
:ok
|
||||
end
|
||||
|
||||
setup do
|
||||
:acl.clear
|
||||
end
|
||||
|
||||
test "access rule match with user part ACL" do
|
||||
:acl.add(:global, :basic_acl_1, {:user, "test1"})
|
||||
:acl.add(:global, :basic_acl_1, {:user, "test2"})
|
||||
:acl.add_access(:global, :basic_rule_1, [{:allow, [{:acl, :basic_acl_1}]}])
|
||||
# JID can only be passes as jid record.
|
||||
# => TODO: Support passing JID as binary.
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test1@domain1")) == :allow
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test1@domain2")) == :allow
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test2@domain1")) == :allow
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test2@domain2")) == :allow
|
||||
# We match on user part only for local domain. As an implicit rule remote domain are not matched
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test1@otherdomain")) == :deny
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test2@otherdomain")) == :deny
|
||||
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test11@domain1")) == :deny
|
||||
|
||||
:acl.add(:global, :basic_acl_2, {:user, {"test2", "domain1"}})
|
||||
:acl.add_access(:global, :basic_rule_2, [{:allow, [{:acl, :basic_acl_2}]}])
|
||||
assert :acl.match_rule(:global, :basic_rule_2, :jid.from_string("test2@domain1")) == :allow
|
||||
assert :acl.match_rule(:global, :basic_rule_2, :jid.from_string("test2@domain2")) == :deny
|
||||
assert :acl.match_rule(:global, :basic_rule_2, :jid.from_string("test2@otherdomain")) == :deny
|
||||
assert :acl.match_rule(:global, :basic_rule_2, {127,0,0,1}) == :deny
|
||||
end
|
||||
|
||||
test "IP based ACL" do
|
||||
:acl.add(:global, :ip_acl_1, {:ip, "127.0.0.0/24"})
|
||||
:acl.add_access(:global, :ip_rule_1, [{:allow, [{:acl, :ip_acl_1}]}])
|
||||
# IP must be expressed as a tuple when calling match rule
|
||||
assert :acl.match_rule(:global, :ip_rule_1, {127,0,0,1}) == :allow
|
||||
assert :acl.match_rule(:global, :ip_rule_1, {127,0,1,1}) == :deny
|
||||
assert :acl.match_rule(:global, :ip_rule_1, :jid.from_string("test1@domain1")) == :deny
|
||||
end
|
||||
|
||||
test "Access rule are evaluated sequentially" do
|
||||
:acl.add(:global, :user_acl_1, {:user, {"test1", "domain2"}})
|
||||
:acl.add(:global, :user_acl_2, {:user, "test1"})
|
||||
:acl.add_access(:global, :user_rule_1, [{:deny, [{:acl, :user_acl_1}]}, {:allow, [{:acl, :user_acl_2}]}])
|
||||
assert :acl.match_rule(:global, :user_rule_1, :jid.from_string("test1@domain1")) == :allow
|
||||
assert :acl.match_rule(:global, :user_rule_1, :jid.from_string("test1@domain2")) == :deny
|
||||
end
|
||||
|
||||
# Access rules are sometimes used to provide values (i.e.: max_s2s_connections, max_user_sessions)
|
||||
test "Access rules providing values" do
|
||||
:acl.add(:global, :user_acl, {:user_regexp, ""})
|
||||
:acl.add(:global, :admin_acl, {:user, "admin"})
|
||||
:acl.add_access(:global, :value_rule_1, [{10, [{:acl, :admin_acl}]}, {5, [{:acl, :user_acl}]}])
|
||||
assert :acl.match_rule(:global, :value_rule_1, :jid.from_string("test1@domain1")) == 5
|
||||
assert :acl.match_rule(:global, :value_rule_1, :jid.from_string("admin@domain1")) == 10
|
||||
|
||||
# If we have no match, :deny is still the default value
|
||||
# => TODO maybe we should have a match rule which allow passing custom default value ?
|
||||
assert :acl.match_rule(:global, :value_rule_1, :jid.from_string("user@otherdomain")) == :deny
|
||||
end
|
||||
|
||||
|
||||
# At the moment IP and user rules to no go well together: There is
|
||||
# no way to combine IP and user restrictions.
|
||||
# => TODO we need to implement access rules that implement both and will deny the access
|
||||
# if either IP or user returns deny
|
||||
test "mixing IP and user access rules" do
|
||||
:acl.add(:global, :user_acl_1, {:user, "test1"})
|
||||
:acl.add(:global, :ip_acl_1, {:ip, "127.0.0.0/24"})
|
||||
:acl.add_access(:global, :mixed_rule_1, [{:allow, [{:acl, :user_acl_1}]}, {:allow, [{:acl, :ip_acl_1}]}])
|
||||
assert :acl.match_rule(:global, :mixed_rule_1, :jid.from_string("test1@domain1")) == :allow
|
||||
assert :acl.match_rule(:global, :mixed_rule_1, {127,0,0,1}) == :allow
|
||||
|
||||
:acl.add_access(:global, :mixed_rule_2, [{:deny, [{:acl, :user_acl_1}]}, {:allow, [{:acl, :ip_acl_1}]}])
|
||||
assert :acl.match_rule(:global, :mixed_rule_2, :jid.from_string("test1@domain1")) == :deny
|
||||
assert :acl.match_rule(:global, :mixed_rule_2, {127,0,0,1}) == :allow
|
||||
end
|
||||
|
||||
test "access_matches works with predefined access rules" do
|
||||
:acl.add(:global, :user_acl_2, {:user, "user"})
|
||||
:acl.add_access(:global, :user_rule_2, [{:allow, [{:acl, :user_acl_2}]}, {:deny, [:all]}])
|
||||
|
||||
assert :acl.access_matches(:user_rule_2, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches(:user_rule_2, %{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches rule all always matches" do
|
||||
assert :acl.access_matches(:all, %{}, :global) == :allow
|
||||
assert :acl.access_matches(:all, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches rule none never matches" do
|
||||
assert :acl.access_matches(:none, %{}, :global) == :deny
|
||||
assert :acl.access_matches(:none, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches with not existing rule never matches" do
|
||||
assert :acl.access_matches(:bleble, %{}, :global) == :deny
|
||||
assert :acl.access_matches(:bleble, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches works with inlined access rules" do
|
||||
:acl.add(:global, :user_acl_3, {:user, "user"})
|
||||
|
||||
assert :acl.access_matches([{:allow, [{:acl, :user_acl_3}]}, {:deny, [:all]}],
|
||||
%{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches([{:allow, [{:acl, :user_acl_3}]}, {:deny, [:all]}],
|
||||
%{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches allow to have acl rules inlined" do
|
||||
assert :acl.access_matches([{:allow, [{:user, "user"}]}, {:deny, [:all]}],
|
||||
%{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches([{:allow, [{:user, "user"}]}, {:deny, [:all]}],
|
||||
%{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches test have implicit deny at end" do
|
||||
assert :acl.access_matches([{:allow, [{:user, "user"}]}],
|
||||
%{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches([{:allow, [{:user, "user"}]}],
|
||||
%{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches requires that all subrules match" do
|
||||
rules = [{:allow, [{:user, "user"}, {:ip, {{127,0,0,1}, 32}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,2}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches rules are matched in order" do
|
||||
rules = [{:allow, [{:user, "user"}]}, {:deny, [{:user, "user2"}]}, {:allow, [{:user_regexp, "user"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user22", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches rules that require ip but no one is provided don't crash" do
|
||||
rules = [{:allow, [{:ip, {{127,0,0,1}, 32}}]},
|
||||
{:allow, [{:user, "user"}]},
|
||||
{:allow, [{:user, "user2"}, {:ip, {{127,0,0,1}, 32}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches rules that require usr but no one is provided don't crash" do
|
||||
rules = [{:allow, [{:ip, {{127,0,0,1}, 32}}]},
|
||||
{:allow, [{:user, "user"}]},
|
||||
{:allow, [{:user, "user2"}, {:ip, {{127,0,0,2}, 32}}]}]
|
||||
assert :acl.access_matches(rules, %{ip: {127,0,0,1}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{ip: {127,0,0,2}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches rules with all always matches" do
|
||||
rules = [{:allow, [:all]}, {:deny, {:user, "user"}}]
|
||||
assert :acl.access_matches(rules, %{}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches rules with {acl, all} always matches" do
|
||||
rules = [{:allow, [{:acl, :all}]}, {:deny, {:user, "user"}}]
|
||||
assert :acl.access_matches(rules, %{}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches rules with none never matches" do
|
||||
rules = [{:allow, [:none]}, {:deny, [:all]}]
|
||||
assert :acl.access_matches(rules, %{}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches with no rules never matches" do
|
||||
assert :acl.access_matches([], %{}, :global) == :deny
|
||||
assert :acl.access_matches([], %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches ip rule accepts {ip, port}" do
|
||||
rules = [{:allow, [{:ip, {{127,0,0,1}, 32}}]}]
|
||||
assert :acl.access_matches(rules, %{ip: {{127,0,0,1}, 5000}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{ip: {{127,0,0,2}, 5000}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches user rule works" do
|
||||
rules = [{:allow, [{:user, "user1"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "domain1", ""}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "domain3", ""}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches 2 arg user rule works" do
|
||||
rules = [{:allow, [{:user, {"user1", "server1"}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server1", ""}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server2", ""}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user2", "server1", ""}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user2", "server2", ""}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches server rule works" do
|
||||
rules = [{:allow, [{:server, "server1"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "server1", ""}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "server2", ""}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches resource rule works" do
|
||||
rules = [{:allow, [{:resource, "res1"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res2"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain3", "res1"}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches user_regexp rule works" do
|
||||
rules = [{:allow, [{:user_regexp, "user[0-9]"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "domain1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"userA", "domain1", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "domain3", "res1"}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches 2 arg user_regexp rule works" do
|
||||
rules = [{:allow, [{:user_regexp, {"user[0-9]", "server1"}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"userA", "server1", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server2", "res1"}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches server_regexp rule works" do
|
||||
rules = [{:allow, [{:server_regexp, "server[0-9]"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "server1", ""}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "serverA", ""}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches resource_regexp rule works" do
|
||||
rules = [{:allow, [{:resource_regexp, "res[0-9]"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "resA"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain3", "res1"}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches node_regexp rule works" do
|
||||
rules = [{:allow, [{:node_regexp, {"user[0-9]", "server[0-9]"}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"userA", "server1", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "serverA", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"userA", "serverA", "res1"}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches user_glob rule works" do
|
||||
rules = [{:allow, [{:user_glob, "user?"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "domain1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user11", "domain1", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "domain3", "res1"}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches 2 arg user_glob rule works" do
|
||||
rules = [{:allow, [{:user_glob, {"user?", "server1"}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user11", "server1", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server2", "res1"}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches server_glob rule works" do
|
||||
rules = [{:allow, [{:server_glob, "server?"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "server1", ""}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "server11", ""}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "access_matches resource_glob rule works" do
|
||||
rules = [{:allow, [{:resource_glob, "res?"}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res11"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user", "domain3", "res1"}}, :global) == :allow
|
||||
end
|
||||
|
||||
test "access_matches node_glob rule works" do
|
||||
rules = [{:allow, [{:node_glob, {"user?", "server?"}}]}]
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
|
||||
assert :acl.access_matches(rules, %{usr: {"user11", "server1", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user1", "server11", "res1"}}, :global) == :deny
|
||||
assert :acl.access_matches(rules, %{usr: {"user11", "server11", "res1"}}, :global) == :deny
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands allow rule" do
|
||||
assert :acl.transform_access_rules_config([:allow]) == [{:allow, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands deny rule" do
|
||||
assert :acl.transform_access_rules_config([:deny]) == [{:deny, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands <integer> rule" do
|
||||
assert :acl.transform_access_rules_config([100]) == [{100, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands <shaper_name> rule" do
|
||||
assert :acl.transform_access_rules_config([:fast]) == [{:fast, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands allow: <acl_name> rule" do
|
||||
assert :acl.transform_access_rules_config([{:allow, :test1}]) == [{:allow, [{:acl, :test1}]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands deny: <acl_name> rule" do
|
||||
assert :acl.transform_access_rules_config([{:deny, :test1}]) == [{:deny, [{:acl, :test1}]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands integer: <acl_name> rule" do
|
||||
assert :acl.transform_access_rules_config([{100, :test1}]) == [{100, [{:acl, :test1}]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands <shaper_name>: <acl_name> rule" do
|
||||
assert :acl.transform_access_rules_config([{:fast, :test1}]) == [{:fast, [{:acl, :test1}]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands allow rule (no list)" do
|
||||
assert :acl.transform_access_rules_config(:allow) == [{:allow, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands deny rule (no list)" do
|
||||
assert :acl.transform_access_rules_config(:deny) == [{:deny, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands <integer> rule (no list)" do
|
||||
assert :acl.transform_access_rules_config(100) == [{100, [:all]}]
|
||||
end
|
||||
|
||||
test "transform_access_rules_config expands <shaper_name> rule (no list)" do
|
||||
assert :acl.transform_access_rules_config(:fast) == [{:fast, [:all]}]
|
||||
end
|
||||
|
||||
test "access_rules_validator works with <AccessName>" do
|
||||
assert :acl.access_rules_validator(:my_access) == :my_access
|
||||
end
|
||||
|
||||
test "shapes_rules_validator works with <AccessName>" do
|
||||
assert :acl.shaper_rules_validator(:my_access) == :my_access
|
||||
end
|
||||
|
||||
## Checking ACL on both user pattern and IP
|
||||
## ========================================
|
||||
|
||||
# Typical example is mod_register
|
||||
|
||||
# Deprecated approach
|
||||
test "module can test both IP and user through two independent :acl.match_rule check (deprecated)" do
|
||||
:acl.add(:global, :user_acl, {:user, {"test1", "domain1"}})
|
||||
:acl.add(:global, :ip_acl, {:ip, "127.0.0.0/24"})
|
||||
:acl.add_access(:global, :user_rule, [{:allow, [{:acl, :user_acl}]}])
|
||||
:acl.add_access(:global, :ip_rule, [{:allow, [{:acl, :ip_acl}]}])
|
||||
|
||||
# acl module in 16.03 is not able to provide a function for compound result:
|
||||
assert :acl.match_rule(:global, :user_rule, :jid.from_string("test1@domain1")) == :allow
|
||||
assert :acl.match_rule(:global, :ip_rule, {127,0,0,1}) == :allow
|
||||
assert :acl.match_rule(:global, :user_rule, :jid.from_string("test2@domain1")) == :deny
|
||||
assert :acl.match_rule(:global, :ip_rule, {127,0,1,1}) == :deny
|
||||
end
|
||||
|
||||
end
|
@ -1,88 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule EjabberdAdminTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
@author "jsautret@process-one.net"
|
||||
|
||||
setup_all do
|
||||
:mnesia.start
|
||||
:ejabberd_mnesia.start
|
||||
# For some myterious reason, :ejabberd_commands.init mays
|
||||
# sometimes fails if module is not loaded before
|
||||
:ejabberd_config.start(["domain"], [])
|
||||
{:module, :ejabberd_commands} = Code.ensure_loaded(:ejabberd_commands)
|
||||
:ejabberd_hooks.start_link
|
||||
{:ok, _} = :acl.start_link
|
||||
{:ok, _} = :ejabberd_access_permissions.start_link()
|
||||
:ejabberd_commands.start_link
|
||||
:ejabberd_admin.start_link
|
||||
:ok
|
||||
end
|
||||
|
||||
setup do
|
||||
:ok
|
||||
end
|
||||
|
||||
test "Logvel can be set and retrieved" do
|
||||
:ejabberd_logger.start()
|
||||
|
||||
assert :lager == call_command(:set_loglevel, [1])
|
||||
assert {1, :critical, 'Critical'} ==
|
||||
call_command(:get_loglevel, [])
|
||||
|
||||
assert :lager == call_command(:set_loglevel, [2])
|
||||
assert {2, :error, 'Error'} ==
|
||||
call_command(:get_loglevel, [])
|
||||
|
||||
assert :lager == call_command(:set_loglevel, [3])
|
||||
assert {3, :warning, 'Warning'} ==
|
||||
call_command(:get_loglevel, [])
|
||||
|
||||
# assert {:wrong_loglevel, 6} ==
|
||||
# catch_throw call_command(:set_loglevel, [6])
|
||||
# assert {3, :warning, 'Warning'} ==
|
||||
# call_command(:get_loglevel, [])
|
||||
|
||||
assert :lager == call_command(:set_loglevel, [4])
|
||||
assert {4, :info, 'Info'} ==
|
||||
call_command(:get_loglevel, [])
|
||||
|
||||
assert :lager == call_command(:set_loglevel, [5])
|
||||
assert {5, :debug, 'Debug'} ==
|
||||
call_command(:get_loglevel, [])
|
||||
|
||||
assert :lager == call_command(:set_loglevel, [0])
|
||||
assert {0, :no_log, 'No log'} ==
|
||||
call_command(:get_loglevel, [])
|
||||
|
||||
end
|
||||
|
||||
defp call_command(name, args) do
|
||||
:ejabberd_commands.execute_command2(name, args, %{:caller_module => :ejabberd_ctl})
|
||||
end
|
||||
|
||||
test "command status works with ejabberd stopped" do
|
||||
assert :ejabberd_not_running ==
|
||||
elem(call_command(:status, []), 0)
|
||||
end
|
||||
|
||||
end
|
@ -1,74 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule EjabberdAuthMock do
|
||||
|
||||
@author "jsautret@process-one.net"
|
||||
@agent __MODULE__
|
||||
|
||||
def init do
|
||||
try do
|
||||
Agent.stop(@agent)
|
||||
catch
|
||||
:exit, _e -> :ok
|
||||
end
|
||||
|
||||
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: @agent)
|
||||
|
||||
mock(:ejabberd_auth, :user_exists,
|
||||
fn (user, domain) ->
|
||||
Agent.get(@agent, fn users -> Map.get(users, {user, domain}) end) != nil
|
||||
end)
|
||||
mock(:ejabberd_auth, :get_password_s,
|
||||
fn (user, domain) ->
|
||||
Agent.get(@agent, fn users -> Map.get(users, {user, domain}, "") end )
|
||||
end)
|
||||
mock(:ejabberd_auth, :check_password,
|
||||
fn (user, _authzid, domain, password) ->
|
||||
Agent.get(@agent, fn users ->
|
||||
Map.get(users, {user, domain}) end) == password
|
||||
end)
|
||||
mock(:ejabberd_auth, :set_password,
|
||||
fn (user, domain, password) ->
|
||||
Agent.update(@agent, fn users ->
|
||||
Map.put(users, {user, domain}, password) end)
|
||||
end)
|
||||
end
|
||||
|
||||
def create_user(user, domain, password) do
|
||||
Agent.update(@agent, fn users -> Map.put(users, {user, domain}, password) end)
|
||||
end
|
||||
|
||||
####################################################################
|
||||
# Helpers
|
||||
####################################################################
|
||||
|
||||
# TODO refactor: Move to ejabberd_test_mock
|
||||
def mock(module, function, fun) do
|
||||
try do
|
||||
:meck.new(module)
|
||||
catch
|
||||
:error, {:already_started, _pid} -> :ok
|
||||
end
|
||||
|
||||
:meck.expect(module, function, fun)
|
||||
end
|
||||
|
||||
end
|
@ -1,203 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Notes on the tests:
|
||||
#
|
||||
# This test suite will print out errors in logs for tests:
|
||||
#
|
||||
# test "Error in run_fold is ignored"
|
||||
# test "Throw in run_fold is ignored"
|
||||
# test "Exit in run_fold is ignored"
|
||||
#
|
||||
# Those tests are not failing and we can safely ignore those errors in
|
||||
# log as we are exercising hook handler recovery from that situation.
|
||||
|
||||
defmodule EjabberdHooksTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
@author "mremond@process-one.net"
|
||||
@host <<"domain.net">>
|
||||
@self __MODULE__
|
||||
|
||||
setup_all do
|
||||
{:ok, _pid} = :ejabberd_hooks.start_link
|
||||
:ok
|
||||
end
|
||||
|
||||
setup do
|
||||
:meck.unload
|
||||
:true = :ejabberd_hooks.delete_all_hooks
|
||||
:ok
|
||||
end
|
||||
|
||||
test "An anonymous function can be added as a hook" do
|
||||
hookname = :test_fun_hook
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, fn _ -> :ok end, 50)
|
||||
[{50, :undefined, _}] = :ejabberd_hooks.get_handlers(hookname, @host)
|
||||
end
|
||||
|
||||
test "A module function can be added as a hook" do
|
||||
hookname = :test_mod_hook
|
||||
callback = :hook_callback
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, @self, callback, 40)
|
||||
[{40, @self, _callback}] = :ejabberd_hooks.get_handlers(hookname, @host)
|
||||
end
|
||||
|
||||
test "An anonymous function can be removed from hook handlers" do
|
||||
hookname = :test_fun_hook
|
||||
anon_fun = fn _ -> :ok end
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, anon_fun, 50)
|
||||
:ok = :ejabberd_hooks.delete(hookname, @host, anon_fun, 50)
|
||||
[] = :ejabberd_hooks.get_handlers(hookname, @host)
|
||||
end
|
||||
|
||||
test "An module function can be removed from hook handlers" do
|
||||
hookname = :test_mod_hook
|
||||
callback = :hook_callback
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, @self, callback, 40)
|
||||
:ok = :ejabberd_hooks.delete(hookname, @host, @self, callback, 40)
|
||||
[] = :ejabberd_hooks.get_handlers(hookname, @host)
|
||||
# TODO: Check that removed function is not call anymore
|
||||
end
|
||||
|
||||
test "'Run hook' call registered handler once" do
|
||||
test_result = :hook_result
|
||||
run_hook([], fn -> test_result end, test_result)
|
||||
end
|
||||
|
||||
test "'Run hook' can call registered handler with parameters" do
|
||||
test_result = :hook_result_with_params
|
||||
run_hook([:hook_params], fn _ -> test_result end, test_result)
|
||||
end
|
||||
|
||||
# TODO test "Several handlers are run in order by hook"
|
||||
|
||||
test "Hook run chain is stopped when handler return 'stop'" do
|
||||
# setup test
|
||||
hookname = :test_mod_hook
|
||||
modulename = :hook_module
|
||||
mock(modulename, :hook_callback1, fn _ -> :stop end)
|
||||
mock(modulename, :hook_callback2, fn _ -> :end_result end)
|
||||
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 50)
|
||||
|
||||
:ok = :ejabberd_hooks.run(hookname, @host, [:hook_params])
|
||||
# callback2 is never run:
|
||||
[{_pid, {^modulename, _callback, [:hook_params]}, :stop}] = :meck.history(modulename)
|
||||
end
|
||||
|
||||
test "Run fold hooks accumulate state in correct order through handlers" do
|
||||
# setup test
|
||||
hookname = :test_mod_hook
|
||||
modulename = :hook_module
|
||||
mock(modulename, :hook_callback1, fn(list, user) -> [user|list] end)
|
||||
mock(modulename, :hook_callback2, fn(list, _user) -> ["jid2"|list] end)
|
||||
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
|
||||
|
||||
["jid2", "jid1"] = :ejabberd_hooks.run_fold(hookname, @host, [], ["jid1"])
|
||||
end
|
||||
|
||||
test "Hook run_fold are executed based on priority order, not registration order" do
|
||||
# setup test
|
||||
hookname = :test_mod_hook
|
||||
modulename = :hook_module
|
||||
mock(modulename, :hook_callback1, fn(_acc) -> :first end)
|
||||
mock(modulename, :hook_callback2, fn(_acc) -> :second end)
|
||||
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
|
||||
|
||||
:second = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
|
||||
# Both module have been called:
|
||||
2 = length(:meck.history(modulename))
|
||||
end
|
||||
|
||||
# TODO: Test with ability to stop and return a value
|
||||
test "Hook run_fold chain is stopped when handler return 'stop'" do
|
||||
# setup test
|
||||
hookname = :test_mod_hook
|
||||
modulename = :hook_module
|
||||
mock(modulename, :hook_callback1, fn(_acc) -> :stop end)
|
||||
mock(modulename, :hook_callback2, fn(_acc) -> :executed end)
|
||||
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
|
||||
|
||||
:stopped = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
|
||||
# Only one module has been called
|
||||
[{_pid, {^modulename, :hook_callback1, [:started]}, :stop}] = :meck.history(modulename)
|
||||
end
|
||||
|
||||
test "Error in run_fold is ignored" do
|
||||
run_fold_crash(fn(_acc) -> raise "crashed" end)
|
||||
end
|
||||
|
||||
test "Throw in run_fold is ignored" do
|
||||
run_fold_crash(fn(_acc) -> throw :crashed end)
|
||||
end
|
||||
|
||||
test "Exit in run_fold is ignored" do
|
||||
run_fold_crash(fn(_acc) -> exit :crashed end)
|
||||
end
|
||||
|
||||
# test for run hook with various number of params
|
||||
def run_hook(params, fun, result) do
|
||||
# setup test
|
||||
hookname = :test_mod_hook
|
||||
modulename = :hook_module
|
||||
callback = :hook_callback
|
||||
mock(modulename, callback, fun)
|
||||
|
||||
# Then check
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, callback, 40)
|
||||
:ok = :ejabberd_hooks.run(hookname, @host, params)
|
||||
[{_pid, {^modulename, ^callback, ^params}, ^result}] = :meck.history(modulename)
|
||||
end
|
||||
|
||||
def run_fold_crash(crash_fun) do
|
||||
# setup test
|
||||
hookname = :test_mod_hook
|
||||
modulename = :hook_module
|
||||
mock(modulename, :hook_callback1, crash_fun)
|
||||
mock(modulename, :hook_callback2, fn(_acc) -> :final end)
|
||||
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
|
||||
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
|
||||
|
||||
:final = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
|
||||
# Both handlers were called
|
||||
2 = length(:meck.history(modulename))
|
||||
end
|
||||
|
||||
# TODO refactor: Move to ejabberd_test_mock
|
||||
def mock(module, function, fun) do
|
||||
try do
|
||||
:meck.new(module, [:non_strict])
|
||||
catch
|
||||
:error, {:already_started, _pid} -> :ok
|
||||
end
|
||||
|
||||
:meck.expect(module, function, fun)
|
||||
end
|
||||
|
||||
end
|
@ -1,50 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule EjabberdOauthMock do
|
||||
|
||||
@author "jsautret@process-one.net"
|
||||
|
||||
def init() do
|
||||
:mnesia.start
|
||||
:mnesia.create_table(:oauth_token,
|
||||
[ram_copies: [node],
|
||||
attributes: [:oauth_token, :us, :scope, :expire]])
|
||||
:application.start(:cache_tab)
|
||||
:cache_tab.new(:oauth_token,
|
||||
[{:max_size, 1000}, {:life_time, 3600}])
|
||||
end
|
||||
|
||||
def get_token(user, domain, command, expiration \\ 3600) do
|
||||
now = {megasecs, secs, _} = :os.timestamp
|
||||
expire = 1000000 * megasecs + secs + expiration
|
||||
:random.seed now
|
||||
token = to_string :random.uniform(100000000)
|
||||
|
||||
{:ok, _} = :ejabberd_oauth.associate_access_token(token,
|
||||
[{"resource_owner",
|
||||
{:user, user, domain}},
|
||||
{"scope", [to_string command]},
|
||||
{"expiry_time", expire}],
|
||||
[])
|
||||
token
|
||||
end
|
||||
|
||||
end
|
@ -1,121 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule EjabberdSmMock do
|
||||
@author "jsautret@process-one.net"
|
||||
|
||||
require Record
|
||||
Record.defrecord :session, Record.extract(:session, from_lib: "ejabberd/include/ejabberd_sm.hrl")
|
||||
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
|
||||
|
||||
@agent __MODULE__
|
||||
|
||||
def init do
|
||||
ModLastMock.init
|
||||
|
||||
try do
|
||||
Agent.stop(@agent)
|
||||
catch
|
||||
:exit, _e -> :ok
|
||||
end
|
||||
|
||||
{:ok, _pid} = Agent.start_link(fn -> [] end, name: @agent)
|
||||
|
||||
mock(:ejabberd_sm, :get_user_resources,
|
||||
fn (user, domain) -> for s <- get_sessions(user, domain), do: s.resource end)
|
||||
|
||||
mock(:ejabberd_sm, :route,
|
||||
fn (to, {:exit, _reason}) ->
|
||||
user = jid(to, :user)
|
||||
domain = jid(to, :server)
|
||||
resource = jid(to, :resource)
|
||||
disconnect_resource(user, domain, resource)
|
||||
:ok
|
||||
(_, _) -> :ok
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
def connect_resource(user, domain, resource,
|
||||
opts \\ [priority: 1, conn: :c2s]) do
|
||||
Agent.update(@agent, fn sessions ->
|
||||
session = %{user: user, domain: domain, resource: resource,
|
||||
timestamp: :os.timestamp, pid: self, node: node,
|
||||
auth_module: :ejabberd_auth, ip: :undefined,
|
||||
priority: opts[:priority], conn: opts[:conn]}
|
||||
[session | sessions]
|
||||
end)
|
||||
end
|
||||
|
||||
def disconnect_resource(user, domain, resource) do
|
||||
disconnect_resource(user, domain, resource, ModLastMock.now)
|
||||
end
|
||||
|
||||
def disconnect_resource(user, domain, resource, timestamp) do
|
||||
Agent.update(@agent, fn sessions ->
|
||||
for s <- sessions,
|
||||
s.user != user or s.domain != domain or s.resource != resource, do: s
|
||||
end)
|
||||
ModLastMock.set_last user, domain, "", timestamp
|
||||
end
|
||||
|
||||
def get_sessions() do
|
||||
Agent.get(@agent, fn sessions -> sessions end)
|
||||
end
|
||||
|
||||
def get_sessions(user, domain) do
|
||||
Agent.get(@agent, fn sessions ->
|
||||
for s <- sessions, s.user == user, s.domain == domain, do: s
|
||||
end)
|
||||
end
|
||||
|
||||
def get_session(user, domain, resource) do
|
||||
Agent.get(@agent, fn sessions ->
|
||||
for s <- sessions,
|
||||
s.user == user, s.domain == domain, s.resource == resource, do: s
|
||||
end)
|
||||
end
|
||||
|
||||
def to_record(s) do
|
||||
session(usr: {s.user, s.domain, s.ressource},
|
||||
us: {s.user, s.domain},
|
||||
sid: {s.timestamp, s.pid},
|
||||
priority: s.priority,
|
||||
info: [conn: s.conn, ip: s.ip, node: s.node,
|
||||
oor: false, auth_module: s.auth_module])
|
||||
end
|
||||
|
||||
####################################################################
|
||||
# Helpers
|
||||
####################################################################
|
||||
|
||||
|
||||
# TODO refactor: Move to ejabberd_test_mock
|
||||
def mock(module, function, fun) do
|
||||
try do
|
||||
:meck.new(module)
|
||||
catch
|
||||
:error, {:already_started, _pid} -> :ok
|
||||
end
|
||||
|
||||
:meck.expect(module, function, fun)
|
||||
end
|
||||
|
||||
end
|
@ -1,119 +0,0 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% Author : Mickael Remond <mremond@process-one.net>
|
||||
%%% Created : 19 Feb 2015 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%% This is a common test wrapper to run our ejabberd tests written in
|
||||
%%% Elixir from standard common test code.
|
||||
%%%
|
||||
%%% Example: Is run with:
|
||||
%%% ./rebar skip_deps=true ct suites=elixir
|
||||
%%% or from ejabber overall test suite:
|
||||
%%% make quicktest
|
||||
|
||||
-module(elixir_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
suite:setup_ejabberd_lib_path(Config),
|
||||
check_meck(),
|
||||
code:add_pathz(filename:join(test_dir(), "../include")),
|
||||
Config.
|
||||
|
||||
end_per_suite(_Config) ->
|
||||
ok.
|
||||
|
||||
init_per_testcase(_TestCase, Config) ->
|
||||
process_flag(error_handler, ?MODULE),
|
||||
Config.
|
||||
|
||||
all() ->
|
||||
case is_elixir_available() of
|
||||
true ->
|
||||
Dir = test_dir(),
|
||||
filelib:fold_files(Dir, ".*test\.exs$", false,
|
||||
fun(Filename, Acc) -> [list_to_atom(filename:basename(Filename)) | Acc] end,
|
||||
[]);
|
||||
false ->
|
||||
[]
|
||||
end.
|
||||
|
||||
check_meck() ->
|
||||
case catch meck:module_info(module) of
|
||||
meck ->
|
||||
ok;
|
||||
{'EXIT',{undef, _}} ->
|
||||
ct:print("meck is not available. Please make sure you configured ejabberd with --enable-elixir --enable-tools"),
|
||||
ok
|
||||
end.
|
||||
|
||||
is_elixir_available() ->
|
||||
case catch elixir:module_info() of
|
||||
{'EXIT',{undef,_}} ->
|
||||
ct:print("ejabberd has not been build with Elixir support, skipping Elixir tests."),
|
||||
false;
|
||||
ModInfo when is_list(ModInfo) ->
|
||||
true
|
||||
end.
|
||||
|
||||
undefined_function(?MODULE, Func, Args) ->
|
||||
case lists:suffix(".exs", atom_to_list(Func)) of
|
||||
true ->
|
||||
run_elixir_test(Func);
|
||||
false ->
|
||||
error_handler:undefined_function(?MODULE, Func, Args)
|
||||
end;
|
||||
undefined_function(Module, Func, Args) ->
|
||||
error_handler:undefined_function(Module, Func,Args).
|
||||
|
||||
run_elixir_test(Func) ->
|
||||
%% Elixir tests can be tagged as follow to be ignored (place before test start)
|
||||
%% @tag pending: true
|
||||
'Elixir.ExUnit':start([{exclude, [{pending, true}]},
|
||||
{formatters,
|
||||
['Elixir.ExUnit.CLIFormatter',
|
||||
'Elixir.ExUnit.CTFormatter']},
|
||||
{autorun, false}]),
|
||||
|
||||
filelib:fold_files(test_dir(), ".*mock\.exs\$", true,
|
||||
fun (File, N) ->
|
||||
'Elixir.Code':load_file(list_to_binary(File)),
|
||||
N+1
|
||||
end, 0),
|
||||
|
||||
'Elixir.Code':load_file(list_to_binary(filename:join(test_dir(), atom_to_list(Func)))),
|
||||
%% I did not use map syntax, so that this file can still be build under R16
|
||||
catch 'Elixir.ExUnit.Server':cases_loaded(),
|
||||
ResultMap = 'Elixir.ExUnit':run(),
|
||||
case maps:find(failures, ResultMap) of
|
||||
{ok, 0} ->
|
||||
%% Zero failures
|
||||
ok;
|
||||
{ok, Failures} ->
|
||||
ct:print("Tests failed in module '~s': ~.10B failures.~nSee logs for details", [Func, Failures]),
|
||||
ct:fail(elixir_test_failure),
|
||||
error
|
||||
end.
|
||||
|
||||
test_dir() ->
|
||||
{ok, CWD} = file:get_cwd(),
|
||||
filename:join(CWD, "../../test").
|
@ -1,45 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule JidTest do
|
||||
@author "mremond@process-one.net"
|
||||
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
require Record
|
||||
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
|
||||
|
||||
setup_all do
|
||||
:stringprep.start
|
||||
:jid.start
|
||||
:ok
|
||||
end
|
||||
|
||||
test "create a jid from a binary" do
|
||||
jid = :jid.from_string("test@localhost/resource")
|
||||
assert jid(jid, :user) == "test"
|
||||
assert jid(jid, :server) == "localhost"
|
||||
assert jid(jid, :resource) == "resource"
|
||||
end
|
||||
|
||||
test "Check that sending a list to from_string/1 does not crash the jid process" do
|
||||
{:error, :need_jid_as_binary} = :jid.from_string('test@localhost/resource')
|
||||
end
|
||||
end
|
@ -1,374 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule EjabberdModAdminExtraTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
require EjabberdAuthMock
|
||||
require EjabberdSmMock
|
||||
require ModLastMock
|
||||
require ModRosterMock
|
||||
|
||||
@author "jsautret@process-one.net"
|
||||
|
||||
@user "user"
|
||||
@domain "domain"
|
||||
@password "password"
|
||||
@resource "resource"
|
||||
|
||||
require Record
|
||||
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
|
||||
|
||||
setup_all do
|
||||
try do
|
||||
:jid.start
|
||||
:stringprep.start
|
||||
:mnesia.start
|
||||
:ejabberd_mnesia.start
|
||||
:p1_sha.load_nif
|
||||
:ejabberd_hooks.start_link
|
||||
rescue
|
||||
_ -> :ok
|
||||
end
|
||||
:ok = :ejabberd_config.start(["domain"], [])
|
||||
:gen_mod.start_link
|
||||
:acl.start_link
|
||||
:ejabberd_access_permissions.start_link()
|
||||
:ejabberd_commands.start_link
|
||||
:mod_admin_extra.start(@domain, [])
|
||||
:ejabberd_hooks.start_link
|
||||
:ok
|
||||
end
|
||||
|
||||
setup do
|
||||
:meck.unload
|
||||
EjabberdAuthMock.init
|
||||
EjabberdSmMock.init
|
||||
ModRosterMock.init(@domain, :mod_admin_extra)
|
||||
:ok
|
||||
end
|
||||
|
||||
###################### Accounts
|
||||
test "check_account works" do
|
||||
EjabberdAuthMock.create_user @user, @domain, @password
|
||||
|
||||
assert call_command(:check_account, [@user, @domain])
|
||||
refute call_command(:check_account, [@user, "bad_domain"])
|
||||
refute call_command(:check_account, ["bad_user", @domain])
|
||||
|
||||
assert :meck.validate :ejabberd_auth
|
||||
end
|
||||
|
||||
test "check_password works" do
|
||||
|
||||
EjabberdAuthMock.create_user @user, @domain, @password
|
||||
|
||||
assert call_command(:check_password,
|
||||
[@user, @domain, @password])
|
||||
refute call_command(:check_password,
|
||||
[@user, @domain, "bad_password"])
|
||||
refute call_command(:check_password,
|
||||
[@user, "bad_domain", @password])
|
||||
refute call_command(:check_password,
|
||||
["bad_user", @domain, @password])
|
||||
|
||||
assert :meck.validate :ejabberd_auth
|
||||
|
||||
end
|
||||
|
||||
test "check_password_hash works" do
|
||||
|
||||
EjabberdAuthMock.create_user @user, @domain, @password
|
||||
hash = "5F4DCC3B5AA765D61D8327DEB882CF99" # echo -n password|md5
|
||||
|
||||
assert call_command(:check_password_hash,
|
||||
[@user, @domain, hash, "md5"])
|
||||
refute call_command(:check_password_hash,
|
||||
[@user, @domain, "bad_hash", "md5"])
|
||||
refute call_command(:check_password_hash,
|
||||
[@user, "bad_domain", hash, "md5"])
|
||||
refute call_command(:check_password_hash,
|
||||
["bad_user", @domain, hash, "md5"])
|
||||
|
||||
hash = "5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8" # echo -n password|shasum
|
||||
assert call_command(:check_password_hash,
|
||||
[@user, @domain, hash, "sha"])
|
||||
|
||||
assert :unkown_hash_method ==
|
||||
catch_throw call_command(:check_password_hash,
|
||||
[@user, @domain, hash, "bad_method"])
|
||||
|
||||
assert :meck.validate :ejabberd_auth
|
||||
|
||||
end
|
||||
|
||||
test "set_password works" do
|
||||
EjabberdAuthMock.create_user @user, @domain, @password
|
||||
|
||||
assert call_command(:change_password,
|
||||
[@user, @domain, "new_password"])
|
||||
refute call_command(:check_password,
|
||||
[@user, @domain, @password])
|
||||
assert call_command(:check_password,
|
||||
[@user, @domain, "new_password"])
|
||||
assert {:not_found, 'unknown_user'} ==
|
||||
catch_throw call_command(:change_password,
|
||||
["bad_user", @domain,
|
||||
@password])
|
||||
assert :meck.validate :ejabberd_auth
|
||||
end
|
||||
|
||||
###################### Sessions
|
||||
|
||||
test "num_resources works" do
|
||||
assert 0 == call_command(:num_resources,
|
||||
[@user, @domain])
|
||||
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource
|
||||
assert 1 == call_command(:num_resources,
|
||||
[@user, @domain])
|
||||
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
|
||||
assert 2 == call_command(:num_resources,
|
||||
[@user, @domain])
|
||||
|
||||
EjabberdSmMock.connect_resource @user<>"1", @domain, @resource
|
||||
assert 2 == call_command(:num_resources,
|
||||
[@user, @domain])
|
||||
|
||||
EjabberdSmMock.disconnect_resource @user, @domain, @resource
|
||||
assert 1 == call_command(:num_resources,
|
||||
[@user, @domain])
|
||||
|
||||
assert :meck.validate :ejabberd_sm
|
||||
end
|
||||
|
||||
test "resource_num works" do
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"3"
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"1"
|
||||
|
||||
assert :bad_argument ==
|
||||
elem(catch_throw(call_command(:resource_num,
|
||||
[@user, @domain, 0])), 0)
|
||||
assert @resource<>"1" ==
|
||||
call_command(:resource_num, [@user, @domain, 1])
|
||||
assert @resource<>"3" ==
|
||||
call_command(:resource_num, [@user, @domain, 3])
|
||||
assert :bad_argument ==
|
||||
elem(catch_throw(call_command(:resource_num,
|
||||
[@user, @domain, 4])), 0)
|
||||
assert :meck.validate :ejabberd_sm
|
||||
end
|
||||
|
||||
test "kick_session works" do
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"1"
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"3"
|
||||
|
||||
assert 3 == length EjabberdSmMock.get_sessions @user, @domain
|
||||
assert 1 == length EjabberdSmMock.get_session @user, @domain, @resource<>"2"
|
||||
|
||||
assert :ok ==
|
||||
call_command(:kick_session,
|
||||
[@user, @domain,
|
||||
@resource<>"2", "kick"])
|
||||
|
||||
assert 2 == length EjabberdSmMock.get_sessions @user, @domain
|
||||
assert 0 == length EjabberdSmMock.get_session @user, @domain, @resource<>"2"
|
||||
|
||||
assert :meck.validate :ejabberd_sm
|
||||
end
|
||||
|
||||
###################### Last
|
||||
|
||||
test "get_last works" do
|
||||
|
||||
assert {_, 'NOT FOUND'} =
|
||||
call_command(:get_last, [@user, @domain])
|
||||
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"1"
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
|
||||
|
||||
assert {_, 'ONLINE'} =
|
||||
call_command(:get_last, [@user, @domain])
|
||||
|
||||
EjabberdSmMock.disconnect_resource @user, @domain, @resource<>"1"
|
||||
|
||||
assert {_, 'ONLINE'} =
|
||||
call_command(:get_last, [@user, @domain])
|
||||
|
||||
now = {megasecs, secs, _microsecs} = :os.timestamp
|
||||
timestamp = megasecs * 1000000 + secs
|
||||
EjabberdSmMock.disconnect_resource(@user, @domain, @resource<>"2",
|
||||
timestamp)
|
||||
{{year, month, day}, {hour, minute, second}} = :calendar.now_to_universal_time now
|
||||
result = IO.iodata_to_binary(:io_lib.format(
|
||||
"~w-~.2.0w-~.2.0wT~.2.0w:~.2.0w:~.2.0wZ",
|
||||
[year, month, day, hour, minute, second]))
|
||||
assert {result, ""} ==
|
||||
call_command(:get_last, [@user, @domain])
|
||||
|
||||
assert :meck.validate :mod_last
|
||||
end
|
||||
|
||||
###################### Roster
|
||||
|
||||
@tag :skip
|
||||
test "add_rosteritem and delete_rosteritem work" do
|
||||
# Connect user
|
||||
# Add user1 & user2 to user's roster
|
||||
# Remove user1 & user2 from user's roster
|
||||
|
||||
EjabberdSmMock.connect_resource @user, @domain, @resource
|
||||
|
||||
assert [] == ModRosterMock.get_roster(@user, @domain)
|
||||
|
||||
assert :ok ==
|
||||
call_command(:add_rosteritem, [@user, @domain,
|
||||
@user<>"1", @domain,
|
||||
"nick1",
|
||||
"group1",
|
||||
"both"])
|
||||
# Check that user1 is the only item of the user's roster
|
||||
result = ModRosterMock.get_roster(@user, @domain)
|
||||
assert 1 == length result
|
||||
[{{@user, @domain, jid}, opts}] = result
|
||||
assert @user<>"1@"<>@domain == jid
|
||||
assert "nick1" == opts.nick
|
||||
assert ["group1"] == opts.groups
|
||||
assert :both == opts.subs
|
||||
|
||||
# Check that the item roster user1 was pushed with subscription
|
||||
# 'both' to user online ressource
|
||||
jid = :jlib.make_jid(@user, @domain, @resource)
|
||||
assert 1 ==
|
||||
:meck.num_calls(:ejabberd_sm, :route,
|
||||
[jid,
|
||||
{:item, {@user<>"1", @domain, ""}, :both}])
|
||||
|
||||
assert :ok ==
|
||||
call_command(:add_rosteritem, [@user, @domain,
|
||||
@user<>"2", @domain,
|
||||
"nick2",
|
||||
"group2",
|
||||
"both"])
|
||||
result = ModRosterMock.get_roster(@user, @domain)
|
||||
assert 2 == length result
|
||||
|
||||
|
||||
# Check that the item roster user2 was pushed with subscription
|
||||
# 'both' to user online ressource
|
||||
assert 1 ==
|
||||
:meck.num_calls(:ejabberd_sm, :route,
|
||||
[jid,
|
||||
{:item, {@user<>"2", @domain, ""}, :both}])
|
||||
|
||||
|
||||
call_command(:delete_rosteritem, [@user, @domain,
|
||||
@user<>"1", @domain])
|
||||
result = ModRosterMock.get_roster(@user, @domain)
|
||||
assert 1 == length result
|
||||
[{{@user, @domain, jid}, opts}] = result
|
||||
assert @user<>"2@"<>@domain == jid
|
||||
assert "nick2" == opts.nick
|
||||
assert ["group2"] == opts.groups
|
||||
assert :both == opts.subs
|
||||
|
||||
# Check that the item roster user1 was pushed with subscription
|
||||
# 'none' to user online ressource
|
||||
jid = :jlib.make_jid(@user, @domain, @resource)
|
||||
assert 1 ==
|
||||
:meck.num_calls(:ejabberd_sm, :route,
|
||||
[jid,
|
||||
{:item, {@user<>"1", @domain, ""}, :none}])
|
||||
|
||||
call_command(:delete_rosteritem, [@user, @domain,
|
||||
@user<>"2", @domain])
|
||||
|
||||
# Check that the item roster user2 was pushed with subscription
|
||||
# 'none' to user online ressource
|
||||
assert 1 ==
|
||||
:meck.num_calls(:ejabberd_sm, :route,
|
||||
[jid,
|
||||
{:item, {@user<>"2", @domain, ""}, :none}])
|
||||
|
||||
# Check that nothing else was pushed to user resource
|
||||
jid = jid(user: @user, server: @domain, resource: :_,
|
||||
luser: @user, lserver: @domain, lresource: :_)
|
||||
assert 4 ==
|
||||
:meck.num_calls(:ejabberd_sm, :route,
|
||||
[jid,
|
||||
{:item, :_, :_}])
|
||||
|
||||
assert [] == ModRosterMock.get_roster(@user, @domain)
|
||||
assert :meck.validate :ejabberd_sm
|
||||
|
||||
end
|
||||
|
||||
@tag :skip
|
||||
test "get_roster works" do
|
||||
assert [] == ModRosterMock.get_roster(@user, @domain)
|
||||
assert [] == call_command(:get_roster, [@user, @domain],
|
||||
:admin)
|
||||
|
||||
assert :ok ==
|
||||
call_command(:add_rosteritem, [@user, @domain,
|
||||
@user<>"1", @domain,
|
||||
"nick1",
|
||||
"group1",
|
||||
"both"])
|
||||
assert [{@user<>"1@"<>@domain, "", 'both', 'none', "group1"}] ==
|
||||
call_command(:get_roster, [@user, @domain], :admin)
|
||||
assert :ok ==
|
||||
call_command(:add_rosteritem, [@user, @domain,
|
||||
@user<>"2", @domain,
|
||||
"nick2",
|
||||
"group2",
|
||||
"none"])
|
||||
result = call_command(:get_roster, [@user, @domain], :admin)
|
||||
assert 2 == length result
|
||||
assert Enum.member?(result, {@user<>"1@"<>@domain, "", 'both', 'none', "group1"})
|
||||
assert Enum.member?(result, {@user<>"2@"<>@domain, "", 'none', 'none', "group2"})
|
||||
|
||||
end
|
||||
|
||||
defp call_command(name, args) do
|
||||
:ejabberd_commands.execute_command2(name, args, %{:caller_module => :ejabberd_ctl})
|
||||
end
|
||||
|
||||
defp call_command(name, args, mode) do
|
||||
call_command(name, args)
|
||||
end
|
||||
|
||||
# kick_user command is defined in ejabberd_sm, move to extra?
|
||||
# test "kick_user works" do
|
||||
# assert 0 == call_command(:num_resources,
|
||||
# [@user, @domain])
|
||||
# EjabberdSmMock.connect_resource(@user, @domain, @resource<>"1")
|
||||
# EjabberdSmMock.connect_resource(@user, @domain, @resource<>"2")
|
||||
# assert 2 ==
|
||||
# call_command(:kick_user, [@user, @domain])
|
||||
# assert 0 == call_command(:num_resources,
|
||||
# [@user, @domain])
|
||||
# assert :meck.validate :ejabberd_sm
|
||||
# end
|
||||
|
||||
end
|
@ -1,270 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule ModHttpApiMockTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
@author "jsautret@process-one.net"
|
||||
|
||||
# Admin user
|
||||
@admin "admin"
|
||||
@adminpass "adminpass"
|
||||
# Non admin user
|
||||
@user "user"
|
||||
@userpass "userpass"
|
||||
# XMPP domain
|
||||
@domain "domain"
|
||||
# mocked command
|
||||
@command "command_test"
|
||||
@acommand String.to_atom(@command)
|
||||
# default API version
|
||||
@version 0
|
||||
|
||||
require Record
|
||||
Record.defrecord :request, Record.extract(:request, from_lib: "ejabberd/include/ejabberd_http.hrl")
|
||||
|
||||
setup_all do
|
||||
try do
|
||||
:jid.start
|
||||
:mnesia.start
|
||||
:ejabberd_mnesia.start
|
||||
:stringprep.start
|
||||
:ejabberd_hooks.start_link
|
||||
:ejabberd_config.start([@domain], [])
|
||||
{:ok, _} = :ejabberd_access_permissions.start_link()
|
||||
:ejabberd_commands.start_link
|
||||
rescue
|
||||
_ -> :ok
|
||||
end
|
||||
:mod_http_api.start(@domain, [])
|
||||
EjabberdOauthMock.init
|
||||
:ok
|
||||
end
|
||||
|
||||
setup do
|
||||
:meck.unload
|
||||
:meck.new :ejabberd_commands
|
||||
:meck.new(:acl, [:passthrough]) # Need to fake acl to allow oauth
|
||||
EjabberdAuthMock.init
|
||||
:ok
|
||||
end
|
||||
|
||||
test "HTTP GET simple command call with Basic Auth" do
|
||||
EjabberdAuthMock.create_user @user, @domain, @userpass
|
||||
|
||||
# Mock a simple command() -> :ok
|
||||
:meck.expect(:ejabberd_commands, :get_command_format,
|
||||
fn (@acommand, %{usr: {@user, @domain, _}}, @version) ->
|
||||
{[], {:res, :rescode}}
|
||||
end)
|
||||
:meck.expect(:ejabberd_commands, :get_exposed_commands,
|
||||
fn () -> [@acommand] end)
|
||||
:meck.expect(:ejabberd_commands, :execute_command2,
|
||||
fn (@acommand, [], %{usr: {@user, @domain, _}}, @version) ->
|
||||
:ok
|
||||
end)
|
||||
|
||||
:ejabberd_config.add_local_option(:commands, [[{:add_commands, [@acommand]}]])
|
||||
|
||||
# Correct Basic Auth call
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# Basic auth
|
||||
auth: {@user<>"@"<>@domain, @userpass},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
|
||||
# history = :meck.history(:ejabberd_commands)
|
||||
|
||||
assert 200 == elem(result, 0) # HTTP code
|
||||
assert "0" == elem(result, 2) # command result
|
||||
|
||||
# Bad password
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# Basic auth
|
||||
auth: {@user<>"@"<>@domain, @userpass<>"bad"},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
assert 401 == elem(result, 0) # HTTP code
|
||||
|
||||
# Check that the command was executed only once
|
||||
assert 1 ==
|
||||
:meck.num_calls(:ejabberd_commands, :execute_command2, :_)
|
||||
|
||||
assert :meck.validate :ejabberd_auth
|
||||
assert :meck.validate :ejabberd_commands
|
||||
end
|
||||
|
||||
test "HTTP GET simple command call with OAuth" do
|
||||
EjabberdAuthMock.create_user @user, @domain, @userpass
|
||||
|
||||
# Mock a simple command() -> :ok
|
||||
:meck.expect(:ejabberd_commands, :get_command_format,
|
||||
fn (@acommand, %{usr: {@user, @domain, _}}, @version) ->
|
||||
{[], {:res, :rescode}}
|
||||
end)
|
||||
:meck.expect(:ejabberd_commands, :get_exposed_commands,
|
||||
fn () -> [@acommand] end)
|
||||
:meck.expect(:ejabberd_commands, :execute_command2,
|
||||
fn (@acommand, [], %{usr: {@user, @domain, _}, oauth_scope: ["ejabberd:user"]}, @version) ->
|
||||
:ok
|
||||
(@acommand, [], %{usr: {@user, @domain, _}, oauth_scope: [@command]}, @version) ->
|
||||
:ok
|
||||
(@acommand, [], %{usr: {@user, @domain, _}, oauth_scope: _}, @version) ->
|
||||
throw({:error, :access_rules_unauthorized})
|
||||
end)
|
||||
|
||||
|
||||
# Correct OAuth call using specific scope
|
||||
token = EjabberdOauthMock.get_token @user, @domain, @command
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# OAuth
|
||||
auth: {:oauth, token, []},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
assert 200 == elem(result, 0) # HTTP code
|
||||
assert "0" == elem(result, 2) # command result
|
||||
|
||||
# Correct OAuth call using specific ejabberd:user scope
|
||||
token = EjabberdOauthMock.get_token @user, @domain, "ejabberd:user"
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# OAuth
|
||||
auth: {:oauth, token, []},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
assert 200 == elem(result, 0) # HTTP code
|
||||
assert "0" == elem(result, 2) # command result
|
||||
|
||||
# Wrong OAuth token
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# OAuth
|
||||
auth: {:oauth, "bad"<>token, []},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
assert 401 == elem(result, 0) # HTTP code
|
||||
|
||||
# Expired OAuth token
|
||||
token = EjabberdOauthMock.get_token @user, @domain, @command, 1
|
||||
:timer.sleep 1500
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# OAuth
|
||||
auth: {:oauth, token, []},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
assert 401 == elem(result, 0) # HTTP code
|
||||
|
||||
# Wrong OAuth scope
|
||||
token = EjabberdOauthMock.get_token @user, @domain, "bad_command"
|
||||
:timer.sleep 1500
|
||||
req = request(method: :GET,
|
||||
path: ["api", @command],
|
||||
q: [nokey: ""],
|
||||
# OAuth
|
||||
auth: {:oauth, token, []},
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :mod_http_api.process([@command], req)
|
||||
assert 403 == elem(result, 0) # HTTP code
|
||||
|
||||
# Check that the command was executed twice
|
||||
assert 3 ==
|
||||
:meck.num_calls(:ejabberd_commands, :execute_command2, :_)
|
||||
|
||||
assert :meck.validate :ejabberd_auth
|
||||
#assert :meck.validate :ejabberd_commands
|
||||
#assert :ok = :meck.history(:ejabberd_commands)
|
||||
end
|
||||
|
||||
test "Request oauth token, resource owner password credentials" do
|
||||
EjabberdAuthMock.create_user @user, @domain, @userpass
|
||||
:application.set_env(:oauth2, :backend, :ejabberd_oauth)
|
||||
:application.start(:oauth2)
|
||||
|
||||
# Mock a simple command() -> :ok
|
||||
:meck.expect(:ejabberd_commands, :get_command_format,
|
||||
fn (@acommand, {@user, @domain, {:oauth, _token}, false}, @version) ->
|
||||
{[], {:res, :rescode}}
|
||||
end)
|
||||
:meck.expect(:ejabberd_commands, :get_exposed_commands,
|
||||
fn () -> [@acommand] end)
|
||||
|
||||
#Mock acl to allow oauth authorizations
|
||||
:meck.expect(:acl, :match_rule, fn(_Server, _Access, _Jid) -> :allow end)
|
||||
|
||||
|
||||
# Correct password
|
||||
req = request(method: :POST,
|
||||
path: ["oauth", "token"],
|
||||
q: [{"grant_type", "password"}, {"scope", @command}, {"username", @user<>"@"<>@domain}, {"ttl", "4000"}, {"password", @userpass}],
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :ejabberd_oauth.process([], req)
|
||||
assert 200 = elem(result, 0) #http code
|
||||
{kv} = :jiffy.decode(elem(result,2))
|
||||
assert {_, "bearer"} = List.keyfind(kv, "token_type", 0)
|
||||
assert {_, @command} = List.keyfind(kv, "scope", 0)
|
||||
assert {_, 4000} = List.keyfind(kv, "expires_in", 0)
|
||||
{"access_token", _token} = List.keyfind(kv, "access_token", 0)
|
||||
|
||||
#missing grant_type
|
||||
req = request(method: :POST,
|
||||
path: ["oauth", "token"],
|
||||
q: [{"scope", @command}, {"username", @user<>"@"<>@domain}, {"password", @userpass}],
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :ejabberd_oauth.process([], req)
|
||||
assert 400 = elem(result, 0) #http code
|
||||
{kv} = :jiffy.decode(elem(result,2))
|
||||
assert {_, "unsupported_grant_type"} = List.keyfind(kv, "error", 0)
|
||||
|
||||
|
||||
# incorrect user/pass
|
||||
req = request(method: :POST,
|
||||
path: ["oauth", "token"],
|
||||
q: [{"grant_type", "password"}, {"scope", @command}, {"username", @user<>"@"<>@domain}, {"password", @userpass<>"aa"}],
|
||||
ip: {{127,0,0,1},60000},
|
||||
host: @domain)
|
||||
result = :ejabberd_oauth.process([], req)
|
||||
assert 400 = elem(result, 0) #http code
|
||||
{kv} = :jiffy.decode(elem(result,2))
|
||||
assert {_, "invalid_grant"} = List.keyfind(kv, "error", 0)
|
||||
|
||||
assert :meck.validate :ejabberd_auth
|
||||
assert :meck.validate :ejabberd_commands
|
||||
end
|
||||
|
||||
end
|
@ -1,127 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule ModHttpApiTest do
|
||||
@author "mremond@process-one.net"
|
||||
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
require Record
|
||||
Record.defrecord :request, Record.extract(:request, from_lib: "ejabberd/include/ejabberd_http.hrl")
|
||||
Record.defrecord :ejabberd_commands, Record.extract(:ejabberd_commands, from_lib: "ejabberd/include/ejabberd_commands.hrl")
|
||||
|
||||
setup_all do
|
||||
:ok = :mnesia.start
|
||||
:ejabberd_mnesia.start
|
||||
:stringprep.start
|
||||
:ejabberd_hooks.start_link
|
||||
:ok = :ejabberd_config.start(["localhost"], [])
|
||||
:acl.start_link
|
||||
{:ok, _} = :ejabberd_access_permissions.start_link()
|
||||
{:ok, _} = :ejabberd_commands.start_link
|
||||
:ok = :ejabberd_commands.register_commands(cmds)
|
||||
on_exit fn ->
|
||||
:meck.unload
|
||||
unregister_commands(cmds) end
|
||||
end
|
||||
|
||||
test "We can expose several commands to API at a time" do
|
||||
setup_mocks()
|
||||
assert :ok == :ejabberd_commands.expose_commands([:open_cmd, :user_cmd])
|
||||
commands = :ejabberd_commands.get_exposed_commands()
|
||||
assert Enum.member?(commands, :open_cmd)
|
||||
assert Enum.member?(commands, :user_cmd)
|
||||
end
|
||||
|
||||
# test "We can call open commands without authentication" do
|
||||
# setup_mocks()
|
||||
# :ejabberd_commands.expose_commands([:open_cmd])
|
||||
# request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "[]")
|
||||
# {200, _, _} = :mod_http_api.process(["open_cmd"], request)
|
||||
# end
|
||||
|
||||
# This related to the commands config file option
|
||||
test "Attempting to access a command that is not exposed as HTTP API returns 403" do
|
||||
setup_mocks()
|
||||
assert :ok == :ejabberd_commands.expose_commands([])
|
||||
request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "{}")
|
||||
{403, _, _} = :mod_http_api.process(["open_cmd"], request)
|
||||
end
|
||||
|
||||
test "Call to user, admin or restricted commands without authentication are rejected" do
|
||||
setup_mocks()
|
||||
assert :ok == :ejabberd_commands.expose_commands([:user_cmd, :admin_cmd, :restricted])
|
||||
request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "{}")
|
||||
{400, _, _} = :mod_http_api.process(["user_cmd"], request)
|
||||
{403, _, _} = :mod_http_api.process(["admin_cmd"], request)
|
||||
{403, _, _} = :mod_http_api.process(["restricted_cmd"], request)
|
||||
end
|
||||
|
||||
@tag pending: true
|
||||
test "If admin_ip_access is enabled, we can call restricted API without authentication from that IP" do
|
||||
setup_mocks()
|
||||
end
|
||||
|
||||
# Define a set of test commands that we expose through API
|
||||
# We define one for each policy type
|
||||
defp cmds do
|
||||
[:open, :user, :admin, :restricted]
|
||||
|> Enum.map(&({&1, String.to_atom(to_string(&1) <> "_cmd")}))
|
||||
|> Enum.map(fn({cmd_type, cmd}) ->
|
||||
ejabberd_commands(name: cmd, tags: [:test],
|
||||
policy: cmd_type,
|
||||
module: __MODULE__,
|
||||
function: cmd,
|
||||
args: [],
|
||||
result: {:res, :rescode})
|
||||
end)
|
||||
end
|
||||
|
||||
def open_cmd, do: :ok
|
||||
def user_cmd(_, _), do: :ok
|
||||
def admin_cmd, do: :ok
|
||||
def restricted_cmd, do: :ok
|
||||
|
||||
defp setup_mocks() do
|
||||
:meck.unload
|
||||
mock(:gen_mod, :get_module_opt,
|
||||
fn (_server, :mod_http_api, _admin_ip_access, _, _) ->
|
||||
[{:allow, [{:ip, {{127,0,0,2}, 32}}]}]
|
||||
end)
|
||||
end
|
||||
|
||||
defp mock(module, function, fun) do
|
||||
try do
|
||||
:meck.new(module)
|
||||
catch
|
||||
:error, {:already_started, _pid} -> :ok
|
||||
end
|
||||
:meck.expect(module, function, fun)
|
||||
end
|
||||
|
||||
defp unregister_commands(commands) do
|
||||
try do
|
||||
:ejabberd_commands.unregister_commands(commands)
|
||||
catch
|
||||
_,_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,79 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule ModLastMock do
|
||||
|
||||
require Record
|
||||
Record.defrecord :session, Record.extract(:session, from_lib: "ejabberd/include/ejabberd_sm.hrl")
|
||||
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
|
||||
|
||||
@author "jsautret@process-one.net"
|
||||
@agent __MODULE__
|
||||
|
||||
def init do
|
||||
try do
|
||||
Agent.stop(@agent)
|
||||
catch
|
||||
:exit, _e -> :ok
|
||||
end
|
||||
|
||||
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: @agent)
|
||||
|
||||
mock(:mod_last, :get_last_info,
|
||||
fn (user, domain) ->
|
||||
Agent.get(@agent, fn last ->
|
||||
case Map.get(last, {user, domain}, :not_found) do
|
||||
{ts, status} -> {:ok, ts, status}
|
||||
result -> result
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
def set_last(user, domain, status) do
|
||||
set_last(user, domain, status, now)
|
||||
end
|
||||
|
||||
def set_last(user, domain, status, timestamp) do
|
||||
Agent.update(@agent, fn last ->
|
||||
Map.put(last, {user, domain}, {timestamp, status})
|
||||
end)
|
||||
end
|
||||
|
||||
####################################################################
|
||||
# Helpers
|
||||
####################################################################
|
||||
def now() do
|
||||
{megasecs, secs, _microsecs} = :os.timestamp
|
||||
megasecs * 1000000 + secs
|
||||
end
|
||||
|
||||
# TODO refactor: Move to ejabberd_test_mock
|
||||
def mock(module, function, fun) do
|
||||
try do
|
||||
:meck.new(module)
|
||||
catch
|
||||
:error, {:already_started, _pid} -> :ok
|
||||
end
|
||||
|
||||
:meck.expect(module, function, fun)
|
||||
end
|
||||
|
||||
end
|
@ -1,159 +0,0 @@
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 2 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
defmodule ModRosterMock do
|
||||
@author "jsautret@process-one.net"
|
||||
|
||||
require Record
|
||||
Record.defrecord :roster, Record.extract(:roster, from_lib: "ejabberd/include/mod_roster.hrl")
|
||||
Record.defrecord :roster_version, Record.extract(:roster_version, from_lib: "ejabberd/include/mod_roster.hrl")
|
||||
|
||||
@agent __MODULE__
|
||||
|
||||
def init(domain, module) do
|
||||
try do
|
||||
Agent.stop(@agent)
|
||||
catch
|
||||
:exit, _e -> :ok
|
||||
end
|
||||
|
||||
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: @agent)
|
||||
|
||||
mock_with_meck
|
||||
|
||||
:ejabberd_mnesia.create(:mod_roster_mnesia, :roster,
|
||||
[ram_copies: [node()],
|
||||
attributes: Keyword.keys(roster(roster())),
|
||||
index: [:us]])
|
||||
:ejabberd_mnesia.create(:mod_roster_mnesia, :roster_version,
|
||||
[ram_copies: [node()],
|
||||
attributes: Keyword.keys(roster_version(roster_version()))])
|
||||
#:mod_roster.stop(domain)
|
||||
:gen_mod.start_module(domain, :mod_roster)
|
||||
end
|
||||
|
||||
def mock_with_meck do
|
||||
# mock(:gen_mod, :db_type,
|
||||
# fn (_server, :mod_roster_mnesia) ->
|
||||
# :mnesia
|
||||
# end)
|
||||
#
|
||||
# mock(:mnesia, :transaction,
|
||||
# fn (_server, function) ->
|
||||
# {:atomic, function.()}
|
||||
# end)
|
||||
#
|
||||
# mock(:mnesia, :write,
|
||||
# fn (Item) ->
|
||||
# throw Item
|
||||
# {:atomic, :ok}
|
||||
# end)
|
||||
|
||||
mock(:mod_roster_mnesia, :init,
|
||||
fn (_server, _opts) ->
|
||||
:ok
|
||||
end)
|
||||
mock(:mod_roster_mnesia, :transaction,
|
||||
fn (_server, function) ->
|
||||
{:atomic, function.()}
|
||||
end)
|
||||
|
||||
mock(:mod_roster_mnesia, :update_roster_t,
|
||||
fn (user, domain, {u, d, _r}, item) ->
|
||||
add_roster_item(user, domain, u<>"@"<>d,
|
||||
roster(item, :name),
|
||||
roster(item, :subscription),
|
||||
roster(item, :groups),
|
||||
roster(item, :ask),
|
||||
roster(item, :askmessage))
|
||||
end)
|
||||
|
||||
mock(:mod_roster_mnesia, :invalidate_roster_cache,
|
||||
fn (_user, _server) ->
|
||||
:ok
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
def add_roster_item(user, domain, jid, nick, subs \\ :none, groups \\ [],
|
||||
ask \\ :none, askmessage \\ "")
|
||||
when is_binary(user) and byte_size(user) > 0
|
||||
and is_binary(domain) and byte_size(domain) > 0
|
||||
and is_binary(jid) and byte_size(jid) > 0
|
||||
and is_binary(nick)
|
||||
and is_atom(subs)
|
||||
and is_list(groups)
|
||||
and is_atom(ask)
|
||||
and is_binary(askmessage)
|
||||
do
|
||||
Agent.update(@agent, fn roster ->
|
||||
Map.put(roster, {user, domain, jid}, %{nick: nick,
|
||||
subs: subs, groups: groups,
|
||||
ask: ask, askmessage: askmessage})
|
||||
end)
|
||||
end
|
||||
|
||||
def remove_roster_item(user, domain, jid) do
|
||||
Agent.update(@agent, fn roster ->
|
||||
Map.delete(roster, {user, domain, jid})
|
||||
end)
|
||||
end
|
||||
|
||||
def get_rosters() do
|
||||
Agent.get(@agent, fn roster -> roster end)
|
||||
end
|
||||
|
||||
def get_roster(user, domain) do
|
||||
Agent.get(@agent, fn roster ->
|
||||
for {u, d, jid} <- Map.keys(roster), u == user, d == domain,
|
||||
do: {{u, d, jid}, Map.fetch!(roster, {u, d, jid})}
|
||||
end)
|
||||
end
|
||||
|
||||
def to_record({{user, domain, jid}, r}) do
|
||||
roster(usj: {user, domain, jid},
|
||||
us: {user, domain},
|
||||
jid: :jid.from_string(jid),
|
||||
subscription: r.subs,
|
||||
ask: r.ask,
|
||||
groups: r.groups,
|
||||
askmessage: r.askmessage
|
||||
)
|
||||
end
|
||||
def to_records(rosters) do
|
||||
for item <- rosters, do: to_record(item)
|
||||
end
|
||||
|
||||
####################################################################
|
||||
# Helpers
|
||||
####################################################################
|
||||
|
||||
# TODO refactor: Move to ejabberd_test_mock
|
||||
def mock(module, function, fun) do
|
||||
try do
|
||||
:meck.new(module, [:non_strict, :passthrough, :unstick])
|
||||
catch
|
||||
:error, {:already_started, _pid} -> :ok
|
||||
end
|
||||
|
||||
:meck.expect(module, function, fun)
|
||||
end
|
||||
|
||||
end
|
@ -1,7 +0,0 @@
|
||||
Code.require_file "ejabberd_auth_mock.exs", __DIR__
|
||||
Code.require_file "ejabberd_oauth_mock.exs", __DIR__
|
||||
Code.require_file "ejabberd_sm_mock.exs", __DIR__
|
||||
Code.require_file "mod_last_mock.exs", __DIR__
|
||||
Code.require_file "mod_roster_mock.exs", __DIR__
|
||||
|
||||
ExUnit.start
|
Loading…
Reference in New Issue
Block a user