From 8fb73c28bbeccb6a7462e4a0e9fb58726b68bcf5 Mon Sep 17 00:00:00 2001 From: Roger Braun Date: Sat, 22 Apr 2017 13:44:21 +0200 Subject: [PATCH] Only have one subscription per callback. --- config/config.exs | 2 + config/test.exs | 2 + lib/pleroma/web/websub/websub.ex | 54 ++++++++++++++++++++- lib/pleroma/web/websub/websub_controller.ex | 41 ++-------------- test/web/websub/websub_controller_test.exs | 8 --- test/web/websub/websub_test.exs | 48 +++++++++++++++++- 6 files changed, 107 insertions(+), 48 deletions(-) diff --git a/config/config.exs b/config/config.exs index 18a2490a4..3826dddff 100644 --- a/config/config.exs +++ b/config/config.exs @@ -30,6 +30,8 @@ config :mime, :types, %{ "application/xrd+xml" => ["xrd+xml"] } +config :pleroma, :websub_verifier, Pleroma.Web.Websub + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env}.exs" diff --git a/config/test.exs b/config/test.exs index f5d6f240d..5d91279a2 100644 --- a/config/test.exs +++ b/config/test.exs @@ -24,3 +24,5 @@ config :pleroma, Pleroma.Repo, # Reduce hash rounds for testing config :comeonin, :pbkdf2_rounds, 1 + +config :pleroma, :websub_verifier, Pleroma.Web.WebsubMock diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 26a10788a..50878e3c4 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -3,12 +3,15 @@ defmodule Pleroma.Web.Websub do alias Pleroma.Websub alias Pleroma.Web.Websub.WebsubServerSubscription alias Pleroma.Web.OStatus.FeedRepresenter + alias Pleroma.Web.OStatus import Ecto.Query + @websub_verifier Application.get_env(:pleroma, :websub_verifier) + def verify(subscription, getter \\ &HTTPoison.get/3 ) do challenge = Base.encode16(:crypto.strong_rand_bytes(8)) - lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.inserted_at) |> to_string + lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at) |> to_string params = %{ "hub.challenge": challenge, @@ -48,4 +51,53 @@ defmodule Pleroma.Web.Websub do ]) end) end + + def incoming_subscription_request(user, params) do + with {:ok, topic} <- valid_topic(params, user), + {:ok, lease_time} <- lease_time(params), + secret <- params["hub.secret"], + callback <- params["hub.callback"] + do + subscription = get_subscription(topic, callback) + data = %{ + state: subscription.state || "requested", + topic: topic, + secret: secret, + callback: callback + } + + change = Ecto.Changeset.change(subscription, data) + websub = Repo.insert_or_update!(change) + + change = Ecto.Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)}) + websub = Repo.update!(change) + + # Just spawn that for now, maybe pool later. + spawn(fn -> @websub_verifier.verify(websub) end) + + {:ok, websub} + else {:error, reason} -> + {:error, reason} + end + end + + defp get_subscription(topic, callback) do + Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) || %WebsubServerSubscription{} + end + + defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do + {:ok, String.to_integer(lease_seconds)} + end + + defp lease_time(_) do + {:ok, 60 * 60 * 24 * 3} # three days + end + + defp valid_topic(%{"hub.topic" => topic}, user) do + if topic == OStatus.feed_path(user) do + {:ok, topic} + else + {:error, "Wrong topic requested, expected #{OStatus.feed_path(user)}, got #{topic}"} + end + end end diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex index 5766dff64..5d54c6ef5 100644 --- a/lib/pleroma/web/websub/websub_controller.ex +++ b/lib/pleroma/web/websub/websub_controller.ex @@ -1,32 +1,13 @@ defmodule Pleroma.Web.Websub.WebsubController do use Pleroma.Web, :controller - alias Pleroma.Web.Websub.WebsubServerSubscription - alias Pleroma.{Repo, User} - alias Pleroma.Web.OStatus + alias Pleroma.User alias Pleroma.Web.Websub + def websub_subscription_request(conn, %{"nickname" => nickname} = params) do user = User.get_cached_by_nickname(nickname) - with {:ok, topic} <- valid_topic(params, user), - {:ok, lease_time} <- lease_time(params), - secret <- params["hub.secret"] + with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do - data = %{ - state: "requested", - topic: topic, - secret: secret, - callback: params["hub.callback"] - } - - change = Ecto.Changeset.change(%WebsubServerSubscription{}, data) - websub = Repo.insert!(change) - - change = Ecto.Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.inserted_at, lease_time)}) - websub = Repo.update!(change) - - # Just spawn that for now, maybe pool later. - spawn(fn -> Websub.verify(websub) end) - conn |> send_resp(202, "Accepted") else {:error, reason} -> @@ -34,20 +15,4 @@ defmodule Pleroma.Web.Websub.WebsubController do |> send_resp(500, reason) end end - - defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do - {:ok, String.to_integer(lease_seconds)} - end - - defp lease_time(_) do - {:ok, 60 * 60 * 24 * 3} # three days - end - - defp valid_topic(%{"hub.topic" => topic}, user) do - if topic == OStatus.feed_path(user) do - {:ok, topic} - else - {:error, "Wrong topic requested, expected #{OStatus.feed_path(user)}, got #{topic}"} - end - end end diff --git a/test/web/websub/websub_controller_test.exs b/test/web/websub/websub_controller_test.exs index 584db0a19..9a0a5c61b 100644 --- a/test/web/websub/websub_controller_test.exs +++ b/test/web/websub/websub_controller_test.exs @@ -1,8 +1,6 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory - alias Pleroma.Repo - alias Pleroma.Web.Websub.WebsubServerSubscription test "websub subscription request", %{conn: conn} do user = insert(:user) @@ -21,11 +19,5 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do |> post(path, data) assert response(conn, 202) == "Accepted" - subscription = Repo.one!(WebsubServerSubscription) - assert subscription.topic == Pleroma.Web.OStatus.feed_path(user) - assert subscription.state == "requested" - assert subscription.secret == "a random secret" - assert subscription.callback == "http://example.org/sub" - assert subscription.valid_until == NaiveDateTime.add(subscription.inserted_at, 100) end end diff --git a/test/web/websub/websub_test.exs b/test/web/websub/websub_test.exs index 36ea82299..5fe91d0f8 100644 --- a/test/web/websub/websub_test.exs +++ b/test/web/websub/websub_test.exs @@ -1,6 +1,12 @@ +defmodule Pleroma.Web.WebsubMock do + def verify(sub) do + {:ok, sub} + end +end defmodule Pleroma.Web.WebsubTest do use Pleroma.DataCase alias Pleroma.Web.Websub + alias Pleroma.Web.Websub.WebsubServerSubscription import Pleroma.Factory test "a verification of a request that is accepted" do @@ -29,7 +35,6 @@ defmodule Pleroma.Web.WebsubTest do test "a verification of a request that doesn't return 200" do sub = insert(:websub_subscription) - topic = sub.topic getter = fn (_path, _headers, _options) -> {:ok, %HTTPoison.Response{ @@ -41,4 +46,45 @@ defmodule Pleroma.Web.WebsubTest do {:error, sub} = Websub.verify(sub, getter) assert sub.state == "rejected" end + + test "an incoming subscription request" do + user = insert(:user) + + data = %{ + "hub.callback" => "http://example.org/sub", + "hub.mode" => "subscription", + "hub.topic" => Pleroma.Web.OStatus.feed_path(user), + "hub.secret" => "a random secret", + "hub.lease_seconds" => "100" + } + + + {:ok, subscription } = Websub.incoming_subscription_request(user, data) + assert subscription.topic == Pleroma.Web.OStatus.feed_path(user) + assert subscription.state == "requested" + assert subscription.secret == "a random secret" + assert subscription.callback == "http://example.org/sub" + end + + test "an incoming subscription request for an existing subscription" do + user = insert(:user) + sub = insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user)) + + data = %{ + "hub.callback" => sub.callback, + "hub.mode" => "subscription", + "hub.topic" => Pleroma.Web.OStatus.feed_path(user), + "hub.secret" => "a random secret", + "hub.lease_seconds" => "100" + } + + + {:ok, subscription } = Websub.incoming_subscription_request(user, data) + assert subscription.topic == Pleroma.Web.OStatus.feed_path(user) + assert subscription.state == sub.state + assert subscription.secret == "a random secret" + assert subscription.callback == sub.callback + assert length(Repo.all(WebsubServerSubscription)) == 1 + assert subscription.id == sub.id + end end