Handle incoming websub subscriptions.
authorRoger Braun <roger@rogerbraun.net>
Fri, 28 Apr 2017 13:45:10 +0000 (15:45 +0200)
committerRoger Braun <roger@rogerbraun.net>
Fri, 28 Apr 2017 13:45:10 +0000 (15:45 +0200)
config/config.exs
config/test.exs
lib/pleroma/web/federator/federator.ex
lib/pleroma/web/router.ex
lib/pleroma/web/websub/websub.ex
lib/pleroma/web/websub/websub_controller.ex
test/web/websub/websub_controller_test.exs

index 3826dddff24bf89ceeb7626ad4fb3a0281a745b4..a5df31b5a50f9f64a89a94868375637fd9128f24 100644 (file)
@@ -30,7 +30,8 @@ config :mime, :types, %{
   "application/xrd+xml" => ["xrd+xml"]
 }
 
-config :pleroma, :websub_verifier, Pleroma.Web.Websub
+config :pleroma, :websub, Pleroma.Web.Websub
+config :pleroma, :ostatus, Pleroma.Web.OStatus
 
 # Import environment specific config. This must remain at the bottom
 # of this file so it overrides the configuration defined above.
index 5d91279a22ae32927b22760825d6d3e0b73d7e73..85b6ad26b4654834971515b0065f121a7e3f1d2c 100644 (file)
@@ -25,4 +25,5 @@ config :pleroma, Pleroma.Repo,
 # Reduce hash rounds for testing
 config :comeonin, :pbkdf2_rounds, 1
 
-config :pleroma, :websub_verifier, Pleroma.Web.WebsubMock
+config :pleroma, :websub, Pleroma.Web.WebsubMock
+config :pleroma, :ostatus, Pleroma.Web.OStatusMock
index f489ed837b812e6346a3605c6e4523ca75b2074d..38df13540e11788ccfbb338332e2465df441c6a4 100644 (file)
@@ -2,7 +2,7 @@ defmodule Pleroma.Web.Federator do
   alias Pleroma.User
   require Logger
 
-  @websub_verifier Application.get_env(:pleroma, :websub_verifier)
+  @websub Application.get_env(:pleroma, :websub)
 
   def handle(:publish, activity) do
     Logger.debug("Running publish for #{activity.data["id"]}")
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.Federator do
 
   def handle(:verify_websub, websub) do
     Logger.debug("Running websub verification for #{websub.id} (#{websub.topic}, #{websub.callback})")
-    @websub_verifier.verify(websub)
+    @websub.verify(websub)
   end
 
   def handle(type, payload) do
index bff981f9f58d2aa71680d6a43237071c9612d5ef..2ff75ec5dc745d86fc680df05eb4a7e7de2fe7f2 100644 (file)
@@ -75,8 +75,9 @@ defmodule Pleroma.Web.Router do
 
     get "/users/:nickname/feed", OStatus.OStatusController, :feed
     post "/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming
-    post "/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation
     post "/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request
+    get "/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation
+    post "/push/subscriptions/:id", Websub.WebsubController, :websub_incoming
   end
 
   scope "/.well-known", Pleroma.Web do
index ad352ee264da385785d5874f6ddc588dfb4c89e9..ad9e47b467c3e9bc64e049aac9a0f70fac8b1c74 100644 (file)
@@ -42,8 +42,7 @@ defmodule Pleroma.Web.Websub do
       response = FeedRepresenter.to_simple_form(user, [activity], [user])
       |> :xmerl.export_simple(:xmerl_xml)
 
