Merge branch 'phoenix14' into 'develop'

Upgrade to Phoenix 1.4

Closes #451

See merge request pleroma/pleroma!560
stable
lambda 6 years ago
commit 078b8b6d22

@ -50,6 +50,15 @@ config :pleroma, :uri_schemes,
# Configures the endpoint # Configures the endpoint
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],
http: [
dispatch: [
{:_,
[
{"/api/v1/streaming", Elixir.Pleroma.Web.MastodonAPI.WebsocketHandler, []},
{:_, Plug.Adapters.Cowboy.Handler, {Pleroma.Web.Endpoint, []}}
]}
]
],
protocol: "https", protocol: "https",
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
signing_salt: "CqaoopA2", signing_salt: "CqaoopA2",

@ -4,7 +4,8 @@ use Mix.Config
# you can enable the server option below. # you can enable the server option below.
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
http: [port: 4001], http: [port: 4001],
server: false url: [port: 4001],
server: true
# Print only warnings and errors during test # Print only warnings and errors during test
config :logger, level: :warn config :logger, level: :warn

@ -11,7 +11,8 @@ defmodule Pleroma.Web.FederatingPlug do
else else
conn conn
|> put_status(404) |> put_status(404)
|> Phoenix.Controller.render(Pleroma.Web.ErrorView, "404.json") |> Phoenix.Controller.put_view(Pleroma.Web.ErrorView)
|> Phoenix.Controller.render("404.json")
|> halt() |> halt()
end end
end end

@ -6,10 +6,6 @@ defmodule Pleroma.Web.UserSocket do
# channel "room:*", Pleroma.Web.RoomChannel # channel "room:*", Pleroma.Web.RoomChannel
channel("chat:*", Pleroma.Web.ChatChannel) channel("chat:*", Pleroma.Web.ChatChannel)
## Transports
transport(:websocket, Phoenix.Transports.WebSocket)
# transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can # Socket params are passed from the client and can
# be used to verify and authenticate a user. After # be used to verify and authenticate a user. After
# verification, you can put default assigns into # verification, you can put default assigns into

@ -3,8 +3,6 @@ defmodule Pleroma.Web.Endpoint do
socket("/socket", Pleroma.Web.UserSocket) socket("/socket", Pleroma.Web.UserSocket)
socket("/api/v1", Pleroma.Web.MastodonAPI.MastodonSocket)
# Serve at "/" the static files from "priv/static" directory. # Serve at "/" the static files from "priv/static" directory.
# #
# You should set gzip to true if you are running phoenix.digest # You should set gzip to true if you are running phoenix.digest

