From 94fbe04b188890ea813a5458712774e561125dbc Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 25 Mar 2020 21:19:09 -0500 Subject: [PATCH] add authorization --- lib/router/authentication.ex | 2 +- lib/router/authorization.ex | 7 + lib/router/session.ex | 252 ++++++++++++++++++++--------------- 3 files changed, 155 insertions(+), 106 deletions(-) create mode 100644 lib/router/authorization.ex diff --git a/lib/router/authentication.ex b/lib/router/authentication.ex index ae31386..042c976 100644 --- a/lib/router/authentication.ex +++ b/lib/router/authentication.ex @@ -13,5 +13,5 @@ defmodule Wampex.Router.Authentication do realm :: String.t(), authid :: String.t(), challenge :: map() - ) :: boolean() + ) :: {boolean(), any()} end diff --git a/lib/router/authorization.ex b/lib/router/authorization.ex new file mode 100644 index 0000000..cde9f26 --- /dev/null +++ b/lib/router/authorization.ex @@ -0,0 +1,7 @@ +defmodule Wampex.Router.Authorization do + @moduledoc false + + @type access_type :: :publish | :subscribe | :register | :call + + @callback authorized?(type :: access_type(), uri :: String.t(), data :: any()) :: true | false +end diff --git a/lib/router/session.ex b/lib/router/session.ex index a843e2c..b512c64 100644 --- a/lib/router/session.ex +++ b/lib/router/session.ex @@ -38,7 +38,7 @@ defmodule Wampex.Router.Session do :realm, :name, :goodbye, - :peer_information, + :peer, :challenge, :authentication_module, :authorization_module, @@ -190,8 +190,7 @@ defmodule Wampex.Router.Session do data | challenge: challenge, hello_received: true, - realm: realm, - peer_information: options + realm: realm } }, [{:next_event, :internal, :transition}] ++ actions} end @@ -216,9 +215,9 @@ defmodule Wampex.Router.Session do ) do info = auth.parse_challenge(challenge) - {actions, proxy} = maybe_welcome({auth, session_id, sig, realm, name, challenge, info, tt, t}) + {actions, proxy, peer} = maybe_welcome({auth, session_id, sig, realm, name, challenge, info, tt, t}) - {:ok, %SL{sl | data: %Sess{sl.data | proxy: proxy}}, actions} + {:ok, %SL{sl | data: %Sess{sl.data | peer: peer, proxy: proxy}}, actions} end @impl true @@ -255,19 +254,28 @@ defmodule Wampex.Router.Session do transport_pid: t, subscriptions: subs, realm: realm, + authorization_module: am, + peer: peer, subscribe: %Subscribe{request_id: ri, options: opts, topic: topic}, db: db } = data } = sl ) do - id = RealmSession.get_id() + {data, actions} = + case is_authorized?(am, peer, :subscribe, topic) do + true -> + id = RealmSession.get_id() + wc = RealmSession.subscribe(db, realm, topic, {id, {self(), Node.self()}}, opts) + send_to_peer(Broker.subscribed(%Subscribed{request_id: ri, subscription_id: id}), tt, t) - wc = RealmSession.subscribe(db, realm, topic, {id, {self(), Node.self()}}, opts) + {%SL{sl | data: %Sess{data | subscriptions: [{id, topic, wc} | subs]}}, + [{:next_event, :internal, :transition}]} - send_to_peer(Broker.subscribed(%Subscribed{request_id: ri, subscription_id: id}), tt, t) + false -> + {%SL{sl | data: %Sess{data | error: "wamp.error.not_authorized"}}, [{:next_event, :internal, :transition}]} + end - {:ok, %SL{sl | data: %Sess{data | subscriptions: [{id, topic, wc} | subs]}}, - [{:next_event, :internal, :transition}]} + {:ok, data, actions} end @impl true @@ -289,7 +297,6 @@ defmodule Wampex.Router.Session do subs = RealmSession.unsubscribe(db, realm, {subscription_id, {self(), Node.self()}}, subs) send_to_peer(Broker.unsubscribed(%Unsubscribed{request_id: rid}), tt, t) - {:ok, %SL{sl | data: %Sess{sl.data | subscriptions: subs}}, [{:next_event, :internal, :transition}]} end @@ -299,42 +306,54 @@ defmodule Wampex.Router.Session do _, @publish, %SL{ - data: %Sess{ - proxy: proxy, - realm: realm, - transport: tt, - transport_pid: t, - db: db, - publish: %Publish{request_id: rid, options: opts, topic: topic, arg_list: arg_l, arg_kw: arg_kw} - } + data: + %Sess{ + proxy: proxy, + realm: realm, + transport: tt, + transport_pid: t, + db: db, + authorization_module: am, + peer: peer, + publish: %Publish{request_id: rid, options: opts, topic: topic, arg_list: arg_l, arg_kw: arg_kw} + } = data } = sl ) do - pub_id = RealmSession.get_id() - - subs = RealmSession.subscriptions(db, realm, topic) - - Enum.each(subs, fn {id, {pid, node}} -> - send( - {proxy, node}, - {%Event{ - subscription_id: id, - publication_id: pub_id, - arg_list: arg_l, - arg_kw: arg_kw, - details: opts - }, pid} - ) - end) + {data, actions} = + case is_authorized?(am, peer, :publish, topic) do + true -> + pub_id = RealmSession.get_id() + + subs = RealmSession.subscriptions(db, realm, topic) + + Enum.each(subs, fn {id, {pid, node}} -> + send( + {proxy, node}, + {%Event{ + subscription_id: id, + publication_id: pub_id, + arg_list: arg_l, + arg_kw: arg_kw, + details: opts + }, pid} + ) + end) - case opts do - %{acknowledge: true} -> - send_to_peer(Broker.published(%Published{request_id: rid, publication_id: pub_id}), tt, t) + case opts do + %{acknowledge: true} -> + send_to_peer(Broker.published(%Published{request_id: rid, publication_id: pub_id}), tt, t) - %{} -> - :noop - end + %{} -> + :noop + end + + {sl, [{:next_event, :internal, :transition}]} + + false -> + {%SL{sl | data: %Sess{data | error: "wamp.error.not_authorized"}}, [{:next_event, :internal, :transition}]} + end - {:ok, sl, [{:next_event, :internal, :transition}]} + {:ok, data, actions} end @impl true @@ -373,17 +392,28 @@ defmodule Wampex.Router.Session do registrations: regs, db: db, realm: realm, + authorization_module: am, + peer: peer, register: %Register{request_id: rid, procedure: procedure} } = data } = sl ) do - id = RealmSession.get_id() - RealmSession.register(db, realm, procedure, {id, {self(), Node.self()}}) - regd = %Registered{request_id: rid, registration_id: id} - send_to_peer(Dealer.registered(regd), tt, t) + {data, actions} = + case is_authorized?(am, peer, :register, procedure) do + true -> + id = RealmSession.get_id() + RealmSession.register(db, realm, procedure, {id, {self(), Node.self()}}) + regd = %Registered{request_id: rid, registration_id: id} + send_to_peer(Dealer.registered(regd), tt, t) + + {%SL{sl | data: %Sess{data | registrations: [{id, procedure} | regs]}}, + [{:next_event, :internal, :transition}]} + + false -> + {%SL{sl | data: %Sess{data | error: "wamp.error.not_authorized"}}, [{:next_event, :internal, :transition}]} + end - {:ok, %SL{sl | data: %Sess{data | registrations: [{id, procedure} | regs]}}, - [{:next_event, :internal, :transition}]} + {:ok, data, actions} end @impl true @@ -400,66 +430,74 @@ defmodule Wampex.Router.Session do transport_pid: t, realm: realm, request_id: ri, + authorization_module: am, + peer: peer, call: %Call{request_id: call_id, options: opts, procedure: proc, arg_list: al, arg_kw: akw} } = data } = sl ) do - data = - with {callees, index} <- RealmSession.callees(db, realm, proc), - {id, {pid, node}} <- get_live_callee(proxy, callees, index, 3) do - req_id = RealmSession.get_request_id(ri) - opts = Map.put(opts, "procedure", proc) - - send( - {proxy, node}, - { - { - call_id, - %Invocation{ - request_id: req_id, - registration_id: id, - details: opts, - arg_list: al, - arg_kw: akw - }, - {self(), Node.self()} - }, - pid - } - ) + {data, actions} = + case is_authorized?(am, peer, :call, proc) do + true -> + with {callees, index} <- RealmSession.callees(db, realm, proc), + {id, {pid, node}} <- get_live_callee(proxy, callees, index, 3) do + req_id = RealmSession.get_request_id(ri) + opts = Map.put(opts, "procedure", proc) + + send( + {proxy, node}, + { + { + call_id, + %Invocation{ + request_id: req_id, + registration_id: id, + details: opts, + arg_list: al, + arg_kw: akw + }, + {self(), Node.self()} + }, + pid + } + ) + + RealmSession.round_robin(db, realm, proc, length(callees)) + + {%SL{sl | data: %Sess{data | request_id: req_id}}, [{:next_event, :internal, :transition}]} + else + {:error, :no_live_callees} -> + send_to_peer( + Caller.call_error(%Error{ + request_id: call_id, + error: "wamp.error.no_callees", + details: %{procedure: proc} + }), + tt, + t + ) + + {sl, [{:next_event, :internal, :transition}]} + + :not_found -> + send_to_peer( + Caller.call_error(%Error{ + request_id: call_id, + error: "wamp.error.no_registration", + details: %{procedure: proc} + }), + tt, + t + ) + + {sl, [{:next_event, :internal, :transition}]} + end - RealmSession.round_robin(db, realm, proc, length(callees)) - - %SL{sl | data: %Sess{data | request_id: req_id}} - else - {:error, :no_live_callees} -> - send_to_peer( - Caller.call_error(%Error{ - request_id: call_id, - error: "wamp.error.no_callees", - details: %{procedure: proc} - }), - tt, - t - ) - - sl - - :not_found -> - send_to_peer( - Caller.call_error(%Error{ - request_id: call_id, - error: "wamp.error.no_registration", - details: %{procedure: proc} - }), - tt, - t - ) - - sl + false -> + {%SL{sl | data: %Sess{data | error: "wamp.error.not_authorized"}}, [{:next_event, :internal, :transition}]} end - {:ok, data, [{:next_event, :internal, :transition}]} + {:ok, data, actions} end @impl true @@ -601,7 +639,7 @@ defmodule Wampex.Router.Session do {auth, session_id, sig, realm, name, challenge, {authid, authrole, authmethod, authprovider}, tt, t} ) do case auth.authenticate(sig, realm, authid, challenge) do - true -> + {true, peer} -> proxy = Realms.start_realm(Router.realms_name(name), realm) send_to_peer( @@ -620,10 +658,10 @@ defmodule Wampex.Router.Session do t ) - {[{:next_event, :internal, :transition}], proxy} + {[{:next_event, :internal, :transition}], proxy, peer} - false -> - {[{:next_event, :internal, :transition}, {:next_event, :internal, :abort}], nil} + {false, peer} -> + {[{:next_event, :internal, :transition}, {:next_event, :internal, :abort}], nil, peer} end end @@ -686,6 +724,10 @@ defmodule Wampex.Router.Session do defp maybe_update_response(data, resp), do: {data, resp} + def is_authorized?(am, peer, type, uri) do + am.authorized?(type, uri, peer) + end + defp remove_nil_values(message) do message = case Enum.reverse(message) do -- 2.45.3