-      signature = :crypto.hmac(:sha, sub.secret, response) |> Base.encode16
-
+      signature = sign(sub.secret, response)
       HTTPoison.post(sub.callback, response, [
             {"Content-Type", "application/atom+xml"},
             {"X-Hub-Signature", "sha1=#{signature}"}
@@ -51,6 +50,10 @@ defmodule Pleroma.Web.Websub do
     end)
   end
 
+  def sign(secret, doc) do
+    :crypto.hmac(:sha, secret, doc) |> Base.encode16
+  end
+
   def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do
     with {:ok, topic} <- valid_topic(params, user),
          {:ok, lease_time} <- lease_time(params),
index c6b15c0c2182d2a05c59ba22afcc59b8813762d7..cd59a70a39fd86244ce6f58724113fc3703335ba 100644 (file)
@@ -1,7 +1,11 @@
 defmodule Pleroma.Web.Websub.WebsubController do
   use Pleroma.Web, :controller
-  alias Pleroma.User
+  alias Pleroma.{Repo, User}
   alias Pleroma.Web.Websub
+  alias Pleroma.Web.Websub.WebsubClientSubscription
+  require Logger
+
+  @ostatus Application.get_env(:pleroma, :ostatus)
 
   def websub_subscription_request(conn, %{"nickname" => nickname} = params) do
     user = User.get_cached_by_nickname(nickname)
@@ -16,8 +20,30 @@ defmodule Pleroma.Web.Websub.WebsubController do
     end
   end
 
-  def websub_subscription_confirmation(conn, params) do
-    IO.inspect(params)
-    conn
+  def websub_subscription_confirmation(conn, %{"id" => id, "hub.mode" => "subscribe", "hub.challenge" => challenge, "hub.topic" => topic}) do
+    with %WebsubClientSubscription{} = websub <- Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do
+      change = Ecto.Changeset.change(websub, %{state: "accepted"})
+      {:ok, _websub} = Repo.update(change)
+      conn
+      |> send_resp(200, challenge)
+    else _e ->
+      conn
+      |> send_resp(500, "Error")
+    end
+  end
+
+  def websub_incoming(conn, %{"id" => id}) do
+    with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")),
+         %WebsubClientSubscription{} = websub <- Repo.get(WebsubClientSubscription, id),
+         {:ok, body, _conn} = read_body(conn),
+         ^signature <- Websub.sign(websub.secret, body) do
+      @ostatus.handle_incoming(body)
+      conn
+      |> send_resp(200, "OK")
+    else _e ->
+      Logger.debug("Can't handle incoming subscription post")
+      conn
+      |> send_resp(500, "Error")
+    end
   end
 end
index 8368cafea584ec76f1a49abba439d475253158f1..521bbb9aa58214720538ad7cd45ed18333e5cb1d 100644 (file)
@@ -1,6 +1,9 @@
 defmodule Pleroma.Web.Websub.WebsubControllerTest do
   use Pleroma.Web.ConnCase
   import Pleroma.Factory
+  alias Pleroma.Web.Websub.WebsubClientSubscription
+  alias Pleroma.{Repo, Activity}
+  alias Pleroma.Web.Websub
 
   test "websub subscription request", %{conn: conn} do
     user = insert(:user)
@@ -20,4 +23,60 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do
 
     assert response(conn, 202) == "Accepted"
   end
+
+  test "websub subscription confirmation", %{conn: conn} do
+    websub = insert(:websub_client_subscription)
+
+    params = %{
+      "hub.mode" => "subscribe",
+      "hub.topic" => websub.topic,
+      "hub.challenge" => "some challenge",
+      "hub.lease_seconds" => 100
+    }
+
+    conn = conn
+    |> get("/push/subscriptions/#{websub.id}", params)
+
+    websub = Repo.get(WebsubClientSubscription, websub.id)
+
+    assert response(conn, 200) == "some challenge"
+    assert websub.state == "accepted"
+  end
+
+  test "handles incoming feed updates", %{conn: conn} do
+    websub = insert(:websub_client_subscription)
+    doc = "some stuff"
+    signature = Websub.sign(websub.secret, doc)
+
+    conn = conn
+    |> put_req_header("x-hub-signature", "sha1=" <> signature)
+    |> put_req_header("content-type", "application/atom+xml")
+    |> post("/push/subscriptions/#{websub.id}", doc)
+
+    assert response(conn, 200) == "OK"
+
+    assert length(Repo.all(Activity)) == 1
+  end
+
+  test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
+    websub = insert(:websub_client_subscription)
+    doc = "some stuff"
+    signature = Websub.sign("wrong secret", doc)
+
+    conn = conn
+    |> put_req_header("x-hub-signature", "sha1=" <> signature)
+    |> put_req_header("content-type", "application/atom+xml")
+    |> post("/push/subscriptions/#{websub.id}", doc)
+
+    assert response(conn, 500) == "Error"
+
+    assert length(Repo.all(Activity)) == 0
+  end
+end
+
+defmodule Pleroma.Web.OStatusMock do
+  import Pleroma.Factory
+  def handle_incoming(_doc) do
+    insert(:note_activity)
+  end
 end