@ -226,7 +226,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn conn
|> add_link_headers(:home_timeline, activities) |> add_link_headers(:home_timeline, activities)
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
end end
def public_timeline(%{assigns: %{user: user}} = conn, params) do def public_timeline(%{assigns: %{user: user}} = conn, params) do
@ -244,7 +245,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn conn
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only}) |> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
end end
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
@ -259,7 +261,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn conn
|> add_link_headers(:user_statuses, activities, params["id"]) |> add_link_headers(:user_statuses, activities, params["id"])
|> render(StatusView, "index.json", %{ |> put_view(StatusView)
|> render("index.json", %{
activities: activities, activities: activities,
for: reading_user, for: reading_user,
as: :activity as: :activity
@ -278,13 +281,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn conn
|> add_link_headers(:dm_timeline, activities) |> add_link_headers(:dm_timeline, activities)
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
end end
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id), with %Activity{} = activity <- Repo.get(Activity, id),
true <- ActivityPub.visible_for_user?(activity, user) do true <- ActivityPub.visible_for_user?(activity, user) do
try_render(conn, StatusView, "status.json", %{activity: activity, for: user}) conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user})
end end
end end
@ -347,7 +353,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
{:ok, activity} = {:ok, activity} =
Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end) Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end)
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
end end
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
@ -363,28 +371,36 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user) do with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user) do
try_render(conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity}) conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
end end
end end
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
end end
end end
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user), with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
end end
end end
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user), with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
end end
end end
@ -433,7 +449,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
id = List.wrap(id) id = List.wrap(id)
q = from(u in User, where: u.id in ^id) q = from(u in User, where: u.id in ^id)
targets = Repo.all(q) targets = Repo.all(q)
render(conn, AccountView, "relationships.json", %{user: user, targets: targets})
conn
|> put_view(AccountView)
|> render("relationships.json", %{user: user, targets: targets})
end end
# Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array. # Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array.
@ -452,7 +471,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Repo.update() |> Repo.update()
attachment_data = Map.put(new_data, "id", object.id) attachment_data = Map.put(new_data, "id", object.id)
render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
conn
|> put_view(StatusView)
|> render("attachment.json", %{attachment: attachment_data})
end end
end end
@ -463,7 +485,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
description: Map.get(data, "description") description: Map.get(data, "description")
) do ) do
attachment_data = Map.put(object.data, "id", object.id) attachment_data = Map.put(object.data, "id", object.id)
render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
conn
|> put_view(StatusView)
|> render("attachment.json", %{attachment: attachment_data})
end end
end end
@ -471,7 +496,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do
q = from(u in User, where: u.ap_id in ^likes) q = from(u in User, where: u.ap_id in ^likes)
users = Repo.all(q) users = Repo.all(q)
render(conn, AccountView, "accounts.json", %{users: users, as: :user})
conn
|> put_view(AccountView)
|> render(AccountView, "accounts.json", %{users: users, as: :user})
else else
_ -> json(conn, []) _ -> json(conn, [])
end end
@ -481,7 +509,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do
q = from(u in User, where: u.ap_id in ^announces) q = from(u in User, where: u.ap_id in ^announces)
users = Repo.all(q) users = Repo.all(q)
render(conn, AccountView, "accounts.json", %{users: users, as: :user})
conn
|> put_view(AccountView)
|> render("accounts.json", %{users: users, as: :user})
else else
_ -> json(conn, []) _ -> json(conn, [])
end end
@ -503,7 +534,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn conn
|> add_link_headers(:hashtag_timeline, activities, params["tag"], %{"local" => local_only}) |> add_link_headers(:hashtag_timeline, activities, params["tag"], %{"local" => local_only})
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
end end
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
@ -516,7 +548,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
true -> followers true -> followers
end end
render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) conn
|> put_view(AccountView)
|> render("accounts.json", %{users: followers, as: :user})
end end
end end
@ -530,13 +564,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
true -> followers true -> followers
end end
render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) conn
|> put_view(AccountView)
|> render("accounts.json", %{users: followers, as: :user})
end end
end end
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
with {:ok, follow_requests} <- User.get_follow_requests(followed) do with {:ok, follow_requests} <- User.get_follow_requests(followed) do
render(conn, AccountView, "accounts.json", %{users: follow_requests, as: :user}) conn
|> put_view(AccountView)
|> render("accounts.json", %{users: follow_requests, as: :user})
end end
end end
@ -552,7 +590,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
object: follow_activity.data["id"], object: follow_activity.data["id"],
type: "Accept" type: "Accept"
}) do }) do
render(conn, AccountView, "relationship.json", %{user: followed, target: follower}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: followed, target: follower})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -572,7 +612,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
object: follow_activity.data["id"], object: follow_activity.data["id"],
type: "Reject" type: "Reject"
}) do }) do
render(conn, AccountView, "relationship.json", %{user: followed, target: follower}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: followed, target: follower})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -591,7 +633,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
follower, follower,
followed followed
) do ) do
render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: follower, target: followed})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -604,7 +648,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %User{} = followed <- Repo.get_by(User, nickname: uri), with %User{} = followed <- Repo.get_by(User, nickname: uri),
{:ok, follower} <- User.maybe_direct_follow(follower, followed), {:ok, follower} <- User.maybe_direct_follow(follower, followed),
{:ok, _activity} <- ActivityPub.follow(follower, followed) do {:ok, _activity} <- ActivityPub.follow(follower, followed) do
render(conn, AccountView, "account.json", %{user: followed, for: follower}) conn
|> put_view(AccountView)
|> render("account.json", %{user: followed, for: follower})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -617,7 +663,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %User{} = followed <- Repo.get(User, id), with %User{} = followed <- Repo.get(User, id),
{:ok, _activity} <- ActivityPub.unfollow(follower, followed), {:ok, _activity} <- ActivityPub.unfollow(follower, followed),
{:ok, follower, _} <- User.unfollow(follower, followed) do {:ok, follower, _} <- User.unfollow(follower, followed) do
render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: follower, target: followed})
end end
end end
@ -625,7 +673,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %User{} = blocked <- Repo.get(User, id), with %User{} = blocked <- Repo.get(User, id),
{:ok, blocker} <- User.block(blocker, blocked), {:ok, blocker} <- User.block(blocker, blocked),
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do {:ok, _activity} <- ActivityPub.block(blocker, blocked) do
render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: blocker, target: blocked})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -638,7 +688,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %User{} = blocked <- Repo.get(User, id), with %User{} = blocked <- Repo.get(User, id),
{:ok, blocker} <- User.unblock(blocker, blocked), {:ok, blocker} <- User.unblock(blocker, blocked),
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: blocker, target: blocked})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -763,7 +815,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Enum.reverse() |> Enum.reverse()
conn conn
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
end end
def get_lists(%{assigns: %{user: user}} = conn, opts) do def get_lists(%{assigns: %{user: user}} = conn, opts) do
@ -831,7 +884,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Pleroma.List{} = list <- Pleroma.List.get(id, user), with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
{:ok, users} = Pleroma.List.get_following(list) do {:ok, users} = Pleroma.List.get_following(list) do
render(conn, AccountView, "accounts.json", %{users: users, as: :user}) conn
|> put_view(AccountView)
|> render("accounts.json", %{users: users, as: :user})
end end
end end
@ -864,7 +919,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Enum.reverse() |> Enum.reverse()
conn conn
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
else else
_e -> _e ->
conn conn
@ -968,7 +1024,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn conn
|> put_layout(false) |> put_layout(false)
|> render(MastodonView, "index.html", %{initial_state: initial_state}) |> put_view(MastodonView)
|> render("index.html", %{initial_state: initial_state})
else else
conn conn
|> redirect(to: "/web/login") |> redirect(to: "/web/login")
@ -1041,7 +1098,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
Logger.debug("Unimplemented, returning unmodified relationship") Logger.debug("Unimplemented, returning unmodified relationship")
with %User{} = target <- Repo.get(User, id) do with %User{} = target <- Repo.get(User, id) do
render(conn, AccountView, "relationship.json", %{user: user, target: target}) conn
|> put_view(AccountView)
|> render("relationship.json", %{user: user, target: target})
end end
end end
@ -1242,9 +1301,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
end end
def try_render(conn, renderer, target, params) def try_render(conn, target, params)
when is_binary(target) do when is_binary(target) do
res = render(conn, renderer, target, params) res = render(conn, target, params)
if res == nil do if res == nil do
conn conn
@ -1255,7 +1314,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
end end
def try_render(conn, _, _, _) do def try_render(conn, _, _) do
conn conn
|> put_status(501) |> put_status(501)
|> json(%{error: "Can't display this activity"}) |> json(%{error: "Can't display this activity"})

