@@ -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" |
@@ -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 |
@@ -9,6 +9,9 @@ defmodule Pleroma.Web.Endpoint do | |||
# when deploying your static files in production. | |||
plug Plug.Static, | |||
at: "/media", from: "uploads", gzip: false | |||
plug Plug.Static, | |||
at: "/", from: :pleroma, | |||
only: ~w(index.html static) | |||
# Code reloading can be explicitly enabled under the | |||
# :code_reloader configuration of your endpoint. | |||
@@ -0,0 +1,27 @@ | |||
defmodule Pleroma.Web.OStatus.ActivityRepresenter do | |||
def to_simple_form(%{data: %{"object" => %{"type" => "Note"}}} = activity, user) do | |||
h = fn(str) -> [to_charlist(str)] end | |||
updated_at = activity.updated_at | |||
|> NaiveDateTime.to_iso8601 | |||
inserted_at = activity.inserted_at | |||
|> NaiveDateTime.to_iso8601 | |||
attachments = Enum.map(activity.data["object"]["attachment"] || [], fn(attachment) -> | |||
url = hd(attachment["url"]) | |||
{:link, [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])], []} | |||
end) | |||
[ | |||
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, | |||
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']}, | |||
{:id, h.(activity.data["object"]["id"])}, | |||
{:title, ['New note by #{user.nickname}']}, | |||
{:content, [type: 'html'], h.(activity.data["object"]["content"])}, | |||
{:published, h.(inserted_at)}, | |||
{:updated, h.(updated_at)} | |||
] ++ attachments | |||
end | |||
def to_simple_form(_,_), do: nil | |||
end |
@@ -0,0 +1,31 @@ | |||
defmodule Pleroma.Web.OStatus.FeedRepresenter do | |||
alias Pleroma.Web.OStatus | |||
alias Pleroma.Web.OStatus.{UserRepresenter, ActivityRepresenter} | |||
def to_simple_form(user, activities, users) do | |||
most_recent_update = List.first(activities).updated_at | |||
|> NaiveDateTime.to_iso8601 | |||
h = fn(str) -> [to_charlist(str)] end | |||
entries = Enum.map(activities, fn(activity) -> | |||
{:entry, ActivityRepresenter.to_simple_form(activity, user)} | |||
end) | |||
|> Enum.filter(fn ({_, form}) -> form end) | |||
[{ | |||
:feed, [ | |||
xmlns: 'http://www.w3.org/2005/Atom', | |||
"xmlns:activity": 'http://activitystrea.ms/spec/1.0/', | |||
"xmlns:poco": 'http://portablecontacts.net/spec/1.0' | |||
], [ | |||
{:id, h.(OStatus.feed_path(user))}, | |||
{:title, ['#{user.nickname}\'s timeline']}, | |||
{:updated, h.(most_recent_update)}, | |||
{:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []}, | |||
{:link, [rel: 'self', href: h.(OStatus.feed_path(user))], []}, | |||
{:author, UserRepresenter.to_simple_form(user)}, | |||
] ++ entries | |||
}] | |||
end | |||
end |
@@ -0,0 +1,14 @@ | |||
defmodule Pleroma.Web.OStatus do | |||
alias Pleroma.Web | |||
def feed_path(user) do | |||
"#{user.ap_id}/feed.atom" | |||
end | |||
def pubsub_path(user) do | |||
"#{Web.base_url}/push/hub/#{user.nickname}" | |||
end | |||
def user_path(user) do | |||
end | |||
end |
@@ -0,0 +1,31 @@ | |||
defmodule Pleroma.Web.OStatus.OStatusController do | |||
use Pleroma.Web, :controller | |||
alias Pleroma.{User, Activity} | |||
alias Pleroma.Web.OStatus.FeedRepresenter | |||
alias Pleroma.Repo | |||
import Ecto.Query | |||
def feed(conn, %{"nickname" => nickname}) do | |||
user = User.get_cached_by_nickname(nickname) | |||
query = from activity in Activity, | |||
where: fragment("? @> ?", activity.data, ^%{actor: user.ap_id}), | |||
limit: 20, | |||
order_by: [desc: :inserted_at] | |||
activities = query | |||
|> Repo.all | |||
response = FeedRepresenter.to_simple_form(user, activities, [user]) | |||
|> :xmerl.export_simple(:xmerl_xml) | |||
|> to_string | |||
conn | |||
|> put_resp_content_type("application/atom+xml") | |||
|> send_resp(200, response) | |||
end | |||
def temp(conn, params) do | |||
IO.inspect(params) | |||
end | |||
end |
@@ -1,14 +1,20 @@ | |||
defmodule Pleroma.Web.OStatus.UserRepresenter do | |||
alias Pleroma.User | |||
def to_tuple(user, wrapper \\ :author) do | |||
{ | |||
wrapper, [ | |||
{ :id, user.ap_id }, | |||
{ :"activity:object", "http://activitystrea.ms/schema/1.0/person" }, | |||
{ :uri, user.ap_id }, | |||
{ :name, user.nickname }, | |||
{ :link, %{rel: "avatar", href: User.avatar_url(user)}} | |||
] | |||
} | |||
def to_simple_form(user) do | |||
ap_id = to_charlist(user.ap_id) | |||
nickname = to_charlist(user.nickname) | |||
name = to_charlist(user.name) | |||
bio = to_charlist(user.bio) | |||
avatar_url = to_charlist(User.avatar_url(user)) | |||
[ | |||
{ :id, [ap_id] }, | |||
{ :"activity:object", ['http://activitystrea.ms/schema/1.0/person'] }, | |||
{ :uri, [ap_id] }, | |||
{ :"poco:preferredUsername", [nickname] }, | |||
{ :"poco:displayName", [name] }, | |||
{ :"poco:note", [bio] }, | |||
{ :name, [nickname] }, | |||
{ :link, [rel: 'avatar', href: avatar_url], []} | |||
] | |||
end | |||
end |
@@ -66,10 +66,31 @@ defmodule Pleroma.Web.Router do | |||
post "/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar | |||
end | |||
pipeline :ostatus do | |||
plug :accepts, ["xml", "atom"] | |||
end | |||
scope "/", Pleroma.Web do | |||
pipe_through :ostatus | |||
get "/users/:nickname/feed", OStatus.OStatusController, :feed | |||
post "/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request | |||
end | |||
scope "/.well-known", Pleroma.Web do | |||
pipe_through :well_known | |||
get "/host-meta", WebFinger.WebFingerController, :host_meta | |||
get "/webfinger", WebFinger.WebFingerController, :webfinger | |||
end | |||
scope "/", Fallback do | |||
get "/*path", RedirectController, :redirector | |||
end | |||
end | |||
defmodule Fallback.RedirectController do | |||
use Pleroma.Web, :controller | |||
def redirector(conn, _params), do: send_file(conn, 200, "priv/static/index.html") | |||
end |
@@ -66,7 +66,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do | |||
end | |||
with {:ok, activity} <- ActivityPub.insert(activity) do | |||
add_conversation_id(activity) | |||
{:ok, activity} = add_conversation_id(activity) | |||
Pleroma.Web.Websub.publish(Pleroma.Web.OStatus.feed_path(user), user, activity) | |||
{:ok, activity} | |||
end | |||
end | |||
@@ -1,6 +1,7 @@ | |||
defmodule Pleroma.Web.WebFinger do | |||
alias Pleroma.XmlBuilder | |||
alias Pleroma.User | |||
alias Pleroma.Web.OStatus | |||
def host_meta() do | |||
base_url = Pleroma.Web.base_url | |||
@@ -30,7 +31,7 @@ defmodule Pleroma.Web.WebFinger do | |||
[ | |||
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.host}"}, | |||
{:Alias, user.ap_id}, | |||
{:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: "#{user.ap_id}.atom"}} | |||
{:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: OStatus.feed_path(user)}} | |||
] | |||
} | |||
|> XmlBuilder.to_doc | |||
@@ -0,0 +1,102 @@ | |||
defmodule Pleroma.Web.Websub do | |||
alias Pleroma.Repo | |||
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.updated_at) |> to_string | |||
params = %{ | |||
"hub.challenge": challenge, | |||
"hub.lease_seconds": lease_seconds, | |||
"hub.topic": subscription.topic, | |||
"hub.mode": "subscribe" | |||
} | |||
url = hd(String.split(subscription.callback, "?")) | |||
query = URI.parse(subscription.callback).query || "" | |||
params = Map.merge(params, URI.decode_query(query)) | |||
with {:ok, response} <- getter.(url, [], [params: params]), | |||
^challenge <- response.body | |||
do | |||
changeset = Ecto.Changeset.change(subscription, %{state: "active"}) | |||
Repo.update(changeset) | |||
else _e -> | |||
changeset = Ecto.Changeset.change(subscription, %{state: "rejected"}) | |||
{:ok, subscription } = Repo.update(changeset) | |||
{:error, subscription} | |||
end | |||
end | |||
def publish(topic, user, activity) do | |||
query = from sub in WebsubServerSubscription, | |||
where: sub.topic == ^topic and sub.state == "active" | |||
subscriptions = Repo.all(query) | |||
Enum.each(subscriptions, fn(sub) -> | |||
response = FeedRepresenter.to_simple_form(user, [activity], [user]) | |||
|> :xmerl.export_simple(:xmerl_xml) | |||
signature = :crypto.hmac(:sha, sub.secret, response) |> Base.encode16 | |||
HTTPoison.post(sub.callback, response, [ | |||
{"Content-Type", "application/atom+xml"}, | |||
{"X-Hub-Signature", "sha1=#{signature}"} | |||
]) | |||
end) | |||
end | |||
def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = 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 |
@@ -0,0 +1,18 @@ | |||
defmodule Pleroma.Web.Websub.WebsubController do | |||
use Pleroma.Web, :controller | |||
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, _websub} <- Websub.incoming_subscription_request(user, params) | |||
do | |||
conn | |||
|> send_resp(202, "Accepted") | |||
else {:error, reason} -> | |||
conn | |||
|> send_resp(500, reason) | |||
end | |||
end | |||
end |
@@ -0,0 +1,13 @@ | |||
defmodule Pleroma.Web.Websub.WebsubServerSubscription do | |||
use Ecto.Schema | |||
schema "websub_server_subscriptions" do | |||
field :topic, :string | |||
field :callback, :string | |||
field :secret, :string | |||
field :valid_until, :naive_datetime | |||
field :state, :string | |||
timestamps() | |||
end | |||
end |
@@ -39,6 +39,7 @@ defmodule Pleroma.Mixfile do | |||
{:html_sanitize_ex, "~> 1.0.0"}, | |||
{:calendar, "~> 0.16.1"}, | |||
{:cachex, "~> 2.1"}, | |||
{:httpoison, "~> 0.11.1"}, | |||
{:ex_machina, "~> 2.0", only: :test}, | |||
{:mix_test_watch, "~> 0.2", only: :dev}] | |||
end | |||
@@ -18,6 +18,7 @@ | |||
"gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [:mix], []}, | |||
"hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, optional: false]}, {:idna, "4.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, | |||
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.0.1", "2572e7122c78ab7e57b613e7c7f5e42bf9b3c25e430e32f23f1413d86db8a0af", [:mix], [{:mochiweb, "~> 2.12.2", [hex: :mochiweb, optional: false]}]}, | |||
"httpoison": {:hex, :httpoison, "0.11.1", "d06c571274c0e77b6cc50e548db3fd7779f611fbed6681fd60a331f66c143a0b", [:mix], [{:hackney, "~> 1.7.0", [hex: :hackney, optional: false]}]}, | |||
"idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []}, | |||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, | |||
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []}, | |||
@@ -0,0 +1,15 @@ | |||
defmodule Pleroma.Repo.Migrations.CreateWebsubServerSubscription do | |||
use Ecto.Migration | |||
def change do | |||
create table(:websub_server_subscriptions) do | |||
add :topic, :string | |||
add :callback, :string | |||
add :secret, :string | |||
add :valid_until, :naive_datetime | |||
add :state, :string | |||
timestamps() | |||
end | |||
end | |||
end |
@@ -3,7 +3,7 @@ defmodule Pleroma.Factory do | |||
def user_factory do | |||
user = %Pleroma.User{ | |||
name: sequence(:name, &"Test User #{&1}"), | |||
name: sequence(:name, &"Test テスト User #{&1}"), | |||
email: sequence(:email, &"user#{&1}@example.com"), | |||
nickname: sequence(:nickname, &"nick#{&1}"), | |||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), | |||
@@ -64,4 +64,14 @@ defmodule Pleroma.Factory do | |||
data: data | |||
} | |||
end | |||
def websub_subscription_factory do | |||
%Pleroma.Web.Websub.WebsubServerSubscription{ | |||
topic: "http://example.org", | |||
callback: "http://example/org/callback", | |||
secret: "here's a secret", | |||
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 100), | |||
state: "requested" | |||
} | |||
end | |||
end |
@@ -0,0 +1,43 @@ | |||
defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Web.OStatus.ActivityRepresenter | |||
alias Pleroma.{User, Activity} | |||
import Pleroma.Factory | |||
test "a note activity" do | |||
note_activity = insert(:note_activity) | |||
updated_at = note_activity.updated_at | |||
|> NaiveDateTime.to_iso8601 | |||
inserted_at = note_activity.inserted_at | |||
|> NaiveDateTime.to_iso8601 | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
expected = """ | |||
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> | |||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> | |||
<id>#{note_activity.data["object"]["id"]}</id> | |||
<title>New note by #{user.nickname}</title> | |||
<content type="html">#{note_activity.data["object"]["content"]}</content> | |||
<published>#{inserted_at}</published> | |||
<updated>#{updated_at}</updated> | |||
""" | |||
tuple = ActivityRepresenter.to_simple_form(note_activity, user) | |||
res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary | |||
assert clean(res) == clean(expected) | |||
end | |||
test "an unknown activity" do | |||
tuple = ActivityRepresenter.to_simple_form(%Activity{}, nil) | |||
assert is_nil(tuple) | |||
end | |||
defp clean(string) do | |||
String.replace(string, ~r/\s/, "") | |||
end | |||
end |
@@ -0,0 +1,45 @@ | |||
defmodule Pleroma.Web.OStatus.FeedRepresenterTest do | |||
use Pleroma.DataCase | |||
import Pleroma.Factory | |||
alias Pleroma.User | |||
alias Pleroma.Web.OStatus.{FeedRepresenter, UserRepresenter, ActivityRepresenter} | |||
alias Pleroma.Web.OStatus | |||
test "returns a feed of the last 20 items of the user" do | |||
note_activity = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user]) | |||
most_recent_update = note_activity.updated_at | |||
|> NaiveDateTime.to_iso8601 | |||
res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string | |||
user_xml = UserRepresenter.to_simple_form(user) | |||
|> :xmerl.export_simple_content(:xmerl_xml) | |||
entry_xml = ActivityRepresenter.to_simple_form(note_activity, user) | |||
|> :xmerl.export_simple_content(:xmerl_xml) | |||
expected = """ | |||
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0"> | |||
<id>#{OStatus.feed_path(user)}</id> | |||
<title>#{user.nickname}'s timeline</title> | |||
<updated>#{most_recent_update}</updated> | |||
<link rel="hub" href="#{OStatus.pubsub_path(user)}" /> | |||
<link rel="self" href="#{OStatus.feed_path(user)}" /> | |||
<author> | |||
#{user_xml} | |||
</author> | |||
<entry> | |||
#{entry_xml} | |||
</entry> | |||
</feed> | |||
""" | |||
assert clean(res) == clean(expected) | |||
end | |||
defp clean(string) do | |||
String.replace(string, ~r/\s/, "") | |||
end | |||
end |
@@ -0,0 +1,15 @@ | |||
defmodule Pleroma.Web.OStatus.OStatusControllerTest do | |||
use Pleroma.Web.ConnCase | |||
import Pleroma.Factory | |||
alias Pleroma.User | |||
test "gets a feed", %{conn: conn} do | |||
note_activity = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
conn = conn | |||
|> get("/users/#{user.nickname}/feed.atom") | |||
assert response(conn, 200) | |||
end | |||
end |
@@ -3,15 +3,29 @@ defmodule Pleroma.Web.OStatus.UserRepresenterTest do | |||
alias Pleroma.Web.OStatus.UserRepresenter | |||
import Pleroma.Factory | |||
alias Pleroma.User | |||
test "returns a user with id, uri, name and link" do | |||
user = build(:user) | |||
tuple = UserRepresenter.to_tuple(user) | |||
{:author, author} = tuple | |||
user = build(:user, nickname: "レイン") | |||
tuple = UserRepresenter.to_simple_form(user) | |||
[:id, :uri, :name, :link] | |||
|> Enum.each(fn (tag) -> | |||
assert Enum.find(author, fn(e) -> tag == elem(e, 0) end) | |||
end) | |||
res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string | |||
expected = """ | |||
<id>#{user.ap_id}</id> | |||
<activity:object>http://activitystrea.ms/schema/1.0/person</activity:object> | |||
<uri>#{user.ap_id}</uri> | |||
<poco:preferredUsername>#{user.nickname}</poco:preferredUsername> | |||
<poco:displayName>#{user.name}</poco:displayName> | |||
<poco:note>#{user.bio}</poco:note> | |||
<name>#{user.nickname}</name> | |||
<link rel="avatar" href="#{User.avatar_url(user)}" /> | |||
""" | |||
assert clean(res) == clean(expected) | |||
end | |||
defp clean(string) do | |||
String.replace(string, ~r/\s/, "") | |||
end | |||
end |
@@ -0,0 +1,23 @@ | |||
defmodule Pleroma.Web.Websub.WebsubControllerTest do | |||
use Pleroma.Web.ConnCase | |||
import Pleroma.Factory | |||
test "websub subscription request", %{conn: conn} do | |||
user = insert(:user) | |||
path = Pleroma.Web.OStatus.pubsub_path(user) | |||
data = %{ | |||
"hub.callback": "http://example.org/sub", | |||
"hub.mode": "subscribe", | |||
"hub.topic": Pleroma.Web.OStatus.feed_path(user), | |||
"hub.secret": "a random secret", | |||
"hub.lease_seconds": "100" | |||
} | |||
conn = conn | |||
|> post(path, data) | |||
assert response(conn, 202) == "Accepted" | |||
end | |||
end |
@@ -0,0 +1,90 @@ | |||
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 | |||
sub = insert(:websub_subscription) | |||
topic = sub.topic | |||
getter = fn (_path, _headers, options) -> | |||
%{ | |||
"hub.challenge": challenge, | |||
"hub.lease_seconds": seconds, | |||
"hub.topic": ^topic, | |||
"hub.mode": "subscribe" | |||
} = Keyword.get(options, :params) | |||
assert String.to_integer(seconds) > 0 | |||
{:ok, %HTTPoison.Response{ | |||
status_code: 200, | |||
body: challenge | |||
}} | |||
end | |||
{:ok, sub} = Websub.verify(sub, getter) | |||
assert sub.state == "active" | |||
end | |||
test "a verification of a request that doesn't return 200" do | |||
sub = insert(:websub_subscription) | |||
getter = fn (_path, _headers, _options) -> | |||
{:ok, %HTTPoison.Response{ | |||
status_code: 500, | |||
body: "" | |||
}} | |||
end | |||
{: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" => "subscribe", | |||
"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" => "subscribe", | |||
"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 |