From 4967cbaf1b0d00c44915f4d8b53e2cdad816c113 Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 25 Mar 2020 10:58:46 -0500 Subject: [PATCH] update types for Authentication behaviour, don't error when no subscribers for a published topic --- coveralls.json | 2 +- lib/router.ex | 1 - lib/router/authentication.ex | 12 ++++-- lib/router/realms/session.ex | 9 ++++- lib/router/session.ex | 4 -- mix.exs | 2 +- mix.lock | 2 +- test/support/authentication.ex | 68 ++++++++++++++++++++++++++++++++++ test/support/test_callee.ex | 11 +++++- test/wampex_test.exs | 47 ++++++----------------- 10 files changed, 108 insertions(+), 50 deletions(-) create mode 100644 test/support/authentication.ex diff --git a/coveralls.json b/coveralls.json index d03e2ce..bfb636a 100644 --- a/coveralls.json +++ b/coveralls.json @@ -1,6 +1,6 @@ { "coverage_options": { - "minimum_coverage": 83 + "minimum_coverage": 80 }, "skip_files": [ "test/support" diff --git a/lib/router.ex b/lib/router.ex index f5a8872..d099f0b 100644 --- a/lib/router.ex +++ b/lib/router.ex @@ -49,7 +49,6 @@ defmodule Wampex.Router do def db_name(name), do: Module.concat([name, KV]) def realms_name(name), do: Module.concat([name, Realms]) - def admin_name(name), do: Module.concat([name, Admin]) defp dispatch(name, authentication_module, authorization_module) do [ diff --git a/lib/router/authentication.ex b/lib/router/authentication.ex index 0fdd4d1..ae31386 100644 --- a/lib/router/authentication.ex +++ b/lib/router/authentication.ex @@ -6,8 +6,12 @@ defmodule Wampex.Router.Authentication do @callback authenticate?(methods :: list(String.t())) :: boolean() @callback method() :: String.t() - @callback challenge(realm :: String.t(), authid :: String.t(), session_id :: integer()) :: %{challenge: String.t()} - @callback parse_challenge(Challenge.t()) :: parse_challenge() - @callback authenticate(signature :: String.t(), realm :: String.t(), authid :: String.t(), challenge :: Challenge.t()) :: - boolean() + @callback challenge(realm :: String.t(), authid :: String.t(), session_id :: integer()) :: map() + @callback parse_challenge(map()) :: parse_challenge() + @callback authenticate( + signature :: String.t(), + realm :: String.t(), + authid :: String.t(), + challenge :: map() + ) :: boolean() end diff --git a/lib/router/realms/session.ex b/lib/router/realms/session.ex index 31aacbd..e3ab0a2 100644 --- a/lib/router/realms/session.ex +++ b/lib/router/realms/session.ex @@ -89,8 +89,13 @@ defmodule Wampex.Router.Realms.Session do end defp get_prefix({db, {keyspace, key}, acc}) do - l = ClusterKV.prefix(db, keyspace, key, ".", 2, 500) - {db, {keyspace, key}, acc ++ Enum.flat_map(l, fn {_, subscribers} -> subscribers end)} + case ClusterKV.prefix(db, keyspace, key, ".", 2, 500) do + :not_found -> + {db, {keyspace, key}, acc} + + l -> + {db, {keyspace, key}, acc ++ Enum.flat_map(l, fn {_, subscribers} -> subscribers end)} + end end defp get_wildcard({db, {keyspace, key}, acc}) do diff --git a/lib/router/session.ex b/lib/router/session.ex index 8ea81d1..a843e2c 100644 --- a/lib/router/session.ex +++ b/lib/router/session.ex @@ -690,7 +690,6 @@ defmodule Wampex.Router.Session do message = case Enum.reverse(message) do [map | t] when map == %{} -> t - [nil | t] -> t m -> m end @@ -699,9 +698,6 @@ defmodule Wampex.Router.Session do [list | t] when list == [] -> t - [nil | t] -> - t - m -> m end diff --git a/mix.exs b/mix.exs index 6bb563b..e11ee15 100644 --- a/mix.exs +++ b/mix.exs @@ -40,7 +40,7 @@ defmodule Wampex.Router.MixProject do [ # {:cluster_kv, path: "../cluster_kv"}, {:cluster_kv, - git: "https://gitlab.com/entropealabs/cluster_kv.git", tag: "4c36b8d68f40711cd167edb9917d32a992f49561"}, + git: "https://gitlab.com/entropealabs/cluster_kv.git", tag: "801f732c1cb5a9a9125de34ed2bdf88d2723ed00"}, {:cors_plug, "~> 2.0"}, {:credo, "~> 1.2", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 0.5.1", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index 75273cc..8048420 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, - "cluster_kv": {:git, "https://gitlab.com/entropealabs/cluster_kv.git", "4c36b8d68f40711cd167edb9917d32a992f49561", [tag: "4c36b8d68f40711cd167edb9917d32a992f49561"]}, + "cluster_kv": {:git, "https://gitlab.com/entropealabs/cluster_kv.git", "801f732c1cb5a9a9125de34ed2bdf88d2723ed00", [tag: "801f732c1cb5a9a9125de34ed2bdf88d2723ed00"]}, "conv_case": {:hex, :conv_case, "0.2.2", "5a98b74ab8f7ddbad670e5c7bb39ff280e60699aa3b25c7062ceccf48137433c", [:mix], [], "hexpm", "561c550ab6d55b2a4d4c14449e58c9957798613eb26ea182e14a962965377bca"}, "cors_plug": {:hex, :cors_plug, "2.0.2", "2b46083af45e4bc79632bd951550509395935d3e7973275b2b743bd63cc942ce", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f0d0e13f71c51fd4ef8b2c7e051388e4dfb267522a83a22392c856de7e46465f"}, "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, diff --git a/test/support/authentication.ex b/test/support/authentication.ex new file mode 100644 index 0000000..594ef04 --- /dev/null +++ b/test/support/authentication.ex @@ -0,0 +1,68 @@ +defmodule Wampex.Router.AuthenticationImpl do + @moduledoc false + @behaviour Wampex.Router.Authentication + require Logger + + alias Wampex.Crypto + alias Wampex.Serializers.JSON + + @wampcra "wampcra" + @auth_provider "userdb" + @auth_role "user" + @auth_password "bnASR5qF9y8k/sHF6S+NneCOhvVI0zFkvoKQpc2F+hA=" + @salt "test" + @keylen 32 + @iterations 10 + + @impl true + def authenticate?(methods) do + can_auth(methods) + end + + @impl true + def method, do: @wampcra + + @impl true + def challenge(_realm, authid, session_id) do + now = DateTime.to_iso8601(DateTime.utc_now()) + + %{ + challenge: + JSON.serialize!(%{ + nonce: Crypto.random_string(@keylen), + authprovider: @auth_provider, + authid: authid, + timestamp: now, + authrole: @auth_role, + authmethod: @wampcra, + session: session_id + }), + salt: @salt, + keylen: @keylen, + iterations: @iterations + } + end + + @impl true + def parse_challenge(challenge) do + ch = JSON.deserialize!(challenge.challenge) + + {get_in(ch, ["authid"]), get_in(ch, ["authrole"]), get_in(ch, ["authmethod"]), get_in(ch, ["authprovider"])} + end + + @impl true + def authenticate(signature, realm, authid, %{ + challenge: challenge + }) do + authid + |> get_secret(realm) + |> Crypto.hash_challenge(challenge) + |> :pbkdf2.compare_secure(signature) + end + + defp get_secret(_authid, _uri), do: Crypto.pbkdf2(@auth_password, @salt, @iterations, @keylen) + + defp can_auth([]), do: false + defp can_auth([@wampcra | _]), do: true + defp can_auth([_ | t]), do: can_auth(t) +end diff --git a/test/support/test_callee.ex b/test/support/test_callee.ex index c0866b9..c57dc82 100644 --- a/test/support/test_callee.ex +++ b/test/support/test_callee.ex @@ -4,7 +4,7 @@ defmodule TestCallee do require Logger alias Wampex.Client alias Wampex.Roles.{Callee, Dealer} - alias Callee.{Register, Yield} + alias Callee.{Register, Unregister, Yield} alias Dealer.Invocation def start_link(test, name, device) do @@ -36,4 +36,13 @@ defmodule TestCallee do {:noreply, state} end + + def terminate(_r, {_test, name, id}) do + Client.send_request( + name, + Callee.unregister(%Unregister{registration_id: id}) + ) + + :ok + end end diff --git a/test/wampex_test.exs b/test/wampex_test.exs index 0e980c1..52ddf3a 100644 --- a/test/wampex_test.exs +++ b/test/wampex_test.exs @@ -11,6 +11,7 @@ defmodule WampexTest do alias Wampex.Roles.Peer.Hello alias Wampex.Roles.Publisher.Publish alias Wampex.Router + alias Wampex.Router.AuthenticationImpl require Logger @url "ws://localhost:4000/ws" @@ -33,9 +34,8 @@ defmodule WampexTest do topologies: topologies, replicas: 1, quorum: 1, - admin_realm: @realm_uri, - admin_authid: @authid, - admin_password: @auth_password + authentication_module: AuthenticationImpl, + authorization_module: nil ) ] end @@ -73,38 +73,6 @@ defmodule WampexTest do ) end - @tag :client - test "admin callee is invoked and responds and caller gets result" do - caller_name = TestAdminCaller - Client.start_link(name: caller_name, session: @session) - - %Result{arg_list: [id]} = - Client.send_request( - caller_name, - Caller.call(%Call{ - procedure: "admin.create_peer", - arg_kw: %{authid: "chris", password: "woot!", realm: @realm_uri} - }) - ) - - assert is_binary(id) - end - - @tag :client - test "admin callee is invoked and responds with error" do - caller_name = TestAdminErrorCaller - Client.start_link(name: caller_name, session: @session) - - assert {:error, 48, "Error registering user", %{}, [], %{}} = - Client.send_request( - caller_name, - Caller.call(%Call{ - procedure: "admin.create_peer", - arg_kw: %{authid: "chris", password: "woot!", realm: "not.real"} - }) - ) - end - @tag :client test "callee is invoked and responds and caller gets result" do callee_name = TestCalleeRespond @@ -144,6 +112,15 @@ defmodule WampexTest do Client.start_link(name: TestPublisher, session: @session) + Client.cast_send_request( + TestPublisher, + Publisher.publish(%Publish{ + topic: "no.subscribers", + arg_list: [12.5, 45.6, 87.5], + arg_kw: %{loc: "60645"} + }) + ) + Client.cast_send_request( TestPublisher, Publisher.publish(%Publish{ -- 2.45.3