@ -1,81 +0,0 @@
defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
use Phoenix.Socket
alias Pleroma.Web.OAuth.Token
alias Pleroma.{User, Repo}
transport(
:websocket,
Phoenix.Transports.WebSocket.Raw,
# We never receive data.
timeout: :infinity
)
@spec connect(params :: map(), Phoenix.Socket.t()) :: {:ok, Phoenix.Socket.t()} | :error
def connect(%{"access_token" => token} = params, socket) do
with %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
%User{} = user <- Repo.get(User, user_id),
stream
when stream in [
"public",
"public:local",
"public:media",
"public:local:media",
"user",
"direct",
"list",
"hashtag"
] <- params["stream"] do
topic =
case stream do
"hashtag" -> "hashtag:#{params["tag"]}"
"list" -> "list:#{params["list"]}"
_ -> stream
end
socket =
socket
|> assign(:topic, topic)
|> assign(:user, user)
Pleroma.Web.Streamer.add_socket(topic, socket)
{:ok, socket}
else
_e -> :error
end
end
def connect(%{"stream" => stream} = params, socket)
when stream in ["public", "public:local", "hashtag"] do
topic =
case stream do
"hashtag" -> "hashtag:#{params["tag"]}"
_ -> stream
end
socket =
socket
|> assign(:topic, topic)
Pleroma.Web.Streamer.add_socket(topic, socket)
{:ok, socket}
end
def connect(_params, _socket), do: :error
def id(_), do: nil
def handle(:text, message, _state) do
# | :ok
# | state
# | {:text, message}
# | {:text, message, state}
# | {:close, "Goodbye!"}
{:text, message}
end
def handle(:closed, _, %{socket: socket}) do
topic = socket.assigns[:topic]
Pleroma.Web.Streamer.remove_socket(topic, socket)
end
end

