From 54626057cbd97c21a0c576ec042c210cbdb3ed7e Mon Sep 17 00:00:00 2001 From: Eduardo Gurgel Date: Sat, 30 May 2015 21:19:50 +1200 Subject: [PATCH] Refactor Poxa.PusherEvent.build to validate socketid and channel --- lib/poxa/event_handler.ex | 21 ++++++--------- lib/poxa/pusher_event.ex | 31 ++++++++++++++++++----- lib/poxa/websocket_handler.ex | 2 +- test/event_handler_test.exs | 22 ++++++++-------- test/pusher_event_test.exs | 45 ++++++++++++++++++++++++++++++--- test/websocket_handler_test.exs | 8 +++--- 6 files changed, 89 insertions(+), 40 deletions(-) diff --git a/lib/poxa/event_handler.ex b/lib/poxa/event_handler.ex index e41b44c..f58c8b5 100644 --- a/lib/poxa/event_handler.ex +++ b/lib/poxa/event_handler.ex @@ -25,11 +25,11 @@ defmodule Poxa.EventHandler do {:ok, body, req} = :cowboy_req.body(req) case JSX.decode(body) do {:ok, data} -> - if invalid_data?(data) do - req = :cowboy_req.set_resp_body(@invalid_event_json, req) - {true, req, state} - else - {false, req, %{body: body, data: data}} + case PusherEvent.build(data) do + {:ok, event} -> {false, req, %{body: body, event: event}} + _ -> + req = :cowboy_req.set_resp_body(@invalid_event_json, req) + {true, req, state} end _ -> req = :cowboy_req.set_resp_body(@invalid_event_json, req) @@ -37,10 +37,6 @@ defmodule Poxa.EventHandler do end end - defp invalid_data?(%{"name" => _, "data" => _, "channel" => _}), do: false - defp invalid_data?(%{"name" => _, "data" => _, "channels" => _}), do: false - defp invalid_data?(_), do: true - @authentication_error_json JSX.encode!(%{error: "Authentication error"}) # http://pusher.com/docs/rest_api#authentication def is_authorized(req, %{body: body} = state) do @@ -55,8 +51,8 @@ defmodule Poxa.EventHandler do end @invalid_data_size_json JSX.encode!(%{error: "Data key must be smaller than 10KB"}) - def valid_entity_length(req, %{data: data} = state) do - valid = byte_size(data["data"]) <= 10_000 + def valid_entity_length(req, %{event: event} = state) do + valid = byte_size(event.data) <= 10_000 unless valid do req = :cowboy_req.set_resp_body(@invalid_data_size_json, req) end @@ -71,8 +67,7 @@ defmodule Poxa.EventHandler do {[{{"application", "json", []}, :undefined}], req, state} end - def post(req, %{data: data}) do - event = PusherEvent.build(data) + def post(req, %{event: event}) do PusherEvent.publish(event) Event.notify(:api_message, %{channels: event.channels, name: event.name}) req = :cowboy_req.set_resp_body("{}", req) diff --git a/lib/poxa/pusher_event.ex b/lib/poxa/pusher_event.ex index 8067a1e..fcf59d5 100644 --- a/lib/poxa/pusher_event.ex +++ b/lib/poxa/pusher_event.ex @@ -130,20 +130,37 @@ defmodule Poxa.PusherEvent do @doc """ Builds the struct based on the decoded JSON from /events endpoint """ - @spec build(map) :: Poxa.PusherEvent.t + @spec build(map) :: {:ok, Poxa.PusherEvent.t} | {:error, atom} def build(%{"name" => name, "channels" => channels, "data" => data} = event) do - %Poxa.PusherEvent{channels: channels, data: data, name: name, socket_id: event["socket_id"]} + build_event(channels, data, name, event["socket_id"]) end def build(%{"name" => name, "channel" => channel, "data" => data} = event) do - %Poxa.PusherEvent{channels: [channel], data: data, name: name, socket_id: event["socket_id"]} + build_event([channel], data, name, event["socket_id"]) end + def build(_), do: {:error, :invalid_pusher_event} - def build_client_event(%{"event" => event, "channel" => channel, "data" => data}, socket_id) do - %Poxa.PusherEvent{channels: [channel], data: data, name: event, socket_id: socket_id} + @doc """ + Build client events + """ + @spec build_client_event(map, binary) :: {:ok, Poxa.PusherEvent.t} | {:error, atom} + def build_client_event(%{"event" => name, "channel" => channel, "data" => data}, socket_id) do + build_event([channel], data, name, socket_id) + end + def build_client_event(%{"name" => name, "channel" => channel, "data" => data}, socket_id) do + build_event([channel], data, name, socket_id) end - def build_client_event(%{"name" => event, "channel" => channel, "data" => data}, socket_id) do - %Poxa.PusherEvent{channels: [channel], data: data, name: event, socket_id: socket_id} + + defp build_event(channels, data, name, socket_id) do + event = %Poxa.PusherEvent{channels: channels, data: data, name: name, socket_id: socket_id} + if valid?(event), do: {:ok, event}, + else: {:error, :invalid_event} + end + + defp valid?(%Poxa.PusherEvent{channels: channels, data: data, name: event, socket_id: socket_id}) do + Enum.all?(channels, &Poxa.Channel.valid?(&1)) and + (!socket_id || Poxa.SocketId.valid?(socket_id)) end + defp valid?(_), do: false @doc """ Send `message` to `channels` excluding `exclude` diff --git a/lib/poxa/websocket_handler.ex b/lib/poxa/websocket_handler.ex index 34626a0..c6d268c 100644 --- a/lib/poxa/websocket_handler.ex +++ b/lib/poxa/websocket_handler.ex @@ -101,7 +101,7 @@ defmodule Poxa.WebsocketHandler do end # Client Events defp handle_pusher_event("client-" <> _event_name, decoded_json, req, %State{socket_id: socket_id} = state) do - event = PusherEvent.build_client_event(decoded_json, socket_id) + {:ok, event} = PusherEvent.build_client_event(decoded_json, socket_id) channel = List.first(event.channels) if Channel.private_or_presence?(channel) and Channel.subscribed?(channel, self) do PusherEvent.publish(event) diff --git a/test/event_handler_test.exs b/test/event_handler_test.exs index 8d20a1d..68717e5 100644 --- a/test/event_handler_test.exs +++ b/test/event_handler_test.exs @@ -12,11 +12,12 @@ defmodule Poxa.EventHandlerTest do end test "malformed_request with valid data" do - data = %{"name" => "", "data" => "", "channel" => ""} + event = %PusherEvent{} expect(:cowboy_req, :body, 1, {:ok, :body, :req1}) - expect(JSX, :decode, 1, {:ok, data}) + expect(JSX, :decode, 1, {:ok, :data}) + expect(PusherEvent, :build, [{[:data], {:ok, event}}]) - assert malformed_request(:req, :state) == {false, :req1, %{body: :body, data: data}} + assert malformed_request(:req, :state) == {false, :req1, %{body: :body, event: event}} assert validate :cowboy_req assert validate JSX @@ -34,10 +35,10 @@ defmodule Poxa.EventHandlerTest do end test "malformed_request with invalid data" do - data = %{"name" => "", "data" => ""} expect(:cowboy_req, :body, 1, {:ok, :body, :req1}) expect(:cowboy_req, :set_resp_body, 2, :req2) - expect(JSX, :decode, 1, {:ok, data}) + expect(JSX, :decode, 1, {:ok, :data}) + expect(PusherEvent, :build, [{[:data], {:error, :reason}}]) assert malformed_request(:req, :state) == {true, :req2, :state} @@ -71,28 +72,27 @@ defmodule Poxa.EventHandlerTest do end test "valid_entity_length with data key smaller than 10KB" do - json = %{"data" => "not10KB"} - assert valid_entity_length(:req, %{data: json}) == {true, :req, %{data: json}} + event = %PusherEvent{data: "not10KB"} + assert valid_entity_length(:req, %{event: event}) == {true, :req, %{event: event}} end test "valid_entity_length with data key bigger than 10KB" do data = File.read! "test/more_than_10KB.data" - json = %{"data" => data} + event = %PusherEvent{data: data} expect(:cowboy_req, :set_resp_body, 2, :req2) - assert valid_entity_length(:req, %{data: json}) == {false, :req2, %{data: json}} + assert valid_entity_length(:req, %{event: event}) == {false, :req2, %{event: event}} assert validate :cowboy_req end test "post event" do event = %PusherEvent{} - expect(PusherEvent, :build, 1, event) expect(PusherEvent, :publish, [{[event], :ok}]) expect(Event, :notify, 2, :ok) expect(:cowboy_req, :set_resp_body, 2, :req2) - assert post(:req, %{data: :data}) == {true, :req2, nil} + assert post(:req, %{event: event}) == {true, :req2, nil} assert validate PusherEvent assert validate :cowboy_req diff --git a/test/pusher_event_test.exs b/test/pusher_event_test.exs index 2eb12eb..0a2f909 100644 --- a/test/pusher_event_test.exs +++ b/test/pusher_event_test.exs @@ -53,22 +53,59 @@ defmodule Poxa.PusherEventTest do event = %{"channel" => "channel_name", "data" => "event_data", "name" => "event_etc"} - assert build(event) == %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"]} + assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"]}} end test "build PusherEvent with multiple channels" do event = %{"channels" => ["channel_name1", "channel_name2"], "data" => "event_data", "name" => "event_etc"} - assert build(event) == %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name1","channel_name2"]} + assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name1","channel_name2"]}} end test "build PusherEvent with excluding socket_id" do event = %{"channel" => "channel_name", "data" => "event_data", - "socket_id" => "socketId", + "socket_id" => "123.456", "name" => "event_etc"} - assert build(event) == %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "socketId"} + assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}} + end + + test "build PusherEvent with invalid socket_id" do + event = %{"channel" => "channel_name", + "data" => "event_data", + "socket_id" => "123:456", + "name" => "event_etc"} + assert build(event) == {:error, :invalid_event} + end + + test "build PusherEvent with invalid channel name" do + event = %{"channel" => "channel:name", + "data" => "event_data", + "socket_id" => "123.456", + "name" => "event_etc"} + assert build(event) == {:error, :invalid_event} + end + + test "build_client_event with excluding socket_id" do + event = %{"channel" => "channel_name", + "data" => "event_data", + "name" => "event_etc"} + assert build_client_event(event, "123.456") == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}} + end + + test "build_client_event with invalid excluding socket_id" do + event = %{"channel" => "channel_name", + "data" => "event_data", + "name" => "event_etc"} + assert build_client_event(event, "123:456") == {:error, :invalid_event} + end + + test "build_client_event with invalid channel name" do + event = %{"channel" => "channel:name", + "data" => "event_data", + "name" => "event_etc"} + assert build_client_event(event, "123.456") == {:error, :invalid_event} end test "sending message to a channel" do diff --git a/test/websocket_handler_test.exs b/test/websocket_handler_test.exs index 0c8aee3..09e8148 100644 --- a/test/websocket_handler_test.exs +++ b/test/websocket_handler_test.exs @@ -211,7 +211,7 @@ defmodule Poxa.WebsocketHandlerTest do decoded_json = %{"event" => "client-event"} event = %PusherEvent{channels: ["presence-channel"], name: "client-event"} expect(JSX, :decode!, [{[:client_event_json], decoded_json}]) - expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], event}]) + expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], {:ok, event}}]) expect(PusherEvent, :publish, 1, :ok) expect(Channel, :subscribed?, 2, true) expect(Event, :notify, [{[:client_event_message, %{socket_id: :socket_id, channels: ["presence-channel"], name: "client-event"}], :ok}]) @@ -231,7 +231,7 @@ defmodule Poxa.WebsocketHandlerTest do decoded_json = %{"event" => "client-event"} event = %PusherEvent{channels: ["private-channel"], name: "client-event"} expect(JSX, :decode!, [{[:client_event_json], decoded_json}]) - expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], event}]) + expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], {:ok, event}}]) expect(PusherEvent, :publish, 1, :ok) expect(Channel, :subscribed?, 2, true) expect(Event, :notify, [{[:client_event_message, %{socket_id: :socket_id, channels: ["private-channel"], name: "client-event"}], :ok}]) @@ -251,7 +251,7 @@ defmodule Poxa.WebsocketHandlerTest do decoded_json = %{"event" => "client-event"} event = %PusherEvent{channels: ["private-not-subscribed"], name: "client-event"} expect(JSX, :decode!, [{[:client_event_json], decoded_json}]) - expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], event}]) + expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], {:ok, event}}]) expect(Channel, :subscribed?, 2, false) state = %State{socket_id: :socket_id} @@ -268,7 +268,7 @@ defmodule Poxa.WebsocketHandlerTest do test "client event on public channel" do event = %PusherEvent{channels: ["public-channel"], name: "client-event"} expect(JSX, :decode!, 1, %{"event" => "client-event"}) - expect(PusherEvent, :build_client_event, 2, event) + expect(PusherEvent, :build_client_event, 2, {:ok, event}) state = %State{socket_id: :socket_id}