diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/acl_test.exs | 1 | ||||
-rw-r--r-- | test/ejabberd_admin_test.exs | 1 | ||||
-rw-r--r-- | test/ejabberd_commands_mock_test.exs | 56 | ||||
-rw-r--r-- | test/ejabberd_commands_test.exs | 23 | ||||
-rw-r--r-- | test/ejabberd_cyrsasl_test.exs | 10 | ||||
-rw-r--r-- | test/ejabberd_oauth_mock.exs | 7 | ||||
-rw-r--r-- | test/elixir-config/attr_test.exs | 87 | ||||
-rw-r--r-- | test/elixir-config/config_test.exs | 65 | ||||
-rw-r--r-- | test/elixir-config/ejabberd_logger.exs | 49 | ||||
-rw-r--r-- | test/elixir-config/shared/ejabberd.exs | 31 | ||||
-rw-r--r-- | test/elixir-config/shared/ejabberd_different_from_default.exs | 9 | ||||
-rw-r--r-- | test/elixir-config/shared/ejabberd_for_validation.exs | 20 | ||||
-rw-r--r-- | test/elixir-config/validation_test.exs | 32 | ||||
-rw-r--r-- | test/mod_admin_extra_test.exs | 4 | ||||
-rw-r--r-- | test/mod_http_api_mock_test.exs | 97 | ||||
-rw-r--r-- | test/mod_http_api_test.exs | 39 | ||||
-rw-r--r-- | test/test_helper.exs | 7 |
17 files changed, 497 insertions, 41 deletions
diff --git a/test/acl_test.exs b/test/acl_test.exs index 551c74ae0..4bd8e6989 100644 --- a/test/acl_test.exs +++ b/test/acl_test.exs @@ -26,6 +26,7 @@ defmodule ACLTest do setup_all do :ok = :mnesia.start :ok = :jid.start + :stringprep.start :ok = :ejabberd_config.start(["domain1", "domain2"], []) :ok = :acl.start end diff --git a/test/ejabberd_admin_test.exs b/test/ejabberd_admin_test.exs index 1c999314c..31b8ab2e2 100644 --- a/test/ejabberd_admin_test.exs +++ b/test/ejabberd_admin_test.exs @@ -28,6 +28,7 @@ defmodule EjabberdAdminTest do # For some myterious reason, :ejabberd_commands.init mays # sometimes fails if module is not loaded before {:module, :ejabberd_commands} = Code.ensure_loaded(:ejabberd_commands) + {:ok, _} = :ejabberd_access_permissions.start_link() :ejabberd_commands.init :ejabberd_admin.start :ok diff --git a/test/ejabberd_commands_mock_test.exs b/test/ejabberd_commands_mock_test.exs index 487cf6a4b..419a989d6 100644 --- a/test/ejabberd_commands_mock_test.exs +++ b/test/ejabberd_commands_mock_test.exs @@ -18,9 +18,13 @@ # # ---------------------------------------------------------------------- +## TODO Fix next test error: add admin user ACL + defmodule EjabberdCommandsMockTest do use ExUnit.Case, async: false + require EjabberdOauthMock + @author "jsautret@process-one.net" # mocked callback module @@ -44,13 +48,18 @@ defmodule EjabberdCommandsMockTest do _ -> :ok end :mnesia.start + :ok = :jid.start + :ok = :ejabberd_config.start(["domain1", "domain2"], []) + {:ok, _} = :ejabberd_access_permissions.start_link() + :ok = :acl.start EjabberdOauthMock.init - :ok + on_exit fn -> :meck.unload end end setup do :meck.unload :meck.new(@module, [:non_strict]) + :mnesia.delete_table(:ejabberd_commands) :ejabberd_commands.init end @@ -167,7 +176,7 @@ defmodule EjabberdCommandsMockTest do # default version is latest one assert :result3 == :ejabberd_commands.execute_command(command_name, []) # no such command in APIv0 - assert :unknown_command == + assert {:error, :unknown_command} == catch_throw :ejabberd_commands.execute_command(command_name, [], 0) assert :result1 == :ejabberd_commands.execute_command(command_name, [], 1) assert :result1 == :ejabberd_commands.execute_command(command_name, [], 2) @@ -180,7 +189,7 @@ defmodule EjabberdCommandsMockTest do test "API command with user policy" do - mock_commands_config + mock_commands_config [:user, :admin] # Register a command test(user, domain) -> {:versionN, user, domain} # with policy=user and versions 1 & 3 @@ -313,9 +322,8 @@ defmodule EjabberdCommandsMockTest do end - test "API command with admin policy" do - mock_commands_config + mock_commands_config [:admin] # Register a command test(user, domain) -> {user, domain} # with policy=admin @@ -393,13 +401,47 @@ defmodule EjabberdCommandsMockTest do assert :meck.validate @module end + test "Commands can perform extra check on access" do + mock_commands_config [:admin, :open] + + command_name = :test + function = :test_command + command = ejabberd_commands(name: command_name, + args: [{:user, :binary}, {:host, :binary}], + access: [:basic_rule_1], + module: @module, + function: function, + policy: :open) + :meck.expect(@module, function, + fn(user, domain) when is_binary(user) and is_binary(domain) -> + {user, domain} + end) + assert :ok == :ejabberd_commands.register_commands [command] + +# :acl.add(:global, :basic_acl_1, {:user, @user, @host}) +# :acl.add_access(:global, :basic_rule_1, [{:allow, [{:acl, :basic_acl_1}]}]) + + assert {@user, @domain} == + :ejabberd_commands.execute_command(:undefined, + {@user, @domain, + @userpass, false}, + command_name, + [@user, @domain]) + assert {@user, @domain} == + :ejabberd_commands.execute_command(:undefined, + {@admin, @domain, + @adminpass, false}, + command_name, + [@user, @domain]) + + end ########################################################## # Utils # Mock a config where only @admin user is allowed to call commands # as admin - def mock_commands_config do + def mock_commands_config(commands \\ []) do EjabberdAuthMock.init EjabberdAuthMock.create_user @user, @domain, @userpass EjabberdAuthMock.create_user @admin, @domain, @adminpass @@ -408,10 +450,12 @@ defmodule EjabberdCommandsMockTest do :meck.expect(:ejabberd_config, :get_option, fn(:commands_admin_access, _, _) -> :commands_admin_access (:oauth_access, _, _) -> :all + (:commands, _, _) -> [{:add_commands, commands}] (_, _, default) -> default end) :meck.expect(:ejabberd_config, :get_myhosts, fn() -> [@domain] end) + :meck.new :acl :meck.expect(:acl, :access_matches, fn(:commands_admin_access, info, _scope) -> diff --git a/test/ejabberd_commands_test.exs b/test/ejabberd_commands_test.exs index 31d108214..c8219d0cf 100644 --- a/test/ejabberd_commands_test.exs +++ b/test/ejabberd_commands_test.exs @@ -28,7 +28,12 @@ defmodule EjabberdCommandsTest do setup_all do :mnesia.start + :stringprep.start + :ok = :ejabberd_config.start(["localhost"], []) + {:ok, _} = :ejabberd_access_permissions.start_link() + :ejabberd_commands.init + :ok end test "Check that we can register a command" do @@ -37,6 +42,14 @@ defmodule EjabberdCommandsTest do assert Enum.member?(commands, {:test_user, [], "Test user"}) end + test "get_exposed_commands/0 returns registered commands" do + commands = [open_test_command] + :ok = :ejabberd_commands.register_commands(commands) + :ok = :ejabberd_commands.expose_commands(commands) + exposed_commands = :ejabberd_commands.get_exposed_commands + assert Enum.member?(exposed_commands, :test_open) + end + test "Check that admin commands are rejected with noauth credentials" do :ok = :ejabberd_commands.register_commands([admin_test_command]) @@ -70,6 +83,16 @@ defmodule EjabberdCommandsTest do ]}}}}) end + defp open_test_command do + ejabberd_commands(name: :test_open, tags: [:test], + desc: "Test open", + policy: :open, + module: __MODULE__, + function: :test_open, + args: [], + result: {:res, :rescode}) + end + defp admin_test_command do ejabberd_commands(name: :test_admin, tags: [:roster], desc: "Test admin", diff --git a/test/ejabberd_cyrsasl_test.exs b/test/ejabberd_cyrsasl_test.exs index 0dc64ee44..d9b949294 100644 --- a/test/ejabberd_cyrsasl_test.exs +++ b/test/ejabberd_cyrsasl_test.exs @@ -71,8 +71,8 @@ defmodule EjabberdCyrsaslTest do response = "username=\"#{user}\",realm=\"#{domain}\",nonce=\"#{nonce}\",cnonce=\"#{cnonce}\"," <> "nc=\"#{nc}\",qop=auth,digest-uri=\"#{digest_uri}\",response=\"#{response_hash}\"," <> "charset=utf-8,algorithm=md5-sess" - assert {:continue, calc_str, state3} = :cyrsasl.server_step(state1, response) - assert {:ok, list} = :cyrsasl.server_step(state3, "") + assert {:continue, _calc_str, state3} = :cyrsasl.server_step(state1, response) + assert {:ok, _list} = :cyrsasl.server_step(state3, "") end defp calc_digest_sha(user, domain, pass, nc, nonce, cnonce) do @@ -94,7 +94,7 @@ defmodule EjabberdCyrsaslTest do defp setup_anonymous_mocks() do :meck.unload mock(:ejabberd_auth_anonymous, :is_sasl_anonymous_enabled, - fn (host) -> + fn (_host) -> true end) mock(:ejabberd_auth, :is_user_exists, @@ -119,7 +119,7 @@ defmodule EjabberdCyrsaslTest do end end - defp check_password(user, authzid, pass) do + defp check_password(_user, authzid, pass) do case get_password(authzid) do {^pass, mod} -> {true, mod} @@ -128,7 +128,7 @@ defmodule EjabberdCyrsaslTest do end end - defp check_password_digest(user, authzid, pass, digest, digest_gen) do + defp check_password_digest(_user, authzid, _pass, digest, digest_gen) do case get_password(authzid) do {spass, mod} -> v = digest_gen.(spass) diff --git a/test/ejabberd_oauth_mock.exs b/test/ejabberd_oauth_mock.exs index 81cfdc038..965bff1e6 100644 --- a/test/ejabberd_oauth_mock.exs +++ b/test/ejabberd_oauth_mock.exs @@ -26,7 +26,10 @@ defmodule EjabberdOauthMock do :mnesia.start :mnesia.create_table(:oauth_token, [ram_copies: [node], - attributes: [:oauth_token, :us, :scope, :expire]]) + 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 @@ -40,7 +43,7 @@ defmodule EjabberdOauthMock do {:user, user, domain}}, {"scope", [to_string command]}, {"expiry_time", expire}], - :undefined) + []) token end diff --git a/test/elixir-config/attr_test.exs b/test/elixir-config/attr_test.exs new file mode 100644 index 000000000..c5cab5bd8 --- /dev/null +++ b/test/elixir-config/attr_test.exs @@ -0,0 +1,87 @@ +defmodule Ejabberd.Config.AttrTest do + use ExUnit.Case, async: true + + alias Ejabberd.Config.Attr + + test "extract attrs from single line block" do + block = quote do + @active false + end + + block_res = Attr.extract_attrs_from_block_with_defaults(block) + assert {:active, false} in block_res + end + + test "extract attrs from multi line block" do + block = quote do + @active false + @opts [http: true] + end + + block_res = Attr.extract_attrs_from_block_with_defaults(block) + assert {:active, false} in block_res + assert {:opts, [http: true]} in block_res + end + + test "inserts correctly defaults attr when missing in block" do + block = quote do + @active false + @opts [http: true] + end + + block_res = Attr.extract_attrs_from_block_with_defaults(block) + + assert {:active, false} in block_res + assert {:git, ""} in block_res + assert {:name, ""} in block_res + assert {:opts, [http: true]} in block_res + assert {:dependency, []} in block_res + end + + test "inserts all defaults attr when passed an empty block" do + block = quote do + end + + block_res = Attr.extract_attrs_from_block_with_defaults(block) + + assert {:active, true} in block_res + assert {:git, ""} in block_res + assert {:name, ""} in block_res + assert {:opts, []} in block_res + assert {:dependency, []} in block_res + end + + test "validates attrs and returns errors, if any" do + block = quote do + @not_supported_attr true + @active "false" + @opts [http: true] + end + + block_res = + block + |> Attr.extract_attrs_from_block_with_defaults + |> Attr.validate + + assert {:ok, {:opts, [http: true]}} in block_res + assert {:ok, {:git, ""}} in block_res + assert {:error, {:not_supported_attr, true}, :attr_not_supported} in block_res + assert {:error, {:active, "false"}, :type_not_supported} in block_res + end + + test "returns the correct type for an attribute" do + assert :boolean == Attr.get_type_for_attr(:active) + assert :string == Attr.get_type_for_attr(:git) + assert :string == Attr.get_type_for_attr(:name) + assert :list == Attr.get_type_for_attr(:opts) + assert :list == Attr.get_type_for_attr(:dependency) + end + + test "returns the correct default for an attribute" do + assert true == Attr.get_default_for_attr(:active) + assert "" == Attr.get_default_for_attr(:git) + assert "" == Attr.get_default_for_attr(:name) + assert [] == Attr.get_default_for_attr(:opts) + assert [] == Attr.get_default_for_attr(:dependency) + end +end diff --git a/test/elixir-config/config_test.exs b/test/elixir-config/config_test.exs new file mode 100644 index 000000000..c359c49c3 --- /dev/null +++ b/test/elixir-config/config_test.exs @@ -0,0 +1,65 @@ +defmodule Ejabberd.ConfigTest do + use ExUnit.Case + + alias Ejabberd.Config + alias Ejabberd.Config.Store + + setup_all do + pid = Process.whereis(Ejabberd.Config.Store) + unless pid != nil and Process.alive?(pid) do + Store.start_link + + File.cd("test/elixir-config/shared") + config_file_path = File.cwd! <> "/ejabberd.exs" + Config.init(config_file_path) + end + + {:ok, %{}} + end + + test "extracts successfully the module name from config file" do + assert [Ejabberd.ConfigFile] == Store.get(:module_name) + end + + test "extracts successfully general opts from config file" do + [general] = Store.get(:general) + shaper = [normal: 1000, fast: 50000, max_fsm_queue: 1000] + assert [loglevel: 4, language: "en", hosts: ["localhost"], shaper: shaper] == general + end + + test "extracts successfully listeners from config file" do + [listen] = Store.get(:listeners) + assert :ejabberd_c2s == listen.module + assert [port: 5222, max_stanza_size: 65536, shaper: :c2s_shaper, access: :c2s] == listen.attrs[:opts] + end + + test "extracts successfully modules from config file" do + [module] = Store.get(:modules) + assert :mod_adhoc == module.module + assert [] == module.attrs[:opts] + end + + test "extracts successfully hooks from config file" do + [register_hook] = Store.get(:hooks) + + assert :register_user == register_hook.hook + assert [host: "localhost"] == register_hook.opts + assert is_function(register_hook.fun) + end + + # TODO: When enalbed, this test causes the evaluation of a different config file, so + # the other tests, that uses the store, are compromised because the data is different. + # So, until a good way is found, this test should remain disabed. + # + # test "init/2 with force:true re-initializes the config store with new data" do + # config_file_path = File.cwd! <> "/ejabberd_different_from_default.exs" + # Config.init(config_file_path, true) + # + # assert [Ejabberd.ConfigFile] == Store.get(:module_name) + # assert [[loglevel: 4, language: "en", hosts: ["localhost"]]] == Store.get(:general) + # assert [] == Store.get(:modules) + # assert [] == Store.get(:listeners) + # + # Store.stop + # end +end diff --git a/test/elixir-config/ejabberd_logger.exs b/test/elixir-config/ejabberd_logger.exs new file mode 100644 index 000000000..d13f79aa6 --- /dev/null +++ b/test/elixir-config/ejabberd_logger.exs @@ -0,0 +1,49 @@ +defmodule Ejabberd.Config.EjabberdLoggerTest do + use ExUnit.Case + + import ExUnit.CaptureIO + + alias Ejabberd.Config + alias Ejabberd.Config.Store + alias Ejabberd.Config.Validation + alias Ejabberd.Config.EjabberdLogger + + setup_all do + pid = Process.whereis(Ejabberd.Config.Store) + unless pid != nil and Process.alive?(pid) do + Store.start_link + + File.cd("test/elixir-config/shared") + config_file_path = File.cwd! <> "/ejabberd_for_validation.exs" + Config.init(config_file_path) + end + + {:ok, %{}} + end + + test "outputs correctly when attr is not supported" do + error_msg = "[ WARN ] Annotation @attr_not_supported is not supported.\n" + + [_mod_irc, _mod_configure, mod_time] = Store.get(:modules) + fun = fn -> + mod_time + |> Validation.validate + |> EjabberdLogger.log_errors + end + + assert capture_io(fun) == error_msg + end + + test "outputs correctly when dependency is not found" do + error_msg = "[ WARN ] Module :mod_adhoc was not found, but is required as a dependency.\n" + + [_mod_irc, mod_configure, _mod_time] = Store.get(:modules) + fun = fn -> + mod_configure + |> Validation.validate + |> EjabberdLogger.log_errors + end + + assert capture_io(fun) == error_msg + end +end diff --git a/test/elixir-config/shared/ejabberd.exs b/test/elixir-config/shared/ejabberd.exs new file mode 100644 index 000000000..5d0243bb5 --- /dev/null +++ b/test/elixir-config/shared/ejabberd.exs @@ -0,0 +1,31 @@ +defmodule Ejabberd.ConfigFile do + use Ejabberd.Config + + def start do + [loglevel: 4, + language: "en", + hosts: ["localhost"], + shaper: shaper] + end + + defp shaper do + [normal: 1000, + fast: 50000, + max_fsm_queue: 1000] + end + + listen :ejabberd_c2s do + @opts [ + port: 5222, + max_stanza_size: 65536, + shaper: :c2s_shaper, + access: :c2s] + end + + module :mod_adhoc do + end + + hook :register_user, [host: "localhost"], fn(user, server) -> + info("User registered: #{user} on #{server}") + end +end diff --git a/test/elixir-config/shared/ejabberd_different_from_default.exs b/test/elixir-config/shared/ejabberd_different_from_default.exs new file mode 100644 index 000000000..a39409683 --- /dev/null +++ b/test/elixir-config/shared/ejabberd_different_from_default.exs @@ -0,0 +1,9 @@ +defmodule Ejabberd.ConfigFile do + use Ejabberd.Config + + def start do + [loglevel: 4, + language: "en", + hosts: ["localhost"]] + end +end diff --git a/test/elixir-config/shared/ejabberd_for_validation.exs b/test/elixir-config/shared/ejabberd_for_validation.exs new file mode 100644 index 000000000..8c0196c7e --- /dev/null +++ b/test/elixir-config/shared/ejabberd_for_validation.exs @@ -0,0 +1,20 @@ +defmodule Ejabberd.ConfigFile do + use Ejabberd.Config + + def start do + [loglevel: 4, + language: "en", + hosts: ["localhost"]] + end + + module :mod_time do + @attr_not_supported true + end + + module :mod_configure do + @dependency [:mod_adhoc] + end + + module :mod_irc do + end +end diff --git a/test/elixir-config/validation_test.exs b/test/elixir-config/validation_test.exs new file mode 100644 index 000000000..1df775966 --- /dev/null +++ b/test/elixir-config/validation_test.exs @@ -0,0 +1,32 @@ +defmodule Ejabberd.Config.ValidationTest do + use ExUnit.Case + + alias Ejabberd.Config + alias Ejabberd.Config.Store + alias Ejabberd.Config.Validation + + setup_all do + pid = Process.whereis(Ejabberd.Config.Store) + unless pid != nil and Process.alive?(pid) do + Store.start_link + + File.cd("test/elixir-config/shared") + config_file_path = File.cwd! <> "/ejabberd_for_validation.exs" + Config.init(config_file_path) + end + + {:ok, %{}} + end + + test "validates correctly the modules" do + [mod_irc, mod_configure, mod_time] = Store.get(:modules) + + [{:error, _mod, errors}] = Validation.validate(mod_configure) + assert %{dependency: [mod_adhoc: :not_found]} == errors + + [{:error, _mod, errors}] = Validation.validate(mod_time) + assert %{attribute: [{{:attr_not_supported, true}, :attr_not_supported}]} == errors + + [{:ok, _mod}] = Validation.validate(mod_irc) + end +end diff --git a/test/mod_admin_extra_test.exs b/test/mod_admin_extra_test.exs index 761b07b7c..fde66f03f 100644 --- a/test/mod_admin_extra_test.exs +++ b/test/mod_admin_extra_test.exs @@ -22,6 +22,9 @@ defmodule EjabberdModAdminExtraTest do use ExUnit.Case, async: false require EjabberdAuthMock + require EjabberdSmMock + require ModLastMock + require ModRosterMock @author "jsautret@process-one.net" @@ -42,6 +45,7 @@ defmodule EjabberdModAdminExtraTest do rescue _ -> :ok end + {:ok, _} = :ejabberd_access_permissions.start_link() :ejabberd_commands.init :ok = :ejabberd_config.start([@domain], []) :mod_admin_extra.start(@domain, []) diff --git a/test/mod_http_api_mock_test.exs b/test/mod_http_api_mock_test.exs index 47b1fe94a..4809ecd59 100644 --- a/test/mod_http_api_mock_test.exs +++ b/test/mod_http_api_mock_test.exs @@ -46,6 +46,7 @@ defmodule ModHttpApiMockTest do :mnesia.start :stringprep.start :ejabberd_config.start([@domain], []) + {:ok, _} = :ejabberd_access_permissions.start_link() :ejabberd_commands.init rescue _ -> :ok @@ -58,6 +59,7 @@ defmodule ModHttpApiMockTest do setup do :meck.unload :meck.new :ejabberd_commands + :meck.new(:acl, [:passthrough]) # Need to fake acl to allow oauth EjabberdAuthMock.init :ok end @@ -70,9 +72,9 @@ defmodule ModHttpApiMockTest do fn (@acommand, {@user, @domain, @userpass, false}, @version) -> {[], {:res, :rescode}} end) - :meck.expect(:ejabberd_commands, :get_command_policy, - fn (@acommand) -> {:ok, :user} end) - :meck.expect(:ejabberd_commands, :get_commands, + :meck.expect(:ejabberd_commands, :get_command_policy_and_scope, + fn (@acommand) -> {:ok, :user, [:erlang.atom_to_binary(@acommand,:utf8)]} end) + :meck.expect(:ejabberd_commands, :get_exposed_commands, fn () -> [@acommand] end) :meck.expect(:ejabberd_commands, :execute_command, fn (:undefined, {@user, @domain, @userpass, false}, @acommand, [], @version, _) -> @@ -123,9 +125,9 @@ defmodule ModHttpApiMockTest do fn (@acommand, {@user, @domain, {:oauth, _token}, false}, @version) -> {[], {:res, :rescode}} end) - :meck.expect(:ejabberd_commands, :get_command_policy, - fn (@acommand) -> {:ok, :user} end) - :meck.expect(:ejabberd_commands, :get_commands, + :meck.expect(:ejabberd_commands, :get_command_policy_and_scope, + fn (@acommand) -> {:ok, :user, [:erlang.atom_to_binary(@acommand,:utf8), "ejabberd:user"]} end) + :meck.expect(:ejabberd_commands, :get_exposed_commands, fn () -> [@acommand] end) :meck.expect(:ejabberd_commands, :execute_command, fn (:undefined, {@user, @domain, {:oauth, _token}, false}, @@ -134,7 +136,7 @@ defmodule ModHttpApiMockTest do end) - # Correct OAuth call + # Correct OAuth call using specific scope token = EjabberdOauthMock.get_token @user, @domain, @command req = request(method: :GET, path: ["api", @command], @@ -147,6 +149,19 @@ defmodule ModHttpApiMockTest do 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], @@ -184,8 +199,8 @@ defmodule ModHttpApiMockTest do result = :mod_http_api.process([@command], req) assert 401 == elem(result, 0) # HTTP code - # Check that the command was executed only once - assert 1 == + # Check that the command was executed twice + assert 2 == :meck.num_calls(:ejabberd_commands, :execute_command, :_) assert :meck.validate :ejabberd_auth @@ -193,5 +208,69 @@ defmodule ModHttpApiMockTest do #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_command_policy_and_scope, + fn (@acommand) -> {:ok, :user, [:erlang.atom_to_binary(@acommand,:utf8), "ejabberd:user"]} end) + :meck.expect(:ejabberd_commands, :get_exposed_commands, + fn () -> [@acommand] end) + :meck.expect(:ejabberd_commands, :execute_command, + fn (:undefined, {@user, @domain, {:oauth, _token}, false}, + @acommand, [], @version, _) -> + :ok + 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 diff --git a/test/mod_http_api_test.exs b/test/mod_http_api_test.exs index 99b8d9b28..c68270f1f 100644 --- a/test/mod_http_api_test.exs +++ b/test/mod_http_api_test.exs @@ -31,43 +31,44 @@ defmodule ModHttpApiTest do :ok = :mnesia.start :stringprep.start :ok = :ejabberd_config.start(["localhost"], []) - + {:ok, _} = :ejabberd_access_permissions.start_link() :ok = :ejabberd_commands.init - :ok = :ejabberd_commands.register_commands(cmds) - on_exit fn -> unregister_commands(cmds) end + on_exit fn -> + :meck.unload + unregister_commands(cmds) end end test "We can expose several commands to API at a time" do setup_mocks() - :ejabberd_config.add_local_option(:commands, [[{:add_commands, [:open_cmd, :user_cmd]}]]) - commands = :ejabberd_commands.get_commands() + :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_config.add_local_option(:commands, [[{:add_commands, [:open_cmd]}]]) - request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "[]") - {200, _, _} = :mod_http_api.process(["open_cmd"], request) - 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 401" do + test "Attempting to access a command that is not exposed as HTTP API returns 403" do setup_mocks() - :ejabberd_config.add_local_option(:commands, []) + :ejabberd_commands.expose_commands([]) request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "[]") - {401, _, _} = :mod_http_api.process(["open_cmd"], request) + {403, _, _} = :mod_http_api.process(["open_cmd"], request) end test "Call to user, admin or restricted commands without authentication are rejected" do setup_mocks() - :ejabberd_config.add_local_option(:commands, [[{:add_commands, [:user_cmd, :admin_cmd, :restricted]}]]) + :ejabberd_commands.expose_commands([:user_cmd, :admin_cmd, :restricted]) request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "[]") - {401, _, _} = :mod_http_api.process(["user_cmd"], request) - {401, _, _} = :mod_http_api.process(["admin_cmd"], request) - {401, _, _} = :mod_http_api.process(["restricted_cmd"], request) + {403, _, _} = :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 @@ -98,7 +99,7 @@ defmodule ModHttpApiTest do defp setup_mocks() do :meck.unload mock(:gen_mod, :get_module_opt, - fn (_server, :mod_http_api, admin_ip_access, _, _) -> + fn (_server, :mod_http_api, _admin_ip_access, _, _) -> [{:allow, [{:ip, {{127,0,0,2}, 32}}]}] end) end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 000000000..454f2338a --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1,7 @@ +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 |