@ -0,0 +1,120 @@
defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
require Logger
alias Pleroma.Web.OAuth.Token
alias Pleroma.{User, Repo}
@behaviour :cowboy_websocket_handler
@streams [
"public",
"public:local",
"public:media",
"public:local:media",
"user",
"direct",
"list",
"hashtag"
]
@anonymous_streams ["public", "public:local", "hashtag"]
# Handled by periodic keepalive in Pleroma.Web.Streamer.
@timeout :infinity
def init(_type, _req, _opts) do
{:upgrade, :protocol, :cowboy_websocket}
end
def websocket_init(_type, req, _opts) do
with {qs, req} <- :cowboy_req.qs(req),
params <- :cow_qs.parse_qs(qs),
access_token <- List.keyfind(params, "access_token", 0),
{_, stream} <- List.keyfind(params, "stream", 0),
{:ok, user} <- allow_request(stream, access_token),
topic when is_binary(topic) <- expand_topic(stream, params) do
send(self(), :subscribe)
{:ok, req, %{user: user, topic: topic}, @timeout}
else
{:error, code} ->
Logger.debug("#{__MODULE__} denied connection: #{inspect(code)} - #{inspect(req)}")
{:ok, req} = :cowboy_req.reply(code, req)
{:shutdown, req}
error ->
Logger.debug("#{__MODULE__} denied connection: #{inspect(error)} - #{inspect(req)}")
{:shutdown, req}
end
end
# We never receive messages.
def websocket_handle(_frame, req, state) do
{:ok, req, state}
end
def websocket_info(:subscribe, req, state) do
Logger.debug(
"#{__MODULE__} accepted websocket connection for user #{
(state.user || %{id: "anonymous"}).id
}, topic #{state.topic}"
)
Pleroma.Web.Streamer.add_socket(state.topic, streamer_socket(state))
{:ok, req, state}
end
def websocket_info({:text, message}, req, state) do
{:reply, {:text, message}, req, state}
end
def websocket_terminate(reason, _req, state) do
Logger.debug(
"#{__MODULE__} terminating websocket connection for user #{
(state.user || %{id: "anonymous"}).id
}, topic #{state.topic || "?"}: #{inspect(reason)}"
)
Pleroma.Web.Streamer.remove_socket(state.topic, streamer_socket(state))
:ok
end
# Public streams without authentication.
defp allow_request(stream, nil) when stream in @anonymous_streams do
{:ok, nil}
end
# Authenticated streams.
defp allow_request(stream, {"access_token", access_token}) when stream in @streams do
with %Token{user_id: user_id} <- Repo.get_by(Token, token: access_token),
user = %User{} <- Repo.get(User, user_id) do
{:ok, user}
else
_ -> {:error, 403}
end
end
# Not authenticated.
defp allow_request(stream, _) when stream in @streams, do: {:error, 403}
# No matching stream.
defp allow_request(_, _), do: {:error, 404}
defp expand_topic("hashtag", params) do
case List.keyfind(params, "tag", 0) do
{_, tag} -> "hashtag:#{tag}"
_ -> nil
end
end
defp expand_topic("list", params) do
case List.keyfind(params, "list", 0) do
{_, list} -> "list:#{list}"
_ -> nil
end
end
defp expand_topic(topic, _), do: topic
defp streamer_socket(state) do
%{transport_pid: self(), assigns: state}
end
end

@ -4,17 +4,9 @@ defmodule Pleroma.Web.Streamer do
alias Pleroma.{User, Notification, Activity, Object, Repo} alias Pleroma.{User, Notification, Activity, Object, Repo}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
def init(args) do @keepalive_interval :timer.seconds(30)
{:ok, args}
end
def start_link do def start_link do
spawn(fn ->
# 30 seconds
Process.sleep(1000 * 30)
GenServer.cast(__MODULE__, %{action: :ping})
end)
GenServer.start_link(__MODULE__, %{}, name: __MODULE__) GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end end
@ -30,6 +22,16 @@ defmodule Pleroma.Web.Streamer do
GenServer.cast(__MODULE__, %{action: :stream, topic: topic, item: item}) GenServer.cast(__MODULE__, %{action: :stream, topic: topic, item: item})
end end
def init(args) do
spawn(fn ->
# 30 seconds
Process.sleep(@keepalive_interval)
GenServer.cast(__MODULE__, %{action: :ping})
end)
{:ok, args}
end
def handle_cast(%{action: :ping}, topics) do def handle_cast(%{action: :ping}, topics) do
Map.values(topics) Map.values(topics)
|> List.flatten() |> List.flatten()
@ -40,7 +42,7 @@ defmodule Pleroma.Web.Streamer do
spawn(fn -> spawn(fn ->
# 30 seconds # 30 seconds
Process.sleep(1000 * 30) Process.sleep(@keepalive_interval)
GenServer.cast(__MODULE__, %{action: :ping}) GenServer.cast(__MODULE__, %{action: :ping})
end) end)

