+++ /dev/null
-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
- authed =
- authid
- |> get_secret(realm)
- |> Crypto.hash_challenge(challenge)
- |> :pbkdf2.compare_secure(signature)
-
- {authed, %{}}
- 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
+++ /dev/null
-defmodule TestCallee do
- @moduledoc false
- use GenServer
- require Logger
- alias Wampex.Client
- alias Wampex.Roles.{Callee, Dealer}
- alias Callee.{Register, Yield}
- alias Dealer.Invocation
-
- def start_link(test, name, device) do
- GenServer.start_link(__MODULE__, {test, name, device})
- end
-
- def init({test, name, device}) do
- {:ok, reg} = Client.register(name, %Register{procedure: "com.actuator.#{device}.light"})
- send(test, {:registered, reg})
- {:ok, {test, name, reg}}
- end
-
- def handle_info(
- %Invocation{request_id: id, details: %{"procedure" => proc}, arg_kw: arg_kw} = invocation,
- {test, name, _reg} = state
- ) do
- Logger.info("Got invocation #{proc}")
-
- send(test, invocation)
-
- Client.yield(
- name,
- %Yield{
- request_id: id,
- arg_list: [:ok],
- arg_kw: %{color: Map.get(arg_kw, "color")}
- }
- )
-
- {:noreply, state}
- end
-end
+++ /dev/null
-defmodule TestSubscriber do
- @moduledoc false
- use GenServer
- require Logger
- alias Wampex.Client
- alias Wampex.Roles.Subscriber.Subscribe
-
- def start_link(test, name, topic) do
- GenServer.start_link(__MODULE__, {test, name, topic})
- end
-
- def init({test, name, topic}) do
- {:ok, sub1} = Client.subscribe(name, %Subscribe{topic: topic})
- {:ok, sub2} = Client.subscribe(name, %Subscribe{topic: "com.data.test"})
- {:ok, sub3} = Client.subscribe(name, %Subscribe{topic: "com.data"})
-
- {:ok, sub4} =
- Client.subscribe(name, %Subscribe{topic: "com...temp", options: %{"match" => "wildcard"}})
-
- {:ok, sub5} =
- Client.subscribe(name, %Subscribe{
- topic: "com..test.temp",
- options: %{"match" => "wildcard"}
- })
-
- send(test, {:subscribed, sub5})
- {:ok, {test, name, [sub1, sub2, sub3, sub4, sub5]}}
- end
-
- def handle_info(event, {test, _name, _sub} = state) do
- send(test, event)
- {:noreply, state}
- end
-end
+++ /dev/null
-defmodule WampexTest do
- use ExUnit.Case, async: true
- doctest Wampex
-
- alias Wampex.Client
- alias Client.{Authentication, Realm, Session}
- alias Wampex.Roles.Broker.Event
- alias Wampex.Roles.{Callee, Caller, Peer, Publisher, Subscriber}
- alias Wampex.Roles.Caller.Call
- alias Wampex.Roles.Dealer.{Invocation, Result}
- alias Wampex.Roles.Peer.{Error, Hello}
- alias Wampex.Roles.Publisher.Publish
- alias Wampex.Router
- alias Wampex.Router.AuthenticationImpl
- alias Wampex.Router.AuthorizationImpl
- require Logger
-
- @url "ws://localhost:5999/ws"
- @authid "admin"
- @auth_password "test1234"
- @realm_uri "admin"
- @auth %Authentication{authid: @authid, authmethods: ["wampcra"], secret: @auth_password}
- @realm %Realm{name: @realm_uri, authentication: @auth}
- @roles [Callee, Caller, Publisher, Subscriber]
- @device "as987d9a8sd79a87ds"
-
- @session %Session{url: @url, realm: @realm, roles: @roles}
-
- setup_all do
- topologies = Application.get_env(:wampex_router, :topologies)
-
- [
- server:
- Router.start_link(
- name: TestRouter,
- port: 5999,
- topologies: topologies,
- replicas: 1,
- quorum: 1,
- authentication_module: AuthenticationImpl,
- authorization_module: AuthorizationImpl
- )
- ]
- end
-
- @tag :client
- test "callee registration" do
- name = TestCalleeRegistration
- Client.start_link(name: name, session: @session, reconnect: false)
- TestCallee.start_link(self(), name, @device)
- assert_receive {:registered, id}
- end
-
- @tag :abort
- test "abort" do
- Process.flag(:trap_exit, true)
- callee_name = TestAbort
- {:ok, _pid} = Client.start_link(name: callee_name, session: @session, reconnect: false)
-
- try do
- Client.cast(callee_name, Peer.hello(%Hello{realm: "test", roles: [Callee]}))
- catch
- :exit, er ->
- assert {:normal, _} = er
- end
- end
-
- @tag :client
- test "caller receives error when calling unknown procedure" do
- caller_name = TestExistCaller
- Client.start_link(name: caller_name, session: @session, reconnect: false)
-
- assert %Error{error: "wamp.error.no_registration"} =
- Client.call(
- caller_name,
- %Call{
- procedure: "this.should.not.exist",
- arg_list: [1],
- arg_kw: %{color: "#FFFFFF"}
- }
- )
- 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, reconnect: false)
-
- assert %Result{arg_kw: %{"authid" => "chris"}} =
- Client.call(
- caller_name,
- %Call{
- procedure: "admin.create_peer",
- arg_kw: %{
- authid: "chris",
- password: "woot!",
- realm: @realm_uri,
- roles: [],
- tenant: "org.entropealabs"
- }
- }
- )
- end
-
- @tag :client
- test "admin callee is invoked and responds with error" do
- caller_name = TestAdminErrorCaller
- Client.start_link(name: caller_name, session: @session, reconnect: false)
-
- assert %Error{error: "error"} =
- Client.call(
- caller_name,
- %Call{
- procedure: "admin.create_peer",
- arg_kw: %{
- authid: "chris",
- password: "woot!",
- realm: "not.real",
- roles: [],
- tenant: "org.entropealabs"
- }
- }
- )
- end
-
- @tag :client
- test "callee is invoked and responds and caller gets result" do
- callee_name = TestCalleeRespond
- Client.start_link(name: callee_name, session: @session, reconnect: false)
- TestCallee.start_link(self(), callee_name, @device)
- assert_receive {_, _}
- caller_name = TestCaller
- Client.start_link(name: caller_name, session: @session, reconnect: false)
-
- %Result{arg_list: ["ok"], arg_kw: %{"color" => "#FFFFFF"}} =
- Client.call(
- caller_name,
- %Call{
- procedure: "com.actuator.#{@device}.light",
- arg_list: [1],
- arg_kw: %{color: "#FFFFFF"}
- }
- )
-
- assert_receive %Invocation{}
- end
-
- @tag :client
- test "subscriber registration" do
- name = TestSubscriberRegister
- Client.start_link(name: name, session: @session, reconnect: false)
- TestSubscriber.start_link(self(), name, "com.data.temp")
- assert_receive {:subscribed, id}
- end
-
- @tag :client
- test "subscriber receives events from publisher" do
- name = TestSubscriberEvents
- Client.start_link(name: name, session: @session, reconnect: false)
- TestSubscriber.start_link(self(), name, "com.data.test.temp")
- assert_receive {:subscribed, id}
-
- Client.start_link(name: TestPublisher, session: @session, reconnect: false)
-
- Client.publish(
- TestPublisher,
- %Publish{
- topic: "com.data.test.temp",
- arg_list: [12.5, 45.6, 87.5],
- arg_kw: %{loc: "60645"}
- }
- )
-
- assert_receive %Event{}, 2000
- assert_receive %Event{}, 2000
- assert_receive %Event{}, 2000
- end
-end