@ -17,7 +17,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def verify_credentials(%{assigns: %{user: user}} = conn, _params) do def verify_credentials(%{assigns: %{user: user}} = conn, _params) do
token = Phoenix.Token.sign(conn, "user socket", user.id) token = Phoenix.Token.sign(conn, "user socket", user.id)
render(conn, UserView, "show.json", %{user: user, token: token})
conn
|> put_view(UserView)
|> render("show.json", %{user: user, token: token})
end end
def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do
@ -58,7 +61,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
activities = ActivityPub.fetch_public_activities(params) activities = ActivityPub.fetch_public_activities(params)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
def public_timeline(%{assigns: %{user: user}} = conn, params) do def public_timeline(%{assigns: %{user: user}} = conn, params) do
@ -71,7 +75,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
activities = ActivityPub.fetch_public_activities(params) activities = ActivityPub.fetch_public_activities(params)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
def friends_timeline(%{assigns: %{user: user}} = conn, params) do def friends_timeline(%{assigns: %{user: user}} = conn, params) do
@ -86,16 +91,22 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|> ActivityPub.contain_timeline(user) |> ActivityPub.contain_timeline(user)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
def show_user(conn, params) do def show_user(conn, params) do
with {:ok, shown} <- TwitterAPI.get_user(params) do with {:ok, shown} <- TwitterAPI.get_user(params) do
if user = conn.assigns.user do params =
render(conn, UserView, "show.json", %{user: shown, for: user}) if user = conn.assigns.user do
else %{user: shown, for: user}
render(conn, UserView, "show.json", %{user: shown}) else
end %{user: shown}
end
conn
|> put_view(UserView)
|> render("show.json", params)
else else
{:error, msg} -> {:error, msg} ->
bad_request_reply(conn, msg) bad_request_reply(conn, msg)
@ -108,7 +119,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
activities = ActivityPub.fetch_user_activities(target_user, user, params) activities = ActivityPub.fetch_user_activities(target_user, user, params)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
{:error, msg} -> {:error, msg} ->
bad_request_reply(conn, msg) bad_request_reply(conn, msg)
@ -124,7 +136,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
activities = ActivityPub.fetch_activities([user.ap_id], params) activities = ActivityPub.fetch_activities([user.ap_id], params)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
def dm_timeline(%{assigns: %{user: user}} = conn, params) do def dm_timeline(%{assigns: %{user: user}} = conn, params) do
@ -137,14 +150,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
activities = Repo.all(query) activities = Repo.all(query)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
def notifications(%{assigns: %{user: user}} = conn, params) do def notifications(%{assigns: %{user: user}} = conn, params) do
notifications = Notification.for_user(user, params) notifications = Notification.for_user(user, params)
conn conn
|> render(NotificationView, "notification.json", %{notifications: notifications, for: user}) |> put_view(NotificationView)
|> render("notification.json", %{notifications: notifications, for: user})
end end
def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do
@ -153,7 +168,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
notifications = Notification.for_user(user, params) notifications = Notification.for_user(user, params)
conn conn
|> render(NotificationView, "notification.json", %{notifications: notifications, for: user}) |> put_view(NotificationView)
|> render("notification.json", %{notifications: notifications, for: user})
end end
def notifications_read(%{assigns: %{user: _user}} = conn, _) do def notifications_read(%{assigns: %{user: _user}} = conn, _) do
@ -163,7 +179,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def follow(%{assigns: %{user: user}} = conn, params) do def follow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.follow(user, params) do case TwitterAPI.follow(user, params) do
{:ok, user, followed, _activity} -> {:ok, user, followed, _activity} ->
render(conn, UserView, "show.json", %{user: followed, for: user}) conn
|> put_view(UserView)
|> render("show.json", %{user: followed, for: user})
{:error, msg} -> {:error, msg} ->
forbidden_json_reply(conn, msg) forbidden_json_reply(conn, msg)
@ -173,7 +191,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def block(%{assigns: %{user: user}} = conn, params) do def block(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.block(user, params) do case TwitterAPI.block(user, params) do
{:ok, user, blocked} -> {:ok, user, blocked} ->
render(conn, UserView, "show.json", %{user: blocked, for: user}) conn
|> put_view(UserView)
|> render("show.json", %{user: blocked, for: user})
{:error, msg} -> {:error, msg} ->
forbidden_json_reply(conn, msg) forbidden_json_reply(conn, msg)
@ -183,7 +203,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def unblock(%{assigns: %{user: user}} = conn, params) do def unblock(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.unblock(user, params) do case TwitterAPI.unblock(user, params) do
{:ok, user, blocked} -> {:ok, user, blocked} ->
render(conn, UserView, "show.json", %{user: blocked, for: user}) conn
|> put_view(UserView)
|> render("show.json", %{user: blocked, for: user})
{:error, msg} -> {:error, msg} ->
forbidden_json_reply(conn, msg) forbidden_json_reply(conn, msg)
@ -192,14 +214,18 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, activity} <- TwitterAPI.delete(user, id) do with {:ok, activity} <- TwitterAPI.delete(user, id) do
render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) conn
|> put_view(ActivityView)
|> render("activity.json", %{activity: activity, for: user})
end end
end end
def unfollow(%{assigns: %{user: user}} = conn, params) do def unfollow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.unfollow(user, params) do case TwitterAPI.unfollow(user, params) do
{:ok, user, unfollowed} -> {:ok, user, unfollowed} ->
render(conn, UserView, "show.json", %{user: unfollowed, for: user}) conn
|> put_view(UserView)
|> render("show.json", %{user: unfollowed, for: user})
{:error, msg} -> {:error, msg} ->
forbidden_json_reply(conn, msg) forbidden_json_reply(conn, msg)
@ -209,7 +235,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id), with %Activity{} = activity <- Repo.get(Activity, id),
true <- ActivityPub.visible_for_user?(activity, user) do true <- ActivityPub.visible_for_user?(activity, user) do
render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) conn
|> put_view(ActivityView)
|> render("activity.json", %{activity: activity, for: user})
end end
end end
@ -223,7 +251,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
"user" => user "user" => user
}) do }) do
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
end end
@ -290,34 +319,44 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)}, with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
{:ok, activity} <- TwitterAPI.fav(user, id) do {:ok, activity} <- TwitterAPI.fav(user, id) do
render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) conn
|> put_view(ActivityView)
|> render("activity.json", %{activity: activity, for: user})
end end
end end
def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)}, with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
{:ok, activity} <- TwitterAPI.unfav(user, id) do {:ok, activity} <- TwitterAPI.unfav(user, id) do
render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) conn
|> put_view(ActivityView)
|> render("activity.json", %{activity: activity, for: user})
end end
end end
def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)}, with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
{:ok, activity} <- TwitterAPI.repeat(user, id) do {:ok, activity} <- TwitterAPI.repeat(user, id) do
render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) conn
|> put_view(ActivityView)
|> render("activity.json", %{activity: activity, for: user})
end end
end end
def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)}, with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
{:ok, activity} <- TwitterAPI.unrepeat(user, id) do {:ok, activity} <- TwitterAPI.unrepeat(user, id) do
render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) conn
|> put_view(ActivityView)
|> render("activity.json", %{activity: activity, for: user})
end end
end end
def register(conn, params) do def register(conn, params) do
with {:ok, user} <- TwitterAPI.register_user(params) do with {:ok, user} <- TwitterAPI.register_user(params) do
render(conn, UserView, "show.json", %{user: user}) conn
|> put_view(UserView)
|> render("show.json", %{user: user})
else else
{:error, errors} -> {:error, errors} ->
conn conn
@ -339,7 +378,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
{:ok, user} = User.update_and_set_cache(change) {:ok, user} = User.update_and_set_cache(change)
CommonAPI.update(user) CommonAPI.update(user)
render(conn, UserView, "show.json", %{user: user, for: user}) conn
|> put_view(UserView)
|> render("show.json", %{user: user, for: user})
end end
def update_banner(%{assigns: %{user: user}} = conn, params) do def update_banner(%{assigns: %{user: user}} = conn, params) do
@ -394,7 +435,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
true -> followers true -> followers
end end
render(conn, UserView, "index.json", %{users: followers, for: conn.assigns[:user]}) conn
|> put_view(UserView)
|> render("index.json", %{users: followers, for: conn.assigns[:user]})
else else
_e -> bad_request_reply(conn, "Can't get followers") _e -> bad_request_reply(conn, "Can't get followers")
end end
@ -410,7 +453,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
true -> friends true -> friends
end end
render(conn, UserView, "index.json", %{users: friends, for: conn.assigns[:user]}) conn
|> put_view(UserView)
|> render("index.json", %{users: friends, for: conn.assigns[:user]})
else else
_e -> bad_request_reply(conn, "Can't get friends") _e -> bad_request_reply(conn, "Can't get friends")
end end
@ -419,7 +464,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def friend_requests(conn, params) do def friend_requests(conn, params) do
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
{:ok, friend_requests} <- User.get_follow_requests(user) do {:ok, friend_requests} <- User.get_follow_requests(user) do
render(conn, UserView, "index.json", %{users: friend_requests, for: conn.assigns[:user]}) conn
|> put_view(UserView)
|> render("index.json", %{users: friend_requests, for: conn.assigns[:user]})
else else
_e -> bad_request_reply(conn, "Can't get friend requests") _e -> bad_request_reply(conn, "Can't get friend requests")
end end
@ -439,7 +486,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
object: follow_activity.data["id"], object: follow_activity.data["id"],
type: "Accept" type: "Accept"
}) do }) do
render(conn, UserView, "show.json", %{user: follower, for: followed}) conn
|> put_view(UserView)
|> render("show.json", %{user: follower, for: followed})
else else
e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}") e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}")
end end
@ -458,7 +507,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
object: follow_activity.data["id"], object: follow_activity.data["id"],
type: "Reject" type: "Reject"
}) do }) do
render(conn, UserView, "show.json", %{user: follower, for: followed}) conn
|> put_view(UserView)
|> render("show.json", %{user: follower, for: followed})
else else
e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}") e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}")
end end
@ -522,7 +573,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
{:ok, user} <- User.update_and_set_cache(changeset) do {:ok, user} <- User.update_and_set_cache(changeset) do
CommonAPI.update(user) CommonAPI.update(user)
render(conn, UserView, "user.json", %{user: user, for: user})
conn
|> put_view(UserView)
|> render("user.json", %{user: user, for: user})
else else
error -> error ->
Logger.debug("Can't update user: #{inspect(error)}") Logger.debug("Can't update user: #{inspect(error)}")
@ -534,14 +588,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
activities = TwitterAPI.search(user, params) activities = TwitterAPI.search(user, params)
conn conn
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> put_view(ActivityView)
|> render("index.json", %{activities: activities, for: user})
end end
def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
users = User.search(query, true) users = User.search(query, true)
conn conn
|> render(UserView, "index.json", %{users: users, for: user}) |> put_view(UserView)
|> render("index.json", %{users: users, for: user})
end end
defp bad_request_reply(conn, error_message) do defp bad_request_reply(conn, error_message) do

@ -43,12 +43,13 @@ defmodule Pleroma.Mixfile do
# Type `mix help deps` for examples and options. # Type `mix help deps` for examples and options.
defp deps do defp deps do
[ [
{:phoenix, "~> 1.3.3"}, # Until Phoenix 1.4.1 is released
{:phoenix_pubsub, "~> 1.0.2"}, {:phoenix, github: "phoenixframework/phoenix", branch: "v1.4"},
{:plug_cowboy, "~> 1.0"},
{:phoenix_pubsub, "~> 1.1"},
{:phoenix_ecto, "~> 3.3"}, {:phoenix_ecto, "~> 3.3"},
{:postgrex, ">= 0.13.5"}, {:postgrex, ">= 0.13.5"},
{:gettext, "~> 0.15"}, {:gettext, "~> 0.15"},
{:cowboy, "~> 1.1.2", override: true},
{:comeonin, "~> 4.1.1"}, {:comeonin, "~> 4.1.1"},
{:pbkdf2_elixir, "~> 0.12.3"}, {:pbkdf2_elixir, "~> 0.12.3"},
{:trailing_format_plug, "~> 0.0.7"}, {:trailing_format_plug, "~> 0.0.7"},
@ -72,7 +73,8 @@ defmodule Pleroma.Mixfile do
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false}, {:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"}, {:web_push_encryption, "~> 0.2.1"},
{:swoosh, "~> 0.20"}, {:swoosh, "~> 0.20"},
{:gen_smtp, "~> 0.13"} {:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
] ]
end end

@ -32,7 +32,7 @@
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"}, "meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"},
"mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], [], "hexpm"}, "mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], [], "hexpm"},
"mock": {:hex, :mock, "0.3.1", "994f00150f79a0ea50dc9d86134cd9ebd0d177ad60bd04d1e46336cdfdb98ff9", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, "mock": {:hex, :mock, "0.3.1", "994f00150f79a0ea50dc9d86134cd9ebd0d177ad60bd04d1e46336cdfdb98ff9", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
@ -40,11 +40,13 @@
"nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"}, "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"}, "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"},
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.3.4", "aaa1b55e5523083a877bcbe9886d9ee180bf2c8754905323493c2ac325903dc5", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix": {:git, "https://github.com/phoenixframework/phoenix.git", "ea22dc50b574178a300ecd19253443960407df93", [branch: "v1.4"]},
"phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.11.2", "86ebd768258ba60a27f5578bec83095bdb93485d646fc4111db8844c316602d6", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_html": {:hex, :phoenix_html, "2.11.2", "86ebd768258ba60a27f5578bec83095bdb93485d646fc4111db8844c316602d6", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], [], "hexpm"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.6.2", "e06a7bd2bb6de5145da0dd950070110dce88045351224bd98e84edfdaaf5ffee", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, "plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "1.0.0", "2e2a7d3409746d335f451218b8bb0858301c3de6d668c3052716c909936eb57a", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"}, "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"}, "postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
@ -57,4 +59,5 @@
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
"unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"}, "unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"},
"web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
} }

@ -0,0 +1,100 @@
defmodule Pleroma.Integration.MastodonWebsocketTest do
use Pleroma.DataCase
import Pleroma.Factory
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth
alias Pleroma.Integration.WebsocketClient
alias Pleroma.Web.Streamer
@path Pleroma.Web.Endpoint.url()
|> URI.parse()
|> Map.put(:scheme, "ws")
|> Map.put(:path, "/api/v1/streaming")
|> URI.to_string()
setup do
GenServer.start(Streamer, %{}, name: Streamer)
on_exit(fn ->
if pid = Process.whereis(Streamer) do
Process.exit(pid, :kill)
end
end)
end
def start_socket(qs \\ nil, headers \\ []) do
path =
case qs do
nil -> @path
qs -> @path <> qs
end
WebsocketClient.start_link(self(), path, headers)
end
test "refuses invalid requests" do
assert {:error, {400, _}} = start_socket()
assert {:error, {404, _}} = start_socket("?stream=ncjdk")
end
test "requires authentication and a valid token for protected streams" do
assert {:error, {403, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa")
assert {:error, {403, _}} = start_socket("?stream=user")
end
test "allows public streams without authentication" do
assert {:ok, _} = start_socket("?stream=public")
assert {:ok, _} = start_socket("?stream=public:local")
assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
end
test "receives well formatted events" do
user = insert(:user)
{:ok, _} = start_socket("?stream=public")
{:ok, activity} = CommonAPI.post(user, %{"status" => "nice echo chamber"})
assert_receive {:text, raw_json}, 1_000
assert {:ok, json} = Jason.decode(raw_json)
assert "update" == json["event"]
assert json["payload"]
assert {:ok, json} = Jason.decode(json["payload"])
# Note: we remove the "statuses_count" from this result as it changes in the meantime
view_json =
Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: activity, for: nil)
|> Jason.encode!()
|> Jason.decode!()
|> put_in(["account", "statuses_count"], 0)
assert json == view_json
end
describe "with a valid user token" do
setup do
{:ok, app} =
Pleroma.Repo.insert(
OAuth.App.register_changeset(%OAuth.App{}, %{
client_name: "client",
scopes: "scope",
redirect_uris: "url"
})
)
user = insert(:user)
{:ok, auth} = OAuth.Authorization.create_authorization(app, user)
{:ok, token} = OAuth.Token.exchange_token(app, auth)
%{user: user, token: token}
end
test "accepts valid tokens", state do
assert {:ok, _} = start_socket("?stream=user&access_token=#{state.token.token}")
end
end
end

@ -0,0 +1,58 @@
defmodule Pleroma.Integration.WebsocketClient do
# https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs
@doc """
Starts the WebSocket server for given ws URL. Received Socket.Message's
are forwarded to the sender pid
"""
def start_link(sender, url, headers \\ []) do
:crypto.start()
:ssl.start()
:websocket_client.start_link(
String.to_charlist(url),
__MODULE__,
[sender],
extra_headers: headers
)
end
@doc """
Closes the socket
"""
def close(socket) do
send(socket, :close)
end
@doc """
Sends a low-level text message to the client.
"""
def send_text(server_pid, msg) do
send(server_pid, {:text, msg})
end
@doc false
def init([sender], _conn_state) do
{:ok, %{sender: sender}}
end
@doc false
def websocket_handle(frame, _conn_state, state) do
send(state.sender, frame)
{:ok, state}
end
@doc false
def websocket_info({:text, msg}, _conn_state, state) do
{:reply, {:text, msg}, state}
end
def websocket_info(:close, _conn_state, _state) do
{:close, <<>>, "done"}
end
@doc false
def websocket_terminate(_reason, _conn_state, _state) do
:ok
end
end

@ -1,31 +0,0 @@
defmodule Pleroma.Web.MastodonApi.MastodonSocketTest do
use Pleroma.DataCase
alias Pleroma.Web.{Streamer, CommonAPI}
import Pleroma.Factory
test "public is working when non-authenticated" do
user = insert(:user)
task =
Task.async(fn ->
assert_receive {:text, _}, 4_000
end)
fake_socket = %{
transport_pid: task.pid,
assigns: %{}
}
topics = %{
"public" => [fake_socket]
}
{:ok, activity} = CommonAPI.post(user, %{"status" => "Test"})
Streamer.push_to_socket(topics, "public", activity)
Task.await(task)
end
end

@ -1,11 +1,7 @@
defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Pleroma.User
alias Pleroma.Web.WebFinger.WebFingerController
import Pleroma.Factory import Pleroma.Factory
import ExUnit.CaptureLog
import Tesla.Mock import Tesla.Mock
setup do setup do

Loading…
Cancel
Save