aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/web
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/web')
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex32
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex37
-rw-r--r--lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex48
-rw-r--r--lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex17
-rw-r--r--lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex56
-rw-r--r--lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex12
-rw-r--r--lib/pleroma/web/activity_pub/mrf/normalize_markup.ex10
-rw-r--r--lib/pleroma/web/activity_pub/mrf/reject_non_public.ex38
-rw-r--r--lib/pleroma/web/activity_pub/mrf/tag_policy.ex53
-rw-r--r--lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex (renamed from lib/pleroma/web/activity_pub/mrf/user_allowlist.ex)7
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex2
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex187
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex39
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex4
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex65
-rw-r--r--lib/pleroma/web/admin_api/config.ex165
-rw-r--r--lib/pleroma/web/admin_api/views/account_view.ex8
-rw-r--r--lib/pleroma/web/admin_api/views/config_view.ex21
-rw-r--r--lib/pleroma/web/admin_api/views/report_view.ex22
-rw-r--r--lib/pleroma/web/auth/pleroma_authenticator.ex11
-rw-r--r--lib/pleroma/web/common_api/common_api.ex68
-rw-r--r--lib/pleroma/web/common_api/utils.ex23
-rw-r--r--lib/pleroma/web/controller_helper.ex18
-rw-r--r--lib/pleroma/web/endpoint.ex12
-rw-r--r--lib/pleroma/web/federator/federator.ex12
-rw-r--r--lib/pleroma/web/federator/retry_queue.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api.ex4
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex418
-rw-r--r--lib/pleroma/web/mastodon_api/search_controller.ex90
-rw-r--r--lib/pleroma/web/mastodon_api/subscription_controller.ex8
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex20
-rw-r--r--lib/pleroma/web/mastodon_api/views/conversation_view.ex4
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex14
-rw-r--r--lib/pleroma/web/mastodon_api/websocket_handler.ex18
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy.ex23
-rw-r--r--lib/pleroma/web/metadata/opengraph.ex2
-rw-r--r--lib/pleroma/web/metadata/player_view.ex4
-rw-r--r--lib/pleroma/web/metadata/rel_me.ex4
-rw-r--r--lib/pleroma/web/metadata/twitter_card.ex2
-rw-r--r--lib/pleroma/web/mongooseim/mongoose_im_controller.ex2
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex7
-rw-r--r--lib/pleroma/web/oauth/authorization.ex12
-rw-r--r--lib/pleroma/web/oauth/fallback_controller.ex9
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex212
-rw-r--r--lib/pleroma/web/oauth/token.ex5
-rw-r--r--lib/pleroma/web/oauth/token/response.ex12
-rw-r--r--lib/pleroma/web/oauth/token/strategy/refresh_token.ex4
-rw-r--r--lib/pleroma/web/oauth/token/strategy/revoke.ex4
-rw-r--r--lib/pleroma/web/oauth/token/utils.ex4
-rw-r--r--lib/pleroma/web/ostatus/handlers/note_handler.ex13
-rw-r--r--lib/pleroma/web/ostatus/ostatus.ex22
-rw-r--r--lib/pleroma/web/ostatus/ostatus_controller.ex8
-rw-r--r--lib/pleroma/web/rel_me.ex2
-rw-r--r--lib/pleroma/web/rich_media/helpers.ex44
-rw-r--r--lib/pleroma/web/rich_media/parser.ex2
-rw-r--r--lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex39
-rw-r--r--lib/pleroma/web/rich_media/parsers/oembed_parser.ex4
-rw-r--r--lib/pleroma/web/rich_media/parsers/ogp.ex4
-rw-r--r--lib/pleroma/web/rich_media/parsers/twitter_card.ex4
-rw-r--r--lib/pleroma/web/router.ex44
-rw-r--r--lib/pleroma/web/salmon/salmon.ex2
-rw-r--r--lib/pleroma/web/streamer.ex45
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex (renamed from lib/pleroma/web/templates/o_auth/o_auth/results.html.eex)0
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex2
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex (renamed from lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex)0
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/reset.html.eex (renamed from lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex)2
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex (renamed from lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex)0
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex (renamed from lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex)0
-rw-r--r--lib/pleroma/web/translation_helpers.ex17
-rw-r--r--lib/pleroma/web/twitter_api/controllers/password_controller.ex37
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex22
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex35
-rw-r--r--lib/pleroma/web/twitter_api/views/password_view.ex8
-rw-r--r--lib/pleroma/web/uploader_controller.ex8
-rw-r--r--lib/pleroma/web/views/error_view.ex2
-rw-r--r--lib/pleroma/web/web.ex2
76 files changed, 1493 insertions, 730 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 73c6e4cbf..20f72e676 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -188,6 +188,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end)
end
+ def stream_out_participations(%Object{data: %{"context" => context}}, user) do
+ with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
+ conversation = Repo.preload(conversation, :participations),
+ last_activity_id =
+ fetch_latest_activity_id_for_context(conversation.ap_id, %{
+ "user" => user,
+ "blocking_user" => user
+ }) do
+ if last_activity_id do
+ stream_out_participations(conversation.participations)
+ end
+ end
+ end
+
+ def stream_out_participations(_, _), do: :noop
+
def stream_out(activity) do
public = "https://www.w3.org/ns/activitystreams#Public"
@@ -388,6 +404,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
+ with data <- %{
+ "to" => [follower_address],
+ "type" => "Delete",
+ "actor" => ap_id,
+ "object" => %{"type" => "Person", "id" => ap_id}
+ },
+ {:ok, activity} <- insert(data, true, true),
+ :ok <- maybe_federate(activity) do
+ {:ok, user}
+ end
+ end
+
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
user = User.get_cached_by_ap_id(actor)
to = (object.data["to"] || []) ++ (object.data["cc"] || [])
@@ -400,7 +429,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"to" => to,
"deleted_activity_id" => activity && activity.id
},
- {:ok, activity} <- insert(data, local),
+ {:ok, activity} <- insert(data, local, false),
+ stream_out_participations(object, user),
_ <- decrease_replies_count_if_reply(object),
# Changing note count prior to enqueuing federation task in order to avoid
# race conditions on updating user.info
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 0182bda46..cf5176201 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -31,9 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
conn
else
conn
- |> put_status(404)
- |> json(%{error: "not found"})
- |> halt
+ |> render_error(:not_found, "not found")
+ |> halt()
end
end
@@ -190,7 +189,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
Logger.info(inspect(conn.req_headers))
end
- json(conn, "error")
+ json(conn, dgettext("errors", "error"))
end
def relay(conn, _params) do
@@ -218,9 +217,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
else
+ err =
+ dgettext("errors", "can't read inbox of %{nickname} as %{as_nickname}",
+ nickname: nickname,
+ as_nickname: user.nickname
+ )
+
conn
|> put_status(:forbidden)
- |> json("can't read inbox of #{nickname} as #{user.nickname}")
+ |> json(err)
end
end
@@ -246,7 +251,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
else
- _ -> {:error, "Can't delete object"}
+ _ -> {:error, dgettext("errors", "Can't delete object")}
end
end
@@ -255,12 +260,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
{:ok, activity, _object} <- ActivityPub.like(user, object) do
{:ok, activity}
else
- _ -> {:error, "Can't like object"}
+ _ -> {:error, dgettext("errors", "Can't like object")}
end
end
def handle_user_activity(_, _) do
- {:error, "Unhandled activity type"}
+ {:error, dgettext("errors", "Unhandled activity type")}
end
def update_outbox(
@@ -288,22 +293,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> json(message)
end
else
+ err =
+ dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
+ nickname: nickname,
+ as_nickname: user.nickname
+ )
+
conn
|> put_status(:forbidden)
- |> json("can't update outbox of #{nickname} as #{user.nickname}")
+ |> json(err)
end
end
def errors(conn, {:error, :not_found}) do
conn
- |> put_status(404)
- |> json("Not found")
+ |> put_status(:not_found)
+ |> json(dgettext("errors", "Not found"))
end
def errors(conn, _e) do
conn
- |> put_status(500)
- |> json("error")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "error"))
end
defp set_requester_reachable(%Plug.Conn{} = conn, _) do
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
new file mode 100644
index 000000000..2da3eac2f
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
@@ -0,0 +1,48 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
+ alias Pleroma.User
+
+ require Logger
+
+ # has the user successfully posted before?
+ defp old_user?(%User{} = u) do
+ u.info.note_count > 0 || u.info.follower_count > 0
+ end
+
+ # does the post contain links?
+ defp contains_links?(%{"content" => content} = _object) do
+ content
+ |> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"],a.zrl")
+ |> Floki.attribute("a", "href")
+ |> length() > 0
+ end
+
+ defp contains_links?(_), do: false
+
+ def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do
+ with {:ok, %User{} = u} <- User.get_or_fetch_by_ap_id(actor),
+ {:contains_links, true} <- {:contains_links, contains_links?(object)},
+ {:old_user, true} <- {:old_user, old_user?(u)} do
+ {:ok, message}
+ else
+ {:contains_links, false} ->
+ {:ok, message}
+
+ {:old_user, false} ->
+ {:reject, nil}
+
+ {:error, _} ->
+ {:reject, nil}
+
+ e ->
+ Logger.warn("[MRF anti-link-spam] WTF: unhandled error #{inspect(e)}")
+ {:reject, nil}
+ end
+ end
+
+ # in all other cases, pass through
+ def filter(message), do: {:ok, message}
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
index 15d8514be..2d03df68a 100644
--- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
+++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
@@ -9,8 +9,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
@behaviour Pleroma.Web.ActivityPub.MRF
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
+
def filter_by_summary(
- %{"summary" => parent_summary} = _parent,
+ %{data: %{"summary" => parent_summary}} = _in_reply_to,
%{"summary" => child_summary} = child
)
when not is_nil(child_summary) and byte_size(child_summary) > 0 and
@@ -24,17 +25,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
end
end
- def filter_by_summary(_parent, child), do: child
-
- def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
- child = object["object"]
- in_reply_to = Object.normalize(child["inReplyTo"])
+ def filter_by_summary(_in_reply_to, child), do: child
+ def filter(%{"type" => "Create", "object" => child_object} = object) do
child =
- if(in_reply_to,
- do: filter_by_summary(in_reply_to.data, child),
- else: child
- )
+ child_object["inReplyTo"]
+ |> Object.normalize(child_object["inReplyTo"])
+ |> filter_by_summary(child_object)
object = Map.put(object, "object", child)
diff --git a/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex
new file mode 100644
index 000000000..01d21a299
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex
@@ -0,0 +1,56 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
+ @moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
+ @behaviour Pleroma.Web.ActivityPub.MRF
+
+ alias Pleroma.HTTP
+ alias Pleroma.Web.MediaProxy
+
+ require Logger
+
+ @hackney_options [
+ pool: :media,
+ recv_timeout: 10_000
+ ]
+
+ def perform(:prefetch, url) do
+ Logger.info("Prefetching #{inspect(url)}")
+
+ url
+ |> MediaProxy.url()
+ |> HTTP.get([], adapter: @hackney_options)
+ end
+
+ def perform(:preload, %{"object" => %{"attachment" => attachments}} = _message) do
+ Enum.each(attachments, fn
+ %{"url" => url} when is_list(url) ->
+ url
+ |> Enum.each(fn
+ %{"href" => href} ->
+ PleromaJobQueue.enqueue(:background, __MODULE__, [:prefetch, href])
+
+ x ->
+ Logger.debug("Unhandled attachment URL object #{inspect(x)}")
+ end)
+
+ x ->
+ Logger.debug("Unhandled attachment #{inspect(x)}")
+ end)
+ end
+
+ @impl true
+ def filter(
+ %{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message
+ )
+ when is_list(attachments) and length(attachments) > 0 do
+ PleromaJobQueue.enqueue(:background, __MODULE__, [:preload, message])
+
+ {:ok, message}
+ end
+
+ @impl true
+ def filter(message), do: {:ok, message}
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
index f30fee0d5..86a48bda5 100644
--- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
@@ -10,19 +10,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
def filter(
%{
"type" => "Create",
- "object" => %{"content" => content, "attachment" => _attachment} = child_object
+ "object" => %{"content" => content, "attachment" => _} = _child_object
} = object
)
when content in [".", "<p>.</p>"] do
- child_object =
- child_object
- |> Map.put("content", "")
-
- object =
- object
- |> Map.put("object", child_object)
-
- {:ok, object}
+ {:ok, put_in(object, ["object", "content"], "")}
end
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
index 9c87c6963..c269d0f89 100644
--- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
+++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
@@ -8,18 +8,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@behaviour Pleroma.Web.ActivityPub.MRF
- def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
+ def filter(%{"type" => "Create", "object" => child_object} = object) do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
- child = object["object"]
-
content =
- child["content"]
+ child_object["content"]
|> HTML.filter_tags(scrub_policy)
- child = Map.put(child, "content", content)
-
- object = Map.put(object, "object", child)
+ object = put_in(object, ["object", "content"], content)
{:ok, object}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index ea3df1b4d..da13fd7c7 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -3,46 +3,42 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
- alias Pleroma.User
@moduledoc "Rejects non-public (followers-only, direct) activities"
+
+ alias Pleroma.Config
+ alias Pleroma.User
+
@behaviour Pleroma.Web.ActivityPub.MRF
+ @public "https://www.w3.org/ns/activitystreams#Public"
+
@impl true
def filter(%{"type" => "Create"} = object) do
user = User.get_cached_by_ap_id(object["actor"])
- public = "https://www.w3.org/ns/activitystreams#Public"
# Determine visibility
visibility =
cond do
- public in object["to"] -> "public"
- public in object["cc"] -> "unlisted"
+ @public in object["to"] -> "public"
+ @public in object["cc"] -> "unlisted"
user.follower_address in object["to"] -> "followers"
true -> "direct"
end
- policy = Pleroma.Config.get(:mrf_rejectnonpublic)
+ policy = Config.get(:mrf_rejectnonpublic)
+
+ cond do
+ visibility in ["public", "unlisted"] ->
+ {:ok, object}
- case visibility do
- "public" ->
+ visibility == "followers" and Keyword.get(policy, :allow_followersonly) ->
{:ok, object}
- "unlisted" ->
+ visibility == "direct" and Keyword.get(policy, :allow_direct) ->
{:ok, object}
- "followers" ->
- with true <- Keyword.get(policy, :allow_followersonly) do
- {:ok, object}
- else
- _e -> {:reject, nil}
- end
-
- "direct" ->
- with true <- Keyword.get(policy, :allow_direct) do
- {:ok, object}
- else
- _e -> {:reject, nil}
- end
+ true ->
+ {:reject, nil}
end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index 6683b8d8e..b42c4ed76 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -19,12 +19,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
- `mrf_tag:disable-any-subscription`: Reject any follow requests
"""
+ @public "https://www.w3.org/ns/activitystreams#Public"
+
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
defp get_tags(_), do: []
defp process_tag(
"mrf_tag:media-force-nsfw",
- %{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message
+ %{
+ "type" => "Create",
+ "object" => %{"attachment" => child_attachment} = object
+ } = message
)
when length(child_attachment) > 0 do
tags = (object["tag"] || []) ++ ["nsfw"]
@@ -41,7 +46,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"mrf_tag:media-strip",
- %{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message
+ %{
+ "type" => "Create",
+ "object" => %{"attachment" => child_attachment} = object
+ } = message
)
when length(child_attachment) > 0 do
object = Map.delete(object, "attachment")
@@ -52,19 +60,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"mrf_tag:force-unlisted",
- %{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
+ %{
+ "type" => "Create",
+ "to" => to,
+ "cc" => cc,
+ "actor" => actor,
+ "object" => object
+ } = message
) do
user = User.get_cached_by_ap_id(actor)
- if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") do
- to =
- List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
-
- cc =
- List.delete(cc, user.follower_address) ++ ["https://www.w3.org/ns/activitystreams#Public"]
+ if Enum.member?(to, @public) do
+ to = List.delete(to, @public) ++ [user.follower_address]
+ cc = List.delete(cc, user.follower_address) ++ [@public]
object =
- message["object"]
+ object
|> Map.put("to", to)
|> Map.put("cc", cc)
@@ -82,19 +93,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"mrf_tag:sandbox",
- %{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
+ %{
+ "type" => "Create",
+ "to" => to,
+ "cc" => cc,
+ "actor" => actor,
+ "object" => object
+ } = message
) do
user = User.get_cached_by_ap_id(actor)
- if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") or
- Enum.member?(cc, "https://www.w3.org/ns/activitystreams#Public") do
- to =
- List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
-
- cc = List.delete(cc, "https://www.w3.org/ns/activitystreams#Public")
+ if Enum.member?(to, @public) or Enum.member?(cc, @public) do
+ to = List.delete(to, @public) ++ [user.follower_address]
+ cc = List.delete(cc, @public)
object =
- message["object"]
+ object
|> Map.put("to", to)
|> Map.put("cc", cc)
@@ -123,7 +137,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
end
end
- defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}), do: {:reject, nil}
+ defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}),
+ do: {:reject, nil}
defp process_tag(_, message), do: {:ok, message}
diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
index 47663414a..e35d2c422 100644
--- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
+++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
@@ -21,7 +21,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
@impl true
def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(actor)
- allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
+
+ allow_list =
+ Config.get(
+ [:mrf_user_allowlist, String.to_atom(actor_info.host)],
+ []
+ )
filter_by_list(object, allow_list)
end
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index f376e5618..b7dc90caa 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -88,7 +88,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
true
else
inbox_info = URI.parse(inbox)
- !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
+ !Enum.member?(Config.get([:instance, :quarantined_instances], []), inbox_info.host)
end
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index d22d24479..ad741122f 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.Federator
import Ecto.Query
@@ -22,20 +23,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
@doc """
Modifies an incoming AP object (mastodon format) to our internal format.
"""
- def fix_object(object) do
+ def fix_object(object, options \\ []) do
object
|> fix_actor
|> fix_url
|> fix_attachments
|> fix_context
- |> fix_in_reply_to
+ |> fix_in_reply_to(options)
|> fix_emoji
|> fix_tag
|> fix_content_map
|> fix_likes
|> fix_addressing
|> fix_summary
- |> fix_type
+ |> fix_type(options)
end
def fix_summary(%{"summary" => nil} = object) do
@@ -164,7 +165,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
object
end
- def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object)
+ def fix_in_reply_to(object, options \\ [])
+
+ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
when not is_nil(in_reply_to) do
in_reply_to_id =
cond do
@@ -182,28 +185,34 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
""
end
- case get_obj_helper(in_reply_to_id) do
- {:ok, replied_object} ->
- with %Activity{} = _activity <-
- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
- object
- |> Map.put("inReplyTo", replied_object.data["id"])
- |> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
- |> Map.put("conversation", replied_object.data["context"] || object["conversation"])
- |> Map.put("context", replied_object.data["context"] || object["conversation"])
- else
- e ->
- Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}")
+ object = Map.put(object, "inReplyToAtomUri", in_reply_to_id)
+
+ if Federator.allowed_incoming_reply_depth?(options[:depth]) do
+ case get_obj_helper(in_reply_to_id, options) do
+ {:ok, replied_object} ->
+ with %Activity{} = _activity <-
+ Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
object
- end
+ |> Map.put("inReplyTo", replied_object.data["id"])
+ |> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
+ |> Map.put("conversation", replied_object.data["context"] || object["conversation"])
+ |> Map.put("context", replied_object.data["context"] || object["conversation"])
+ else
+ e ->
+ Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}")
+ object
+ end
- e ->
- Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}")
- object
+ e ->
+ Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}")
+ object
+ end
+ else
+ object
end
end
- def fix_in_reply_to(object), do: object
+ def fix_in_reply_to(object, _options), do: object
def fix_context(object) do
context = object["context"] || object["conversation"] || Utils.generate_context_id()
@@ -336,17 +345,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_content_map(object), do: object
- def fix_type(%{"inReplyTo" => reply_id} = object) when is_binary(reply_id) do
- reply = Object.normalize(reply_id)
+ def fix_type(object, options \\ [])
- if reply.data["type"] == "Question" and object["name"] do
+ def fix_type(%{"inReplyTo" => reply_id} = object, options) when is_binary(reply_id) do
+ reply =
+ if Federator.allowed_incoming_reply_depth?(options[:depth]) do
+ Object.normalize(reply_id, true)
+ end
+
+ if reply && (reply.data["type"] == "Question" and object["name"]) do
Map.put(object, "type", "Answer")
else
object
end
end
- def fix_type(object), do: object
+ def fix_type(object, _), do: object
defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do
with true <- id =~ "follows",
@@ -374,9 +388,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
+ def handle_incoming(data, options \\ [])
+
# Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them
# with nil ID.
- def handle_incoming(%{"type" => "Flag", "object" => objects, "actor" => actor} = data) do
+ def handle_incoming(%{"type" => "Flag", "object" => objects, "actor" => actor} = data, _options) do
with context <- data["context"] || Utils.generate_context_id(),
content <- data["content"] || "",
%User{} = actor <- User.get_cached_by_ap_id(actor),
@@ -409,15 +425,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
# disallow objects with bogus IDs
- def handle_incoming(%{"id" => nil}), do: :error
- def handle_incoming(%{"id" => ""}), do: :error
+ def handle_incoming(%{"id" => nil}, _options), do: :error
+ def handle_incoming(%{"id" => ""}, _options), do: :error
# length of https:// = 8, should validate better, but good enough for now.
- def handle_incoming(%{"id" => id}) when not (is_binary(id) and length(id) > 8), do: :error
+ def handle_incoming(%{"id" => id}, _options) when not (is_binary(id) and length(id) > 8),
+ do: :error
# TODO: validate those with a Ecto scheme
# - tags
# - emoji
- def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data)
+ def handle_incoming(
+ %{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
+ options
+ )
when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do
actor = Containment.get_actor(data)
@@ -427,7 +447,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
- object = fix_object(data["object"])
+ options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
+ object = fix_object(data["object"], options)
params = %{
to: data["to"],
@@ -452,16 +473,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
+ %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data,
+ _options
) do
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
{:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
- {:user_blocked, false} <-
+ {_, false} <-
{:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
- {:user_locked, false} <- {:user_locked, User.locked?(followed)},
- {:follow, {:ok, follower}} <- {:follow, User.follow(follower, followed)} do
+ {_, false} <- {:user_locked, User.locked?(followed)},
+ {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
+ {_, {:ok, _}} <-
+ {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")} do
ActivityPub.accept(%{
to: [follower.ap_id],
actor: followed,
@@ -470,7 +494,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
})
else
{:user_blocked, true} ->
- {:ok, _} = Utils.update_follow_state(activity, "reject")
+ {:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
ActivityPub.reject(%{
to: [follower.ap_id],
@@ -480,7 +504,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
})
{:follow, {:error, _}} ->
- {:ok, _} = Utils.update_follow_state(activity, "reject")
+ {:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
ActivityPub.reject(%{
to: [follower.ap_id],
@@ -501,38 +525,35 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data
+ %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data,
+ _options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor),
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
- {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
+ {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
- {:ok, activity} <-
- ActivityPub.accept(%{
- to: follow_activity.data["to"],
- type: "Accept",
- actor: followed,
- object: follow_activity.data["id"],
- local: false
- }) do
- if not User.following?(follower, followed) do
- {:ok, _follower} = User.follow(follower, followed)
- end
-
- {:ok, activity}
+ {:ok, _follower} = User.follow(follower, followed) do
+ ActivityPub.accept(%{
+ to: follow_activity.data["to"],
+ type: "Accept",
+ actor: followed,
+ object: follow_activity.data["id"],
+ local: false
+ })
else
_e -> :error
end
end
def handle_incoming(
- %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data
+ %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data,
+ _options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor),
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
- {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
+ {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
{:ok, activity} <-
ActivityPub.reject(%{
@@ -551,7 +572,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data
+ %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data,
+ _options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
@@ -564,7 +586,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data
+ %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
+ _options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
@@ -579,7 +602,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
- data
+ data,
+ _options
)
when object_type in ["Person", "Application", "Service", "Organization"] do
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
@@ -617,7 +641,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# an error or a tombstone. This would allow us to verify that a deletion actually took
# place.
def handle_incoming(
- %{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data
+ %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
+ _options
) do
object_id = Utils.get_ap_id(object_id)
@@ -628,7 +653,30 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity}
else
- _e -> :error
+ nil ->
+ case User.get_cached_by_ap_id(object_id) do
+ %User{ap_id: ^actor} = user ->
+ {:ok, followers} = User.get_followers(user)
+
+ Enum.each(followers, fn follower ->
+ User.unfollow(follower, user)
+ end)
+
+ {:ok, friends} = User.get_friends(user)
+
+ Enum.each(friends, fn followed ->
+ User.unfollow(user, followed)
+ end)
+
+ User.invalidate_cache(user)
+ Repo.delete(user)
+
+ nil ->
+ :error
+ end
+
+ _e ->
+ :error
end
end
@@ -638,7 +686,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"object" => %{"type" => "Announce", "object" => object_id},
"actor" => _actor,
"id" => id
- } = data
+ } = data,
+ _options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
@@ -656,7 +705,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"object" => %{"type" => "Follow", "object" => followed},
"actor" => follower,
"id" => id
- } = _data
+ } = _data,
+ _options
) do
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
{:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
@@ -674,7 +724,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"object" => %{"type" => "Block", "object" => blocked},
"actor" => blocker,
"id" => id
- } = _data
+ } = _data,
+ _options
) do
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
@@ -688,7 +739,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data
+ %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data,
+ _options
) do
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
@@ -708,7 +760,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"object" => %{"type" => "Like", "object" => object_id},
"actor" => _actor,
"id" => id
- } = data
+ } = data,
+ _options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
@@ -720,10 +773,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
- def handle_incoming(_), do: :error
+ def handle_incoming(_, _), do: :error
- def get_obj_helper(id) do
- if object = Object.normalize(id), do: {:ok, object}, else: nil
+ def get_obj_helper(id, options \\ []) do
+ if object = Object.normalize(id, true, options), do: {:ok, object}, else: nil
end
def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index faae7e747..4288ea4c8 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -151,16 +151,18 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def create_context(context) do
context = context || generate_id("contexts")
- changeset = Object.context_mapping(context)
- case Repo.insert(changeset) do
- {:ok, object} ->
- object
+ # Ecto has problems accessing the constraint inside the jsonb,
+ # so we explicitly check for the existed object before insert
+ object = Object.get_cached_by_ap_id(context)
- # This should be solved by an upsert, but it seems ecto
- # has problems accessing the constraint inside the jsonb.
- {:error, _} ->
- Object.get_cached_by_ap_id(context)
+ with true <- is_nil(object),
+ changeset <- Object.context_mapping(context),
+ {:ok, inserted_object} <- Repo.insert(changeset) do
+ inserted_object
+ else
+ _ ->
+ object
end
end
@@ -168,14 +170,17 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Enqueues an activity for federation if it's local
"""
def maybe_federate(%Activity{local: true} = activity) do
- priority =
- case activity.data["type"] do
- "Delete" -> 10
- "Create" -> 1
- _ -> 5
- end
+ if Pleroma.Config.get!([:instance, :federating]) do
+ priority =
+ case activity.data["type"] do
+ "Delete" -> 10
+ "Create" -> 1
+ _ -> 5
+ end
+
+ Pleroma.Web.Federator.publish(activity, priority)
+ end
- Pleroma.Web.Federator.publish(activity, priority)
:ok
end
@@ -376,8 +381,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
@doc """
Updates a follow activity's state (for locked accounts).
"""
- def update_follow_state(
- %Activity{data: %{"actor" => actor, "object" => object, "state" => "pending"}} = activity,
+ def update_follow_state_for_all(
+ %Activity{data: %{"actor" => actor, "object" => object}} = activity,
state
) do
try do
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 8965e3253..9908a2e75 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Activity
alias Pleroma.Object
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index de2a13c01..8b3c3c91f 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -10,6 +10,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.AdminAPI.AccountView
+ alias Pleroma.Web.AdminAPI.Config
+ alias Pleroma.Web.AdminAPI.ConfigView
alias Pleroma.Web.AdminAPI.ReportView
alias Pleroma.Web.AdminAPI.Search
alias Pleroma.Web.CommonAPI
@@ -72,7 +74,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def user_show(conn, %{"nickname" => nickname}) do
- with %User{} = user <- User.get_cached_by_nickname(nickname) do
+ with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
conn
|> json(AccountView.render("show.json", %{user: user}))
else
@@ -158,9 +160,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def right_add(conn, _) do
- conn
- |> put_status(404)
- |> json(%{error: "No such permission_group"})
+ render_error(conn, :not_found, "No such permission_group")
end
def right_get(conn, %{"nickname" => nickname}) do
@@ -182,9 +182,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
)
when permission_group in ["moderator", "admin"] do
if admin_nickname == nickname do
- conn
- |> put_status(403)
- |> json(%{error: "You can't revoke your own admin status."})
+ render_error(conn, :forbidden, "You can't revoke your own admin status.")
else
user = User.get_cached_by_nickname(nickname)
@@ -205,9 +203,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def right_delete(conn, _) do
- conn
- |> put_status(404)
- |> json(%{error: "No such permission_group"})
+ render_error(conn, :not_found, "No such permission_group")
end
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
@@ -362,28 +358,63 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
end
+ def config_show(conn, _params) do
+ configs = Pleroma.Repo.all(Config)
+
+ conn
+ |> put_view(ConfigView)
+ |> render("index.json", %{configs: configs})
+ end
+
+ def config_update(conn, %{"configs" => configs}) do
+ updated =
+ if Pleroma.Config.get([:instance, :dynamic_configuration]) do
+ updated =
+ Enum.map(configs, fn
+ %{"group" => group, "key" => key, "value" => value} ->
+ {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
+ config
+
+ %{"group" => group, "key" => key, "delete" => "true"} ->
+ {:ok, _} = Config.delete(%{group: group, key: key})
+ nil
+ end)
+ |> Enum.reject(&is_nil(&1))
+
+ Pleroma.Config.TransferTask.load_and_update_env()
+ Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
+ updated
+ else
+ []
+ end
+
+ conn
+ |> put_view(ConfigView)
+ |> render("index.json", %{configs: updated})
+ end
+
def errors(conn, {:error, :not_found}) do
conn
- |> put_status(404)
- |> json("Not found")
+ |> put_status(:not_found)
+ |> json(dgettext("errors", "Not found"))
end
def errors(conn, {:error, reason}) do
conn
- |> put_status(400)
+ |> put_status(:bad_request)
|> json(reason)
end
def errors(conn, {:param_cast, _}) do
conn
- |> put_status(400)
- |> json("Invalid parameters")
+ |> put_status(:bad_request)
+ |> json(dgettext("errors", "Invalid parameters"))
end
def errors(conn, _) do
conn
- |> put_status(500)
- |> json("Something went wrong")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "Something went wrong"))
end
defp page_params(params) do
diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex
new file mode 100644
index 000000000..24674abc5
--- /dev/null
+++ b/lib/pleroma/web/admin_api/config.ex
@@ -0,0 +1,165 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.Config do
+ use Ecto.Schema
+ import Ecto.Changeset
+ import Pleroma.Web.Gettext
+ alias __MODULE__
+ alias Pleroma.Repo
+
+ @type t :: %__MODULE__{}
+
+ schema "config" do
+ field(:key, :string)
+ field(:group, :string)
+ field(:value, :binary)
+
+ timestamps()
+ end
+
+ @spec get_by_params(map()) :: Config.t() | nil
+ def get_by_params(params), do: Repo.get_by(Config, params)
+
+ @spec changeset(Config.t(), map()) :: Changeset.t()
+ def changeset(config, params \\ %{}) do
+ config
+ |> cast(params, [:key, :group, :value])
+ |> validate_required([:key, :group, :value])
+ |> unique_constraint(:key, name: :config_group_key_index)
+ end
+
+ @spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
+ def create(params) do
+ %Config{}
+ |> changeset(Map.put(params, :value, transform(params[:value])))
+ |> Repo.insert()
+ end
+
+ @spec update(Config.t(), map()) :: {:ok, Config} | {:error, Changeset.t()}
+ def update(%Config{} = config, %{value: value}) do
+ config
+ |> change(value: transform(value))
+ |> Repo.update()
+ end
+
+ @spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
+ def update_or_create(params) do
+ with %Config{} = config <- Config.get_by_params(Map.take(params, [:group, :key])) do
+ Config.update(config, params)
+ else
+ nil -> Config.create(params)
+ end
+ end
+
+ @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
+ def delete(params) do
+ with %Config{} = config <- Config.get_by_params(params) do
+ Repo.delete(config)
+ else
+ nil ->
+ err =
+ dgettext("errors", "Config with params %{params} not found", params: inspect(params))
+
+ {:error, err}
+ end
+ end
+
+ @spec from_binary(binary()) :: term()
+ def from_binary(value), do: :erlang.binary_to_term(value)
+
+ @spec from_binary_to_map(binary()) :: any()
+ def from_binary_to_map(binary) do
+ from_binary(binary)
+ |> do_convert()
+ end
+
+ defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1,
+ do: %{k => do_convert(v)}
+
+ defp do_convert(values) when is_list(values), do: for(val <- values, do: do_convert(val))
+
+ defp do_convert({k, v} = value) when is_tuple(value),
+ do: %{k => do_convert(v)}
+
+ defp do_convert(value) when is_tuple(value), do: %{"tuple" => do_convert(Tuple.to_list(value))}
+
+ defp do_convert(value) when is_binary(value) or is_map(value) or is_number(value), do: value
+
+ defp do_convert(value) when is_atom(value) do
+ string = to_string(value)
+
+ if String.starts_with?(string, "Elixir."),
+ do: String.trim_leading(string, "Elixir."),
+ else: value
+ end
+
+ @spec transform(any()) :: binary()
+ def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity))
+
+ def transform(entity) when is_map(entity) do
+ tuples =
+ for {k, v} <- entity,
+ into: [],
+ do: {if(is_atom(k), do: k, else: String.to_atom(k)), do_transform(v)}
+
+ Enum.reject(tuples, fn {_k, v} -> is_nil(v) end)
+ |> Enum.sort()
+ |> :erlang.term_to_binary()
+ end
+
+ def transform(entity) when is_list(entity) do
+ list = Enum.map(entity, &do_transform(&1))
+ :erlang.term_to_binary(list)
+ end
+
+ def transform(entity), do: :erlang.term_to_binary(entity)
+
+ defp do_transform(%Regex{} = value) when is_map(value), do: value
+
+ defp do_transform(%{"tuple" => [k, values] = entity}) when length(entity) == 2 do
+ {do_transform(k), do_transform(values)}
+ end
+
+ defp do_transform(%{"tuple" => values}) do
+ Enum.reduce(values, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
+ end
+
+ defp do_transform(value) when is_map(value) do
+ values = for {key, val} <- value, into: [], do: {String.to_atom(key), do_transform(val)}
+
+ Enum.sort(values)
+ end
+
+ defp do_transform(value) when is_list(value) do
+ Enum.map(value, &do_transform(&1))
+ end
+
+ defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity)
+
+ defp do_transform(value) when is_binary(value) do
+ String.trim(value)
+ |> do_transform_string()
+ end
+
+ defp do_transform(value), do: value
+
+ defp do_transform_string(value) when byte_size(value) == 0, do: nil
+
+ defp do_transform_string(value) do
+ cond do
+ String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix") ->
+ String.to_existing_atom("Elixir." <> value)
+
+ String.starts_with?(value, ":") ->
+ String.replace(value, ":", "") |> String.to_existing_atom()
+
+ String.starts_with?(value, "i:") ->
+ String.replace(value, "i:", "") |> String.to_integer()
+
+ true ->
+ value
+ end
+ end
+end
diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex
index 28bb667d8..7e1b9c431 100644
--- a/lib/pleroma/web/admin_api/views/account_view.ex
+++ b/lib/pleroma/web/admin_api/views/account_view.ex
@@ -5,8 +5,11 @@
defmodule Pleroma.Web.AdminAPI.AccountView do
use Pleroma.Web, :view
+ alias Pleroma.HTML
+ alias Pleroma.User
alias Pleroma.User.Info
alias Pleroma.Web.AdminAPI.AccountView
+ alias Pleroma.Web.MediaProxy
def render("index.json", %{users: users, count: count, page_size: page_size}) do
%{
@@ -17,9 +20,14 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
end
def render("show.json", %{user: user}) do
+ avatar = User.avatar_url(user) |> MediaProxy.url()
+ display_name = HTML.strip_tags(user.name || user.nickname)
+
%{
"id" => user.id,
+ "avatar" => avatar,
"nickname" => user.nickname,
+ "display_name" => display_name,
"deactivated" => user.info.deactivated,
"local" => user.local,
"roles" => Info.roles(user.info),
diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex
new file mode 100644
index 000000000..a31f1041f
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/config_view.ex
@@ -0,0 +1,21 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.ConfigView do
+ use Pleroma.Web, :view
+
+ def render("index.json", %{configs: configs}) do
+ %{
+ configs: render_many(configs, __MODULE__, "show.json", as: :config)
+ }
+ end
+
+ def render("show.json", %{config: config}) do
+ %{
+ key: config.key,
+ group: config.group,
+ value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value)
+ }
+ end
+end
diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
index 47a73dc7e..a25f3f1fe 100644
--- a/lib/pleroma/web/admin_api/views/report_view.ex
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -5,9 +5,9 @@
defmodule Pleroma.Web.AdminAPI.ReportView do
use Pleroma.Web, :view
alias Pleroma.Activity
+ alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.Web.CommonAPI.Utils
- alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
def render("index.json", %{reports: reports}) do
@@ -23,6 +23,13 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
[account_ap_id | status_ap_ids] = report.data["object"]
account = User.get_cached_by_ap_id(account_ap_id)
+ content =
+ unless is_nil(report.data["content"]) do
+ HTML.filter_tags(report.data["content"])
+ else
+ nil
+ end
+
statuses =
Enum.map(status_ap_ids, fn ap_id ->
Activity.get_by_ap_id_with_object(ap_id)
@@ -30,12 +37,19 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
%{
id: report.id,
- account: AccountView.render("account.json", %{user: account}),
- actor: AccountView.render("account.json", %{user: user}),
- content: report.data["content"],
+ account: merge_account_views(account),
+ actor: merge_account_views(user),
+ content: content,
created_at: created_at,
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
state: report.data["state"]
}
end
+
+ defp merge_account_views(%User{} = user) do
+ Pleroma.Web.MastodonAPI.AccountView.render("account.json", %{user: user})
+ |> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
+ end
+
+ defp merge_account_views(_), do: %{}
end
diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex
index c4a6fce08..a9164ad98 100644
--- a/lib/pleroma/web/auth/pleroma_authenticator.ex
+++ b/lib/pleroma/web/auth/pleroma_authenticator.ex
@@ -24,6 +24,14 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
end
end
+ @doc """
+ Gets or creates Pleroma.Registration record from Ueberauth assigns.
+ Note: some strategies (like `keycloak`) might need extra configuration to fill `uid` from callback response —
+ see [`docs/config.md`](docs/config.md).
+ """
+ def get_registration(%Plug.Conn{assigns: %{ueberauth_auth: %{uid: nil}}}),
+ do: {:error, :missing_uid}
+
def get_registration(%Plug.Conn{
assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}
}) do
@@ -51,9 +59,10 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
def get_registration(%Plug.Conn{} = _conn), do: {:error, :missing_credentials}
+ @doc "Creates Pleroma.User record basing on params and Pleroma.Registration record."
def create_from_registration(
%Plug.Conn{params: %{"authorization" => registration_attrs}},
- registration
+ %Registration{} = registration
) do
nickname = value([registration_attrs["nickname"], Registration.nickname(registration)])
email = value([registration_attrs["email"], Registration.email(registration)])
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 25b5fedb8..8e3892bdf 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -11,7 +11,9 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.ActivityPub.Visibility
+ import Pleroma.Web.Gettext
import Pleroma.Web.CommonAPI.Utils
def follow(follower, followed) do
@@ -35,9 +37,9 @@ defmodule Pleroma.Web.CommonAPI do
end
def accept_follow_request(follower, followed) do
- with {:ok, follower} <- User.maybe_follow(follower, followed),
+ with {:ok, follower} <- User.follow(follower, followed),
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
- {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
+ {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
{:ok, _activity} <-
ActivityPub.accept(%{
to: [follower.ap_id],
@@ -51,7 +53,7 @@ defmodule Pleroma.Web.CommonAPI do
def reject_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
- {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
+ {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
{:ok, _activity} <-
ActivityPub.reject(%{
to: [follower.ap_id],
@@ -73,7 +75,7 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, delete}
else
_ ->
- {:error, "Could not delete"}
+ {:error, dgettext("errors", "Could not delete")}
end
end
@@ -84,7 +86,7 @@ defmodule Pleroma.Web.CommonAPI do
ActivityPub.announce(user, object)
else
_ ->
- {:error, "Could not repeat"}
+ {:error, dgettext("errors", "Could not repeat")}
end
end
@@ -94,7 +96,7 @@ defmodule Pleroma.Web.CommonAPI do
ActivityPub.unannounce(user, object)
else
_ ->
- {:error, "Could not unrepeat"}
+ {:error, dgettext("errors", "Could not unrepeat")}
end
end
@@ -105,7 +107,7 @@ defmodule Pleroma.Web.CommonAPI do
ActivityPub.like(user, object)
else
_ ->
- {:error, "Could not favorite"}
+ {:error, dgettext("errors", "Could not favorite")}
end
end
@@ -115,7 +117,7 @@ defmodule Pleroma.Web.CommonAPI do
ActivityPub.unlike(user, object)
else
_ ->
- {:error, "Could not unfavorite"}
+ {:error, dgettext("errors", "Could not unfavorite")}
end
end
@@ -147,10 +149,10 @@ defmodule Pleroma.Web.CommonAPI do
object = Object.get_cached_by_ap_id(object.data["id"])
{:ok, answer_activities, object}
else
- {:author, _} -> {:error, "Poll's author can't vote"}
- {:existing_votes, _} -> {:error, "Already voted"}
- {:choice_check, {_, false}} -> {:error, "Invalid indices"}
- {:count_check, false} -> {:error, "Too many choices"}
+ {:author, _} -> {:error, dgettext("errors", "Poll's author can't vote")}
+ {:existing_votes, _} -> {:error, dgettext("errors", "Already voted")}
+ {:choice_check, {_, false}} -> {:error, dgettext("errors", "Invalid indices")}
+ {:count_check, false} -> {:error, dgettext("errors", "Too many choices")}
end
end
@@ -218,7 +220,7 @@ defmodule Pleroma.Web.CommonAPI do
cw <- data["spoiler_text"] || "",
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
full_payload <- String.trim(status <> cw),
- length when length in 1..limit <- String.length(full_payload),
+ :ok <- validate_character_limit(full_payload, attachments, limit),
object <-
make_note_data(
user.ap_id,
@@ -250,7 +252,14 @@ defmodule Pleroma.Web.CommonAPI do
Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
)
else
- e -> {:error, e}
+ {:private_to_public, true} ->
+ {:error, dgettext("errors", "The message visibility must be direct")}
+
+ {:error, _} = e ->
+ e
+
+ e ->
+ {:error, e}
end
end
@@ -285,12 +294,11 @@ defmodule Pleroma.Web.CommonAPI do
},
object: %Object{
data: %{
- "to" => object_to,
"type" => "Note"
}
}
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
- true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"),
+ true <- Visibility.is_public?(activity),
%{valid?: true} = info_changeset <-
User.Info.add_pinnned_activity(user.info, activity),
changeset <-
@@ -302,7 +310,7 @@ defmodule Pleroma.Web.CommonAPI do
{:error, err}
_ ->
- {:error, "Could not pin"}
+ {:error, dgettext("errors", "Could not pin")}
end
end
@@ -319,7 +327,7 @@ defmodule Pleroma.Web.CommonAPI do
{:error, err}
_ ->
- {:error, "Could not unpin"}
+ {:error, dgettext("errors", "Could not unpin")}
end
end
@@ -327,7 +335,7 @@ defmodule Pleroma.Web.CommonAPI do
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
{:ok, activity}
else
- {:error, _} -> {:error, "conversation is already muted"}
+ {:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
end
end
@@ -372,8 +380,8 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, activity}
else
{:error, err} -> {:error, err}
- {:account_id, %{}} -> {:error, "Valid `account_id` required"}
- {:account, nil} -> {:error, "Account not found"}
+ {:account_id, %{}} -> {:error, dgettext("errors", "Valid `account_id` required")}
+ {:account, nil} -> {:error, dgettext("errors", "Account not found")}
end
end
@@ -382,14 +390,9 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, activity} <- Utils.update_report_state(activity, state) do
{:ok, activity}
else
- nil ->
- {:error, :not_found}
-
- {:error, reason} ->
- {:error, reason}
-
- _ ->
- {:error, "Could not update state"}
+ nil -> {:error, :not_found}
+ {:error, reason} -> {:error, reason}
+ _ -> {:error, dgettext("errors", "Could not update state")}
end
end
@@ -399,11 +402,8 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, activity} <- set_visibility(activity, opts) do
{:ok, activity}
else
- nil ->
- {:error, :not_found}
-
- {:error, reason} ->
- {:error, reason}
+ nil -> {:error, :not_found}
+ {:error, reason} -> {:error, reason}
end
end
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 6c9e117ae..d4bfdd7e4 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.Utils do
+ import Pleroma.Web.Gettext
+
alias Calendar.Strftime
alias Comeonin.Pbkdf2
alias Pleroma.Activity
@@ -381,7 +383,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
true <- Pbkdf2.checkpw(password, db_user.password_hash) do
{:ok, db_user}
else
- _ -> {:error, "Invalid password."}
+ _ -> {:error, dgettext("errors", "Invalid password.")}
end
end
@@ -464,7 +466,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
if String.length(comment) <= max_size do
{:ok, format_input(comment, "text/plain")}
else
- {:error, "Comment must be up to #{max_size} characters"}
+ {:error,
+ dgettext("errors", "Comment must be up to %{max_size} characters", max_size: max_size)}
end
end
@@ -499,7 +502,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
context
else
_e ->
- {:error, "No such conversation"}
+ {:error, dgettext("errors", "No such conversation")}
end
end
@@ -513,4 +516,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"inReplyTo" => object.data["id"]
}
end
+
+ def validate_character_limit(full_payload, attachments, limit) do
+ length = String.length(full_payload)
+
+ if length < limit do
+ if length > 0 or Enum.count(attachments) > 0 do
+ :ok
+ else
+ {:error, dgettext("errors", "Cannot post an empty status without attachments")}
+ end
+ else
+ {:error, dgettext("errors", "The status is over the character limit")}
+ end
+ end
end
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex
index 55706eeb8..8a753bb4f 100644
--- a/lib/pleroma/web/controller_helper.ex
+++ b/lib/pleroma/web/controller_helper.ex
@@ -15,4 +15,22 @@ defmodule Pleroma.Web.ControllerHelper do
|> put_status(status)
|> json(json)
end
+
+ @spec fetch_integer_param(map(), String.t(), integer() | nil) :: integer() | nil
+ def fetch_integer_param(params, name, default \\ nil) do
+ params
+ |> Map.get(name, default)
+ |> param_to_integer(default)
+ end
+
+ defp param_to_integer(val, _) when is_integer(val), do: val
+
+ defp param_to_integer(val, default) when is_binary(val) do
+ case Integer.parse(val) do
+ {res, _} -> res
+ _ -> default
+ end
+ end
+
+ defp param_to_integer(_, default), do: default
end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index bd76e4295..c123530dc 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -7,13 +7,9 @@ defmodule Pleroma.Web.Endpoint do
socket("/socket", Pleroma.Web.UserSocket)
- # Serve at "/" the static files from "priv/static" directory.
- #
- # You should set gzip to true if you are running phoenix.digest
- # when deploying your static files in production.
+ plug(Pleroma.Plugs.SetLocalePlug)
plug(CORSPlug)
plug(Pleroma.Plugs.HTTPSecurityPlug)
-
plug(Pleroma.Plugs.UploadedMedia)
@static_cache_control "public, no-cache"
@@ -30,6 +26,10 @@ defmodule Pleroma.Web.Endpoint do
}
)
+ # Serve at "/" the static files from "priv/static" directory.
+ #
+ # You should set gzip to true if you are running phoenix.digest
+ # when deploying your static files in production.
plug(
Plug.Static,
at: "/",
@@ -91,7 +91,7 @@ defmodule Pleroma.Web.Endpoint do
Plug.Session,
store: :cookie,
key: cookie_name,
- signing_salt: {Pleroma.Config, :get, [[__MODULE__, :signing_salt], "CqaoopA2"]},
+ signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
http_only: true,
secure: secure_cookies,
extra: extra
diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex
index f4c9fe284..f4f9e83e0 100644
--- a/lib/pleroma/web/federator/federator.ex
+++ b/lib/pleroma/web/federator/federator.ex
@@ -22,6 +22,18 @@ defmodule Pleroma.Web.Federator do
refresh_subscriptions()
end
+ @doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)"
+ # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
+ def allowed_incoming_reply_depth?(depth) do
+ max_replies_depth = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
+
+ if max_replies_depth do
+ (depth || 1) <= max_replies_depth
+ else
+ true
+ end
+ end
+
# Client API
def incoming_doc(doc) do
diff --git a/lib/pleroma/web/federator/retry_queue.ex b/lib/pleroma/web/federator/retry_queue.ex
index 71e49494f..3db948c2e 100644
--- a/lib/pleroma/web/federator/retry_queue.ex
+++ b/lib/pleroma/web/federator/retry_queue.ex
@@ -15,7 +15,9 @@ defmodule Pleroma.Web.Federator.RetryQueue do
def start_link do
enabled =
- if Mix.env() == :test, do: true, else: Pleroma.Config.get([__MODULE__, :enabled], false)
+ if Pleroma.Config.get(:env) == :test,
+ do: true,
+ else: Pleroma.Config.get([__MODULE__, :enabled], false)
if enabled do
Logger.info("Starting retry queue")
@@ -219,7 +221,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do
{:noreply, state}
end
- if Mix.env() == :test do
+ if Pleroma.Config.get(:env) == :test do
defp growth_function(_retries) do
_shutit = Pleroma.Config.get([__MODULE__, :initial_timeout])
DateTime.to_unix(DateTime.utc_now()) - 1
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex
index 3a3ec7c2a..c82b20123 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
import Ecto.Query
import Ecto.Changeset
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index d825555c6..8c2033c3a 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -14,7 +14,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.HTTP
alias Pleroma.Notification
alias Pleroma.Object
- alias Pleroma.Object.Fetcher
alias Pleroma.Pagination
alias Pleroma.Repo
alias Pleroma.ScheduledActivity
@@ -47,14 +46,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
require Logger
- plug(
- Pleroma.Plugs.RateLimitPlug,
- %{
- max_requests: Config.get([:app_account_creation, :max_requests]),
- interval: Config.get([:app_account_creation, :interval])
- }
- when action in [:account_register]
- )
+ plug(Pleroma.Plugs.RateLimiter, :app_account_creation when action == :account_register)
+ plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
@local_mastodon_name "Mastodon-Local"
@@ -143,6 +136,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
_ -> :error
end
end)
+ |> add_if_present(params, "pleroma_background_image", :background, fn value ->
+ with %Plug.Upload{} <- value,
+ {:ok, object} <- ActivityPub.upload(value, type: :background) do
+ {:ok, object.data}
+ else
+ _ -> :error
+ end
+ end)
|> Map.put(:emoji, user_info_emojis)
info_cng = User.Info.profile_update(user.info, info_params)
@@ -159,16 +160,83 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true})
)
else
- _e ->
- conn
- |> put_status(403)
- |> json(%{error: "Invalid request"})
+ _e -> render_error(conn, :forbidden, "Invalid request")
+ end
+ end
+
+ def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
+ change = Changeset.change(user, %{avatar: nil})
+ {:ok, user} = User.update_and_set_cache(change)
+ CommonAPI.update(user)
+
+ json(conn, %{url: nil})
+ end
+
+ def update_avatar(%{assigns: %{user: user}} = conn, params) do
+ {:ok, object} = ActivityPub.upload(params, type: :avatar)
+ change = Changeset.change(user, %{avatar: object.data})
+ {:ok, user} = User.update_and_set_cache(change)
+ CommonAPI.update(user)
+ %{"url" => [%{"href" => href} | _]} = object.data
+
+ json(conn, %{url: href})
+ end
+
+ def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
+ with new_info <- %{"banner" => %{}},
+ info_cng <- User.Info.profile_update(user.info, new_info),
+ changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
+ {:ok, user} <- User.update_and_set_cache(changeset) do
+ CommonAPI.update(user)
+
+ json(conn, %{url: nil})
+ end
+ end
+
+ def update_banner(%{assigns: %{user: user}} = conn, params) do
+ with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
+ new_info <- %{"banner" => object.data},
+ info_cng <- User.Info.profile_update(user.info, new_info),
+ changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
+ {:ok, user} <- User.update_and_set_cache(changeset) do
+ CommonAPI.update(user)
+ %{"url" => [%{"href" => href} | _]} = object.data
+
+ json(conn, %{url: href})
+ end
+ end
+
+ def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
+ with new_info <- %{"background" => %{}},
+ info_cng <- User.Info.profile_update(user.info, new_info),
+ changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
+ {:ok, _user} <- User.update_and_set_cache(changeset) do
+ json(conn, %{url: nil})
+ end
+ end
+
+ def update_background(%{assigns: %{user: user}} = conn, params) do
+ with {:ok, object} <- ActivityPub.upload(params, type: :background),
+ new_info <- %{"background" => object.data},
+ info_cng <- User.Info.profile_update(user.info, new_info),
+ changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
+ {:ok, _user} <- User.update_and_set_cache(changeset) do
+ %{"url" => [%{"href" => href} | _]} = object.data
+
+ json(conn, %{url: href})
end
end
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
+ chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
+
account =
- AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true})
+ AccountView.render("account.json", %{
+ user: user,
+ for: user,
+ with_pleroma_settings: true,
+ with_chat_token: chat_token
+ })
json(conn, account)
end
@@ -187,10 +255,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
account = AccountView.render("account.json", %{user: user, for: for_user})
json(conn, account)
else
- _e ->
- conn
- |> put_status(404)
- |> json(%{error: "Can't find user"})
+ _e -> render_error(conn, :not_found, "Can't find user")
end
end
@@ -234,7 +299,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
"static_url" => url,
"visible_in_picker" => true,
"url" => url,
- "tags" => tags
+ "tags" => tags,
+ # Assuming that a comma is authorized in the category name
+ "category" => (tags -- ["Custom"]) |> Enum.join(",")
}
end)
end
@@ -348,6 +415,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
with %User{} = user <- User.get_cached_by_id(params["id"]) do
+ params =
+ params
+ |> Map.put("tag", params["tagged"])
+
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
conn
@@ -434,41 +505,44 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user})
else
- nil ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
-
- false ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ nil -> render_error(conn, :not_found, "Record not found")
+ false -> render_error(conn, :not_found, "Record not found")
end
end
+ defp get_cached_vote_or_vote(user, object, choices) do
+ idempotency_key = "polls:#{user.id}:#{object.data["id"]}"
+
+ {_, res} =
+ Cachex.fetch(:idempotency_cache, idempotency_key, fn _ ->
+ case CommonAPI.vote(user, object, choices) do
+ {:error, _message} = res -> {:ignore, res}
+ res -> {:commit, res}
+ end
+ end)
+
+ res
+ end
+
def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
with %Object{} = object <- Object.get_by_id(id),
true <- object.data["type"] == "Question",
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user),
- {:ok, _activities, object} <- CommonAPI.vote(user, object, choices) do
+ {:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do
conn
|> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user})
else
nil ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ render_error(conn, :not_found, "Record not found")
false ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ render_error(conn, :not_found, "Record not found")
{:error, message} ->
conn
- |> put_status(422)
+ |> put_status(:unprocessable_entity)
|> json(%{error: message})
end
end
@@ -522,15 +596,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def post_status(conn, %{"status" => "", "media_ids" => media_ids} = params)
- when length(media_ids) > 0 do
- params =
- params
- |> Map.put("status", ".")
-
- post_status(conn, params)
- end
-
def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
params =
params
@@ -548,18 +613,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
params = Map.drop(params, ["scheduled_at"])
- case get_cached_status_or_post(conn, params) do
- {:ignore, message} ->
- conn
- |> put_status(422)
- |> json(%{error: message})
-
+ case CommonAPI.post(user, params) do
{:error, message} ->
conn
- |> put_status(422)
+ |> put_status(:unprocessable_entity)
|> json(%{error: message})
- {_, activity} ->
+ {:ok, activity} ->
conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
@@ -567,29 +627,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- defp get_cached_status_or_post(%{assigns: %{user: user}} = conn, params) do
- idempotency_key =
- case get_req_header(conn, "idempotency-key") do
- [key] -> key
- _ -> Ecto.UUID.generate()
- end
-
- Cachex.fetch(:idempotency_cache, idempotency_key, fn _ ->
- case CommonAPI.post(user, params) do
- {:ok, activity} -> activity
- {:error, message} -> {:ignore, message}
- end
- end)
- end
-
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
json(conn, %{})
else
- _e ->
- conn
- |> put_status(403)
- |> json(%{error: "Can't delete this post"})
+ _e -> render_error(conn, :forbidden, "Can't delete this post")
end
end
@@ -637,8 +679,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, reason} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
+ |> put_status(:bad_request)
+ |> json(%{"error" => reason})
end
end
@@ -714,8 +756,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, reason} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => reason}))
+ |> put_status(:forbidden)
+ |> json(%{"error" => reason})
end
end
@@ -730,8 +772,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, reason} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => reason}))
+ |> put_status(:forbidden)
+ |> json(%{"error" => reason})
end
end
@@ -809,9 +851,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn
|> json(rendered)
else
- conn
- |> put_resp_content_type("application/json")
- |> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
+ render_error(conn, :unsupported_media_type, "mascots can only be images")
end
end
end
@@ -831,7 +871,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn
|> put_view(AccountView)
- |> render(AccountView, "accounts.json", %{for: user, users: users, as: :user})
+ |> render("accounts.json", %{for: user, users: users, as: :user})
else
_ -> json(conn, [])
end
@@ -940,8 +980,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -954,8 +994,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -972,8 +1012,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -990,8 +1030,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1020,8 +1060,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1034,8 +1074,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1056,8 +1096,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1071,8 +1111,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1106,8 +1146,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1120,117 +1160,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
- end
- end
-
- def status_search_query_with_gin(q, query) do
- from([a, o] in q,
- where:
- fragment(
- "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
- o.data,
- ^query
- ),
- order_by: [desc: :id]
- )
- end
-
- def status_search_query_with_rum(q, query) do
- from([a, o] in q,
- where:
- fragment(
- "? @@ plainto_tsquery('english', ?)",
- o.fts_content,
- ^query
- ),
- order_by: [fragment("? <=> now()::date", o.inserted_at)]
- )
- end
-
- def status_search(user, query) do
- fetched =
- if Regex.match?(~r/https?:/, query) do
- with {:ok, object} <- Fetcher.fetch_object_from_id(query),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
- true <- Visibility.visible_for_user?(activity, user) do
- [activity]
- else
- _e -> []
- end
- end || []
-
- q =
- from([a, o] in Activity.with_preloaded_object(Activity),
- where: fragment("?->>'type' = 'Create'", a.data),
- where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
- limit: 40
- )
-
- q =
- if Pleroma.Config.get([:database, :rum_enabled]) do
- status_search_query_with_rum(q, query)
- else
- status_search_query_with_gin(q, query)
- end
-
- Repo.all(q) ++ fetched
- end
-
- def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
- accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
-
- statuses = status_search(user, query)
-
- tags_path = Web.base_url() <> "/tag/"
-
- tags =
- query
- |> String.split()
- |> Enum.uniq()
- |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
- |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
- |> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
-
- res = %{
- "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
- "statuses" =>
- StatusView.render("index.json", activities: statuses, for: user, as: :activity),
- "hashtags" => tags
- }
-
- json(conn, res)
- end
-
- def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
- accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
-
- statuses = status_search(user, query)
-
- tags =
- query
- |> String.split()
- |> Enum.uniq()
- |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
- |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
-
- res = %{
- "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
- "statuses" =>
- StatusView.render("index.json", activities: statuses, for: user, as: :activity),
- "hashtags" => tags
- }
-
- json(conn, res)
- end
-
- def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
- accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
-
- res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
-
- json(conn, res)
+ |> put_status(:forbidden)
+ |> json(%{error: message})
+ end
end
def favourites(%{assigns: %{user: user}} = conn, params) do
@@ -1277,13 +1209,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_view(StatusView)
|> render("index.json", %{activities: activities, for: for_user, as: :activity})
else
- nil ->
- {:error, :not_found}
-
- true ->
- conn
- |> put_status(403)
- |> json(%{error: "Can't get favorites"})
+ nil -> {:error, :not_found}
+ true -> render_error(conn, :forbidden, "Can't get favorites")
end
end
@@ -1315,10 +1242,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
res = ListView.render("list.json", list: list)
json(conn, res)
else
- _e ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ _e -> render_error(conn, :not_found, "Record not found")
end
end
@@ -1334,7 +1258,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, %{})
else
_e ->
- json(conn, "error")
+ json(conn, dgettext("errors", "error"))
end
end
@@ -1385,7 +1309,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, res)
else
_e ->
- json(conn, "error")
+ json(conn, dgettext("errors", "error"))
end
end
@@ -1409,10 +1333,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
else
- _e ->
- conn
- |> put_status(403)
- |> json(%{error: "Error."})
+ _e -> render_error(conn, :forbidden, "Error.")
end
end
@@ -1531,8 +1452,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
e ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(500, Jason.encode!(%{"error" => inspect(e)}))
+ |> put_status(:internal_server_error)
+ |> json(%{error: inspect(e)})
end
end
@@ -1700,20 +1621,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Enum.map_join(", ", fn {_k, v} -> v end)
conn
- |> put_status(422)
+ |> put_status(:unprocessable_entity)
|> json(%{error: error_message})
end
def errors(conn, {:error, :not_found}) do
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ render_error(conn, :not_found, "Record not found")
end
def errors(conn, _) do
conn
- |> put_status(500)
- |> json("Something went wrong")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "Something went wrong"))
end
def suggestions(%{assigns: %{user: user}} = conn, _) do
@@ -1833,21 +1752,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
{:error, errors} ->
conn
- |> put_status(400)
- |> json(Jason.encode!(errors))
+ |> put_status(:bad_request)
+ |> json(errors)
end
end
def account_register(%{assigns: %{app: _app}} = conn, _params) do
- conn
- |> put_status(400)
- |> json(%{error: "Missing parameters"})
+ render_error(conn, :bad_request, "Missing parameters")
end
def account_register(conn, _) do
- conn
- |> put_status(403)
- |> json(%{error: "Invalid credentials"})
+ render_error(conn, :forbidden, "Invalid credentials")
end
def conversations(%{assigns: %{user: user}} = conn, params) do
@@ -1877,21 +1792,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def try_render(conn, target, params)
when is_binary(target) do
- res = render(conn, target, params)
-
- if res == nil do
- conn
- |> put_status(501)
- |> json(%{error: "Can't display this activity"})
- else
- res
+ case render(conn, target, params) do
+ nil -> render_error(conn, :not_implemented, "Can't display this activity")
+ res -> res
end
end
def try_render(conn, _, _) do
- conn
- |> put_status(501)
- |> json(%{error: "Can't display this activity"})
+ render_error(conn, :not_implemented, "Can't display this activity")
end
defp present?(nil), do: false
diff --git a/lib/pleroma/web/mastodon_api/search_controller.ex b/lib/pleroma/web/mastodon_api/search_controller.ex
new file mode 100644
index 000000000..939f7f6cb
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/search_controller.ex
@@ -0,0 +1,90 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SearchController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Activity
+ alias Pleroma.Plugs.RateLimiter
+ alias Pleroma.User
+ alias Pleroma.Web
+ alias Pleroma.Web.ControllerHelper
+ alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.MastodonAPI.StatusView
+
+ require Logger
+ plug(RateLimiter, :search when action in [:search, :search2, :account_search])
+
+ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
+ accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
+ statuses = with_fallback(fn -> Activity.search(user, query) end, [])
+
+ tags_path = Web.base_url() <> "/tag/"
+
+ tags =
+ query
+ |> prepare_tags
+ |> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
+
+ res = %{
+ "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
+ "statuses" =>
+ StatusView.render("index.json", activities: statuses, for: user, as: :activity),
+ "hashtags" => tags
+ }
+
+ json(conn, res)
+ end
+
+ def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
+ accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end)
+ statuses = with_fallback(fn -> Activity.search(user, query) end)
+
+ tags = prepare_tags(query)
+
+ res = %{
+ "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
+ "statuses" =>
+ StatusView.render("index.json", activities: statuses, for: user, as: :activity),
+ "hashtags" => tags
+ }
+
+ json(conn, res)
+ end
+
+ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
+ accounts = User.search(query, search_options(params, user))
+ res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
+
+ json(conn, res)
+ end
+
+ defp prepare_tags(query) do
+ query
+ |> String.split()
+ |> Enum.uniq()
+ |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
+ |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+ end
+
+ defp search_options(params, user) do
+ [
+ resolve: params["resolve"] == "true",
+ following: params["following"] == "true",
+ limit: ControllerHelper.fetch_integer_param(params, "limit"),
+ offset: ControllerHelper.fetch_integer_param(params, "offset"),
+ for_user: user
+ ]
+ end
+
+ defp with_fallback(f, fallback \\ []) do
+ try do
+ f.()
+ rescue
+ error ->
+ Logger.error("#{__MODULE__} search error: #{inspect(error)}")
+ fallback
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/subscription_controller.ex b/lib/pleroma/web/mastodon_api/subscription_controller.ex
index b6c8ff808..255ee2f18 100644
--- a/lib/pleroma/web/mastodon_api/subscription_controller.ex
+++ b/lib/pleroma/web/mastodon_api/subscription_controller.ex
@@ -59,13 +59,13 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
#
def errors(conn, {:error, :not_found}) do
conn
- |> put_status(404)
- |> json("Not found")
+ |> put_status(:not_found)
+ |> json(dgettext("errors", "Not found"))
end
def errors(conn, _) do
conn
- |> put_status(500)
- |> json("Something went wrong")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "Something went wrong"))
end
end
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index b91726b45..62c516f8e 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -66,6 +66,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
defp do_render("account.json", %{user: user} = opts) do
+ display_name = HTML.strip_tags(user.name || user.nickname)
+
image = User.avatar_url(user) |> MediaProxy.url()
header = User.banner_url(user) |> MediaProxy.url()
user_info = User.get_cached_user_info(user)
@@ -96,7 +98,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
id: to_string(user.id),
username: username_from_nickname(user.nickname),
acct: user.nickname,
- display_name: user.name || user.nickname,
+ display_name: display_name,
locked: user_info.locked,
created_at: Utils.to_masto_date(user.inserted_at),
followers_count: user_info.follower_count,
@@ -125,13 +127,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
hide_follows: user.info.hide_follows,
hide_favorites: user.info.hide_favorites,
relationship: relationship,
- skip_thread_containment: user.info.skip_thread_containment
+ skip_thread_containment: user.info.skip_thread_containment,
+ background_image: image_url(user.info.background) |> MediaProxy.url()
}
}
|> maybe_put_role(user, opts[:for])
|> maybe_put_settings(user, opts[:for], user_info)
|> maybe_put_notification_settings(user, opts[:for])
|> maybe_put_settings_store(user, opts[:for], opts)
+ |> maybe_put_chat_token(user, opts[:for], opts)
end
defp username_from_nickname(string) when is_binary(string) do
@@ -163,6 +167,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_settings_store(data, _, _, _), do: data
+ defp maybe_put_chat_token(data, %User{id: id}, %User{id: id}, %{
+ with_chat_token: token
+ }) do
+ data
+ |> Kernel.put_in([:pleroma, :chat_token], token)
+ end
+
+ defp maybe_put_chat_token(data, _, _, _), do: data
+
defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do
data
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
@@ -182,4 +195,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
defp maybe_put_notification_settings(data, _, _), do: data
+
+ defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
+ defp image_url(_), do: nil
end
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index af1dcf66d..38bdec737 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.MastodonAPI.ConversationView do
use Pleroma.Web, :view
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 6836d331a..06a7251d8 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
# TODO: Add cached version.
+ defp get_replied_to_activities([]), do: %{}
+
defp get_replied_to_activities(activities) do
activities
|> Enum.map(fn
@@ -104,7 +106,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
id: to_string(activity.id),
uri: activity_object.data["id"],
url: activity_object.data["id"],
- account: AccountView.render("account.json", %{user: user}),
+ account: AccountView.render("account.json", %{user: user, for: opts[:for]}),
in_reply_to_id: nil,
in_reply_to_account_id: nil,
reblog: reblogged,
@@ -147,8 +149,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
tags = object.data["tag"] || []
sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw")
+ tag_mentions =
+ tags
+ |> Enum.filter(fn tag -> is_map(tag) and tag["type"] == "Mention" end)
+ |> Enum.map(fn tag -> tag["href"] end)
+
mentions =
- activity.recipients
+ (object.data["to"] ++ tag_mentions)
+ |> Enum.uniq()
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.filter(& &1)
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end)
@@ -221,7 +229,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
id: to_string(activity.id),
uri: object.data["id"],
url: url,
- account: AccountView.render("account.json", %{user: user}),
+ account: AccountView.render("account.json", %{user: user, for: opts[:for]}),
in_reply_to_id: reply_to && to_string(reply_to.id),
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
reblog: nil,
diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex
index abfa26754..dbd3542ea 100644
--- a/lib/pleroma/web/mastodon_api/websocket_handler.ex
+++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex
@@ -17,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
"public:media",
"public:local:media",
"user",
+ "user:notification",
"direct",
"list",
"hashtag"
@@ -28,9 +29,10 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
def init(%{qs: qs} = req, state) do
with params <- :cow_qs.parse_qs(qs),
+ sec_websocket <- :cowboy_req.header("sec-websocket-protocol", req, nil),
access_token <- List.keyfind(params, "access_token", 0),
{_, stream} <- List.keyfind(params, "stream", 0),
- {:ok, user} <- allow_request(stream, access_token),
+ {:ok, user} <- allow_request(stream, [access_token, sec_websocket]),
topic when is_binary(topic) <- expand_topic(stream, params) do
{:cowboy_websocket, req, %{user: user, topic: topic}, %{idle_timeout: @timeout}}
else
@@ -83,13 +85,21 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
end
# Public streams without authentication.
- defp allow_request(stream, nil) when stream in @anonymous_streams do
+ defp allow_request(stream, [nil, 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),
+ defp allow_request(stream, [access_token, sec_websocket]) when stream in @streams do
+ token =
+ with {"access_token", token} <- access_token do
+ token
+ else
+ _ -> sec_websocket
+ end
+
+ with true <- is_bitstring(token),
+ %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
user = %User{} <- User.get_cached_by_id(user_id) do
{:ok, user}
else
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
index cee6d8481..dd8888a02 100644
--- a/lib/pleroma/web/media_proxy/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy.ex
@@ -33,20 +33,7 @@ defmodule Pleroma.Web.MediaProxy do
def encode_url(url) do
secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
-
- # Must preserve `%2F` for compatibility with S3
- # https://git.pleroma.social/pleroma/pleroma/issues/580
- replacement = get_replacement(url, ":2F:")
-
- # The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
- base64 =
- url
- |> String.replace("%2F", replacement)
- |> URI.decode()
- |> URI.encode()
- |> String.replace(replacement, "%2F")
- |> Base.url_encode64(@base64_opts)
-
+ base64 = Base.url_encode64(url, @base64_opts)
sig = :crypto.hmac(:sha, secret, base64)
sig64 = sig |> Base.url_encode64(@base64_opts)
@@ -80,12 +67,4 @@ defmodule Pleroma.Web.MediaProxy do
|> Enum.filter(fn value -> value end)
|> Path.join()
end
-
- defp get_replacement(url, replacement) do
- if String.contains?(url, replacement) do
- get_replacement(url, replacement <> replacement)
- else
- replacement
- end
- end
end
diff --git a/lib/pleroma/web/metadata/opengraph.ex b/lib/pleroma/web/metadata/opengraph.ex
index 357b80a2d..4033ec38f 100644
--- a/lib/pleroma/web/metadata/opengraph.ex
+++ b/lib/pleroma/web/metadata/opengraph.ex
@@ -121,4 +121,6 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
acc ++ rendered_tags
end)
end
+
+ defp build_attachments(_), do: []
end
diff --git a/lib/pleroma/web/metadata/player_view.ex b/lib/pleroma/web/metadata/player_view.ex
index e9a8cfc8d..4289ebdbd 100644
--- a/lib/pleroma/web/metadata/player_view.ex
+++ b/lib/pleroma/web/metadata/player_view.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.Metadata.PlayerView do
use Pleroma.Web, :view
import Phoenix.HTML.Tag, only: [content_tag: 3, tag: 2]
diff --git a/lib/pleroma/web/metadata/rel_me.ex b/lib/pleroma/web/metadata/rel_me.ex
index 03af899c4..f87fc1973 100644
--- a/lib/pleroma/web/metadata/rel_me.ex
+++ b/lib/pleroma/web/metadata/rel_me.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.Metadata.Providers.RelMe do
alias Pleroma.Web.Metadata.Providers.Provider
@behaviour Provider
diff --git a/lib/pleroma/web/metadata/twitter_card.ex b/lib/pleroma/web/metadata/twitter_card.ex
index 040b872e7..8dd01e0d5 100644
--- a/lib/pleroma/web/metadata/twitter_card.ex
+++ b/lib/pleroma/web/metadata/twitter_card.ex
@@ -117,6 +117,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
end)
end
+ defp build_attachments(_id, _object), do: []
+
defp player_url(id) do
Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id)
end
diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
index 489d5d3a5..b786a521b 100644
--- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
+++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
@@ -29,7 +29,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
else
false ->
conn
- |> put_status(403)
+ |> put_status(:forbidden)
|> json(false)
_ ->
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 32be430b7..cd9a4f4a8 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -162,7 +162,8 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
accountActivationRequired: Config.get([:instance, :account_activation_required], false),
invitesEnabled: Config.get([:instance, :invites_enabled], false),
features: features,
- restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames])
+ restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
+ skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
}
}
end
@@ -200,8 +201,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
end
def nodeinfo(conn, _) do
- conn
- |> put_status(404)
- |> json(%{error: "Nodeinfo schema version not handled"})
+ render_error(conn, :not_found, "Nodeinfo schema version not handled")
end
end
diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex
index 18973413e..d53e20d12 100644
--- a/lib/pleroma/web/oauth/authorization.ex
+++ b/lib/pleroma/web/oauth/authorization.ex
@@ -76,14 +76,16 @@ defmodule Pleroma.Web.OAuth.Authorization do
def use_token(%Authorization{used: true}), do: {:error, "already used"}
@spec delete_user_authorizations(User.t()) :: {integer(), any()}
- def delete_user_authorizations(%User{id: user_id}) do
- from(
- a in Pleroma.Web.OAuth.Authorization,
- where: a.user_id == ^user_id
- )
+ def delete_user_authorizations(%User{} = user) do
+ user
+ |> delete_by_user_query
|> Repo.delete_all()
end
+ def delete_by_user_query(%User{id: user_id}) do
+ from(a in __MODULE__, where: a.user_id == ^user_id)
+ end
+
@doc "gets auth for app by token"
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
def get_by_token(%App{id: app_id} = _app, token) do
diff --git a/lib/pleroma/web/oauth/fallback_controller.ex b/lib/pleroma/web/oauth/fallback_controller.ex
index e3984f009..dd7f08bf1 100644
--- a/lib/pleroma/web/oauth/fallback_controller.ex
+++ b/lib/pleroma/web/oauth/fallback_controller.ex
@@ -9,21 +9,24 @@ defmodule Pleroma.Web.OAuth.FallbackController do
def call(conn, {:register, :generic_error}) do
conn
|> put_status(:internal_server_error)
- |> put_flash(:error, "Unknown error, please check the details and try again.")
+ |> put_flash(
+ :error,
+ dgettext("errors", "Unknown error, please check the details and try again.")
+ )
|> OAuthController.registration_details(conn.params)
end
def call(conn, {:register, _error}) do
conn
|> put_status(:unauthorized)
- |> put_flash(:error, "Invalid Username/Password")
+ |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.registration_details(conn.params)
end
def call(conn, _error) do
conn
|> put_status(:unauthorized)
- |> put_flash(:error, "Invalid Username/Password")
+ |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.authorize(conn.params)
end
end
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index ae2b80d95..ef53b7ae3 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.OAuth.OAuthController do
use Pleroma.Web, :controller
+ alias Pleroma.Helpers.UriHelper
alias Pleroma.Registration
alias Pleroma.Repo
alias Pleroma.User
@@ -17,6 +18,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
alias Pleroma.Web.OAuth.Scopes
+ require Logger
+
if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth)
plug(:fetch_session)
@@ -24,34 +27,25 @@ defmodule Pleroma.Web.OAuth.OAuthController do
action_fallback(Pleroma.Web.OAuth.FallbackController)
+ @oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
+
# Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
- def authorize(conn, %{"authorization" => _} = params) do
+ def authorize(%Plug.Conn{} = conn, %{"authorization" => _} = params) do
{auth_attrs, params} = Map.pop(params, "authorization")
authorize(conn, Map.merge(params, auth_attrs))
end
- def authorize(%{assigns: %{token: %Token{} = token}} = conn, params) do
+ def authorize(%Plug.Conn{assigns: %{token: %Token{}}} = conn, params) do
if ControllerHelper.truthy_param?(params["force_login"]) do
do_authorize(conn, params)
else
- redirect_uri =
- if is_binary(params["redirect_uri"]) do
- params["redirect_uri"]
- else
- app = Repo.preload(token, :app).app
-
- app.redirect_uris
- |> String.split()
- |> Enum.at(0)
- end
-
- redirect(conn, external: redirect_uri(conn, redirect_uri))
+ handle_existing_authorization(conn, params)
end
end
- def authorize(conn, params), do: do_authorize(conn, params)
+ def authorize(%Plug.Conn{} = conn, params), do: do_authorize(conn, params)
- defp do_authorize(conn, params) do
+ defp do_authorize(%Plug.Conn{} = conn, params) do
app = Repo.get_by(App, client_id: params["client_id"])
available_scopes = (app && app.scopes) || []
scopes = Scopes.fetch_scopes(params, available_scopes)
@@ -68,8 +62,41 @@ defmodule Pleroma.Web.OAuth.OAuthController do
})
end
+ defp handle_existing_authorization(
+ %Plug.Conn{assigns: %{token: %Token{} = token}} = conn,
+ %{"redirect_uri" => @oob_token_redirect_uri}
+ ) do
+ render(conn, "oob_token_exists.html", %{token: token})
+ end
+
+ defp handle_existing_authorization(
+ %Plug.Conn{assigns: %{token: %Token{} = token}} = conn,
+ %{} = params
+ ) do
+ app = Repo.preload(token, :app).app
+
+ redirect_uri =
+ if is_binary(params["redirect_uri"]) do
+ params["redirect_uri"]
+ else
+ default_redirect_uri(app)
+ end
+
+ if redirect_uri in String.split(app.redirect_uris) do
+ redirect_uri = redirect_uri(conn, redirect_uri)
+ url_params = %{access_token: token.token}
+ url_params = UriHelper.append_param_if_present(url_params, :state, params["state"])
+ url = UriHelper.append_uri_params(redirect_uri, url_params)
+ redirect(conn, external: url)
+ else
+ conn
+ |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
+ |> redirect(external: redirect_uri(conn, redirect_uri))
+ end
+ end
+
def create_authorization(
- conn,
+ %Plug.Conn{} = conn,
%{"authorization" => _} = params,
opts \\ []
) do
@@ -81,35 +108,33 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
- def after_create_authorization(conn, auth, %{
- "authorization" => %{"redirect_uri" => redirect_uri} = auth_attrs
+ def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
+ "authorization" => %{"redirect_uri" => @oob_token_redirect_uri}
}) do
- redirect_uri = redirect_uri(conn, redirect_uri)
-
- if redirect_uri == "urn:ietf:wg:oauth:2.0:oob" do
- render(conn, "results.html", %{
- auth: auth
- })
- else
- connector = if String.contains?(redirect_uri, "?"), do: "&", else: "?"
- url = "#{redirect_uri}#{connector}"
- url_params = %{:code => auth.token}
-
- url_params =
- if auth_attrs["state"] do
- Map.put(url_params, :state, auth_attrs["state"])
- else
- url_params
- end
-
- url = "#{url}#{Plug.Conn.Query.encode(url_params)}"
+ render(conn, "oob_authorization_created.html", %{auth: auth})
+ end
+ def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
+ "authorization" => %{"redirect_uri" => redirect_uri} = auth_attrs
+ }) do
+ app = Repo.preload(auth, :app).app
+
+ # An extra safety measure before we redirect (also done in `do_create_authorization/2`)
+ if redirect_uri in String.split(app.redirect_uris) do
+ redirect_uri = redirect_uri(conn, redirect_uri)
+ url_params = %{code: auth.token}
+ url_params = UriHelper.append_param_if_present(url_params, :state, auth_attrs["state"])
+ url = UriHelper.append_uri_params(redirect_uri, url_params)
redirect(conn, external: url)
+ else
+ conn
+ |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
+ |> redirect(external: redirect_uri(conn, redirect_uri))
end
end
defp handle_create_authorization_error(
- conn,
+ %Plug.Conn{} = conn,
{:error, scopes_issue},
%{"authorization" => _} = params
)
@@ -117,31 +142,31 @@ defmodule Pleroma.Web.OAuth.OAuthController do
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39
conn
- |> put_flash(:error, "This action is outside the authorized scopes")
+ |> put_flash(:error, dgettext("errors", "This action is outside the authorized scopes"))
|> put_status(:unauthorized)
|> authorize(params)
end
defp handle_create_authorization_error(
- conn,
+ %Plug.Conn{} = conn,
{:auth_active, false},
%{"authorization" => _} = params
) do
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
conn
- |> put_flash(:error, "Your login is missing a confirmed e-mail address")
+ |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))
|> put_status(:forbidden)
|> authorize(params)
end
- defp handle_create_authorization_error(conn, error, %{"authorization" => _}) do
+ defp handle_create_authorization_error(%Plug.Conn{} = conn, error, %{"authorization" => _}) do
Authenticator.handle_error(conn, error)
end
@doc "Renew access_token with refresh_token"
def token_exchange(
- conn,
+ %Plug.Conn{} = conn,
%{"grant_type" => "refresh_token", "refresh_token" => token} = _params
) do
with {:ok, app} <- Token.Utils.fetch_app(conn),
@@ -151,13 +176,11 @@ defmodule Pleroma.Web.OAuth.OAuthController do
json(conn, Token.Response.build(user, token, response_attrs))
else
- _error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ _error -> render_invalid_credentials_error(conn)
end
end
- def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
+ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "authorization_code"} = params) do
with {:ok, app} <- Token.Utils.fetch_app(conn),
fixed_token = Token.Utils.fix_padding(params["code"]),
{:ok, auth} <- Authorization.get_by_token(app, fixed_token),
@@ -167,14 +190,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
json(conn, Token.Response.build(user, token, response_attrs))
else
- _error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ _error -> render_invalid_credentials_error(conn)
end
end
def token_exchange(
- conn,
+ %Plug.Conn{} = conn,
%{"grant_type" => "password"} = params
) do
with {:ok, %User{} = user} <- Authenticator.get_user(conn),
@@ -189,23 +210,18 @@ defmodule Pleroma.Web.OAuth.OAuthController do
{:auth_active, false} ->
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
- conn
- |> put_status(:forbidden)
- |> json(%{error: "Your login is missing a confirmed e-mail address"})
+ render_error(conn, :forbidden, "Your login is missing a confirmed e-mail address")
{:user_active, false} ->
- conn
- |> put_status(:forbidden)
- |> json(%{error: "Your account is currently disabled"})
+ render_error(conn, :forbidden, "Your account is currently disabled")
_error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ render_invalid_credentials_error(conn)
end
end
def token_exchange(
- conn,
+ %Plug.Conn{} = conn,
%{"grant_type" => "password", "name" => name, "password" => _password} = params
) do
params =
@@ -216,22 +232,20 @@ defmodule Pleroma.Web.OAuth.OAuthController do
token_exchange(conn, params)
end
- def token_exchange(conn, %{"grant_type" => "client_credentials"} = _params) do
+ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "client_credentials"} = _params) do
with {:ok, app} <- Token.Utils.fetch_app(conn),
{:ok, auth} <- Authorization.create_authorization(app, %User{}),
{:ok, token} <- Token.exchange_token(app, auth) do
json(conn, Token.Response.build_for_client_credentials(token))
else
- _error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ _error -> render_invalid_credentials_error(conn)
end
end
# Bad request
- def token_exchange(conn, params), do: bad_request(conn, params)
+ def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
- def token_revoke(conn, %{"token" => _token} = params) do
+ def token_revoke(%Plug.Conn{} = conn, %{"token" => _token} = params) do
with {:ok, app} <- Token.Utils.fetch_app(conn),
{:ok, _token} <- RevokeToken.revoke(app, params) do
json(conn, %{})
@@ -242,17 +256,18 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
- def token_revoke(conn, params), do: bad_request(conn, params)
+ def token_revoke(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
# Response for bad request
- defp bad_request(conn, _) do
- conn
- |> put_status(500)
- |> json(%{error: "Bad request"})
+ defp bad_request(%Plug.Conn{} = conn, _) do
+ render_error(conn, :internal_server_error, "Bad request")
end
@doc "Prepares OAuth request to provider for Ueberauth"
- def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do
+ def prepare_request(%Plug.Conn{} = conn, %{
+ "provider" => provider,
+ "authorization" => auth_attrs
+ }) do
scope =
auth_attrs
|> Scopes.fetch_scopes([])
@@ -273,12 +288,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do
redirect(conn, to: o_auth_path(conn, :request, provider, params))
end
- def request(conn, params) do
+ def request(%Plug.Conn{} = conn, params) do
message =
if params["provider"] do
- "Unsupported OAuth provider: #{params["provider"]}."
+ dgettext("errors", "Unsupported OAuth provider: %{provider}.",
+ provider: params["provider"]
+ )
else
- "Bad OAuth request."
+ dgettext("errors", "Bad OAuth request.")
end
conn
@@ -286,17 +303,20 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|> redirect(to: "/")
end
- def callback(%{assigns: %{ueberauth_failure: failure}} = conn, params) do
+ def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params) do
params = callback_params(params)
messages = for e <- Map.get(failure, :errors, []), do: e.message
message = Enum.join(messages, "; ")
conn
- |> put_flash(:error, "Failed to authenticate: #{message}.")
+ |> put_flash(
+ :error,
+ dgettext("errors", "Failed to authenticate: %{message}.", message: message)
+ )
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
end
- def callback(conn, params) do
+ def callback(%Plug.Conn{} = conn, params) do
params = callback_params(params)
with {:ok, registration} <- Authenticator.get_registration(conn) do
@@ -314,13 +334,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do
})
conn
- |> put_session(:registration_id, registration.id)
+ |> put_session_registration_id(registration.id)
|> registration_details(%{"authorization" => registration_params})
end
else
- _ ->
+ error ->
+ Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns]))
+
conn
- |> put_flash(:error, "Failed to set up user account.")
+ |> put_flash(:error, dgettext("errors", "Failed to set up user account."))
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
end
end
@@ -329,7 +351,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
Map.merge(params, Jason.decode!(state))
end
- def registration_details(conn, %{"authorization" => auth_attrs}) do
+ def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do
render(conn, "register.html", %{
client_id: auth_attrs["client_id"],
redirect_uri: auth_attrs["redirect_uri"],
@@ -340,7 +362,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
})
end
- def register(conn, %{"authorization" => _, "op" => "connect"} = params) do
+ def register(%Plug.Conn{} = conn, %{"authorization" => _, "op" => "connect"} = params) do
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
%Registration{} = registration <- Repo.get(Registration, registration_id),
{_, {:ok, auth}} <-
@@ -359,7 +381,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
- def register(conn, %{"authorization" => _, "op" => "register"} = params) do
+ def register(%Plug.Conn{} = conn, %{"authorization" => _, "op" => "register"} = params) do
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
%Registration{} = registration <- Repo.get(Registration, registration_id),
{:ok, user} <- Authenticator.create_from_registration(conn, registration) do
@@ -395,7 +417,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
defp do_create_authorization(
- conn,
+ %Plug.Conn{} = conn,
%{
"authorization" =>
%{
@@ -416,13 +438,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
# Special case: Local MastodonFE
- defp redirect_uri(conn, "."), do: mastodon_api_url(conn, :login)
+ defp redirect_uri(%Plug.Conn{} = conn, "."), do: mastodon_api_url(conn, :login)
- defp redirect_uri(_conn, redirect_uri), do: redirect_uri
+ defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
- defp get_session_registration_id(conn), do: get_session(conn, :registration_id)
+ defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :registration_id)
- defp put_session_registration_id(conn, registration_id),
+ defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
do: put_session(conn, :registration_id, registration_id)
@spec validate_scopes(App.t(), map()) ::
@@ -432,4 +454,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|> Scopes.fetch_scopes(app.scopes)
|> Scopes.validates(app.scopes)
end
+
+ def default_redirect_uri(%App{} = app) do
+ app.redirect_uris
+ |> String.split()
+ |> Enum.at(0)
+ end
+
+ defp render_invalid_credentials_error(conn) do
+ render_error(conn, :bad_request, "Invalid credentials")
+ end
end
diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex
index f412f7eb2..90c304487 100644
--- a/lib/pleroma/web/oauth/token.ex
+++ b/lib/pleroma/web/oauth/token.ex
@@ -14,7 +14,6 @@ defmodule Pleroma.Web.OAuth.Token do
alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.OAuth.Token.Query
- @expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
@type t :: %__MODULE__{}
schema "oauth_tokens" do
@@ -78,7 +77,7 @@ defmodule Pleroma.Web.OAuth.Token do
defp put_valid_until(changeset, attrs) do
expires_in =
- Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), @expires_in))
+ Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), expires_in()))
changeset
|> change(%{valid_until: expires_in})
@@ -123,4 +122,6 @@ defmodule Pleroma.Web.OAuth.Token do
end
def is_expired?(_), do: false
+
+ defp expires_in, do: Pleroma.Config.get([:oauth2, :token_expires_in], 600)
end
diff --git a/lib/pleroma/web/oauth/token/response.ex b/lib/pleroma/web/oauth/token/response.ex
index 64e78b183..266110814 100644
--- a/lib/pleroma/web/oauth/token/response.ex
+++ b/lib/pleroma/web/oauth/token/response.ex
@@ -1,18 +1,20 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Response do
@moduledoc false
alias Pleroma.User
alias Pleroma.Web.OAuth.Token.Utils
- @expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
-
@doc false
def build(%User{} = user, token, opts \\ %{}) do
%{
token_type: "Bearer",
access_token: token.token,
refresh_token: token.refresh_token,
- expires_in: @expires_in,
+ expires_in: expires_in(),
scope: Enum.join(token.scopes, " "),
me: user.ap_id
}
@@ -25,8 +27,10 @@ defmodule Pleroma.Web.OAuth.Token.Response do
access_token: token.token,
refresh_token: token.refresh_token,
created_at: Utils.format_created_at(token),
- expires_in: @expires_in,
+ expires_in: expires_in(),
scope: Enum.join(token.scopes, " ")
}
end
+
+ defp expires_in, do: Pleroma.Config.get([:oauth2, :token_expires_in], 600)
end
diff --git a/lib/pleroma/web/oauth/token/strategy/refresh_token.ex b/lib/pleroma/web/oauth/token/strategy/refresh_token.ex
index 7df0be14e..c620050c8 100644
--- a/lib/pleroma/web/oauth/token/strategy/refresh_token.ex
+++ b/lib/pleroma/web/oauth/token/strategy/refresh_token.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
@moduledoc """
Functions for dealing with refresh token strategy.
diff --git a/lib/pleroma/web/oauth/token/strategy/revoke.ex b/lib/pleroma/web/oauth/token/strategy/revoke.ex
index dea63ca54..983f095b4 100644
--- a/lib/pleroma/web/oauth/token/strategy/revoke.ex
+++ b/lib/pleroma/web/oauth/token/strategy/revoke.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do
@moduledoc """
Functions for dealing with revocation.
diff --git a/lib/pleroma/web/oauth/token/utils.ex b/lib/pleroma/web/oauth/token/utils.ex
index 7a4fddafd..1e8765e93 100644
--- a/lib/pleroma/web/oauth/token/utils.ex
+++ b/lib/pleroma/web/oauth/token/utils.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Utils do
@moduledoc """
Auxiliary functions for dealing with tokens.
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
index ec6e5cfaf..8e0adad91 100644
--- a/lib/pleroma/web/ostatus/handlers/note_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Federator
alias Pleroma.Web.OStatus
alias Pleroma.Web.XML
@@ -88,14 +89,15 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
Map.put(note, "external_url", url)
end
- def fetch_replied_to_activity(entry, in_reply_to) do
+ def fetch_replied_to_activity(entry, in_reply_to, options \\ []) do
with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do
activity
else
_e ->
- with in_reply_to_href when not is_nil(in_reply_to_href) <-
+ with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
+ in_reply_to_href when not is_nil(in_reply_to_href) <-
XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
- {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href) do
+ {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href, options) do
activity
else
_e -> nil
@@ -104,7 +106,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
end
# TODO: Clean this up a bit.
- def handle_note(entry, doc \\ nil) do
+ def handle_note(entry, doc \\ nil, options \\ []) do
with id <- XML.string_from_xpath("//id", entry),
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
[author] <- :xmerl_xpath.string('//author[1]', doc),
@@ -112,7 +114,8 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
content_html <- OStatus.get_content(entry),
cw <- OStatus.get_cw(entry),
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
- in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to),
+ options <- Keyword.put(options, :depth, (options[:depth] || 0) + 1),
+ in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to, options),
in_reply_to_object <-
(in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil,
in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to,
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
index 6ed089d84..502410c83 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -54,7 +54,7 @@ defmodule Pleroma.Web.OStatus do
"#{Web.base_url()}/ostatus_subscribe?acct={uri}"
end
- def handle_incoming(xml_string) do
+ def handle_incoming(xml_string, options \\ []) do
with doc when doc != :error <- parse_document(xml_string) do
with {:ok, actor_user} <- find_make_or_update_user(doc),
do: Pleroma.Instances.set_reachable(actor_user.ap_id)
@@ -91,10 +91,12 @@ defmodule Pleroma.Web.OStatus do
_ ->
case object_type do
'http://activitystrea.ms/schema/1.0/note' ->
- with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity
+ with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options),
+ do: activity
'http://activitystrea.ms/schema/1.0/comment' ->
- with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity
+ with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options),
+ do: activity
_ ->
Logger.error("Couldn't parse incoming document")
@@ -359,7 +361,7 @@ defmodule Pleroma.Web.OStatus do
end
end
- def fetch_activity_from_atom_url(url) do
+ def fetch_activity_from_atom_url(url, options \\ []) do
with true <- String.starts_with?(url, "http"),
{:ok, %{body: body, status: code}} when code in 200..299 <-
HTTP.get(
@@ -367,7 +369,7 @@ defmodule Pleroma.Web.OStatus do
[{:Accept, "application/atom+xml"}]
) do
Logger.debug("Got document from #{url}, handling...")
- handle_incoming(body)
+ handle_incoming(body, options)
else
e ->
Logger.debug("Couldn't get #{url}: #{inspect(e)}")
@@ -375,13 +377,13 @@ defmodule Pleroma.Web.OStatus do
end
end
- def fetch_activity_from_html_url(url) do
+ def fetch_activity_from_html_url(url, options \\ []) do
Logger.debug("Trying to fetch #{url}")
with true <- String.starts_with?(url, "http"),
{:ok, %{body: body}} <- HTTP.get(url, []),
{:ok, atom_url} <- get_atom_url(body) do
- fetch_activity_from_atom_url(atom_url)
+ fetch_activity_from_atom_url(atom_url, options)
else
e ->
Logger.debug("Couldn't get #{url}: #{inspect(e)}")
@@ -389,11 +391,11 @@ defmodule Pleroma.Web.OStatus do
end
end
- def fetch_activity_from_url(url) do
- with {:ok, [_ | _] = activities} <- fetch_activity_from_atom_url(url) do
+ def fetch_activity_from_url(url, options \\ []) do
+ with {:ok, [_ | _] = activities} <- fetch_activity_from_atom_url(url, options) do
{:ok, activities}
else
- _e -> fetch_activity_from_html_url(url)
+ _e -> fetch_activity_from_html_url(url, options)
end
rescue
e ->
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 2fb6ce41b..372d52899 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -245,14 +245,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
end
def errors(conn, {:error, :not_found}) do
- conn
- |> put_status(404)
- |> text("Not found")
+ render_error(conn, :not_found, "Not found")
end
def errors(conn, _) do
- conn
- |> put_status(500)
- |> text("Something went wrong")
+ render_error(conn, :internal_server_error, "Something went wrong")
end
end
diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex
index 26eb614a6..d376e2069 100644
--- a/lib/pleroma/web/rel_me.ex
+++ b/lib/pleroma/web/rel_me.ex
@@ -10,7 +10,7 @@ defmodule Pleroma.Web.RelMe do
with_body: true
]
- if Mix.env() == :test do
+ if Pleroma.Config.get(:env) == :test do
def parse(url) when is_binary(url), do: parse_url(url)
else
def parse(url) when is_binary(url) do
diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
index 9bc8f2559..6506de46c 100644
--- a/lib/pleroma/web/rich_media/helpers.ex
+++ b/lib/pleroma/web/rich_media/helpers.ex
@@ -4,25 +4,53 @@
defmodule Pleroma.Web.RichMedia.Helpers do
alias Pleroma.Activity
+ alias Pleroma.Config
alias Pleroma.HTML
alias Pleroma.Object
alias Pleroma.Web.RichMedia.Parser
+ @spec validate_page_url(any()) :: :ok | :error
defp validate_page_url(page_url) when is_binary(page_url) do
- if AutoLinker.Parser.is_url?(page_url, true) do
- URI.parse(page_url) |> validate_page_url
- else
- :error
+ validate_tld = Application.get_env(:auto_linker, :opts)[:validate_tld]
+
+ page_url
+ |> AutoLinker.Parser.url?(scheme: true, validate_tld: validate_tld)
+ |> parse_uri(page_url)
+ end
+
+ defp validate_page_url(%URI{host: host, scheme: scheme, authority: authority})
+ when scheme == "https" and not is_nil(authority) do
+ cond do
+ host in Config.get([:rich_media, :ignore_hosts], []) ->
+ :error
+
+ get_tld(host) in Config.get([:rich_media, :ignore_tld], []) ->
+ :error
+
+ true ->
+ :ok
end
end
- defp validate_page_url(%URI{authority: nil}), do: :error
- defp validate_page_url(%URI{scheme: nil}), do: :error
- defp validate_page_url(%URI{}), do: :ok
defp validate_page_url(_), do: :error
+ defp parse_uri(true, url) do
+ url
+ |> URI.parse()
+ |> validate_page_url
+ end
+
+ defp parse_uri(_, _), do: :error
+
+ defp get_tld(host) do
+ host
+ |> String.split(".")
+ |> Enum.reverse()
+ |> hd
+ end
+
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
- with true <- Pleroma.Config.get([:rich_media, :enabled]),
+ with true <- Config.get([:rich_media, :enabled]),
%Object{} = object <- Object.normalize(activity),
false <- object.data["sensitive"] || false,
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex
index e4595800c..21cd47890 100644
--- a/lib/pleroma/web/rich_media/parser.ex
+++ b/lib/pleroma/web/rich_media/parser.ex
@@ -18,7 +18,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
def parse(nil), do: {:error, "No URL provided"}
- if Mix.env() == :test do
+ if Pleroma.Config.get(:env) == :test do
def parse(url), do: parse_url(url)
else
def parse(url) do
diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
index 4a7c5eae0..913975616 100644
--- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
+++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
@@ -1,15 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
- with elements = [_ | _] <- get_elements(html, key_name, prefix),
- meta_data =
- Enum.reduce(elements, data, fn el, acc ->
- attributes = normalize_attributes(el, prefix, key_name, value_name)
+ meta_data =
+ html
+ |> get_elements(key_name, prefix)
+ |> Enum.reduce(data, fn el, acc ->
+ attributes = normalize_attributes(el, prefix, key_name, value_name)
- Map.merge(acc, attributes)
- end) do
- {:ok, meta_data}
+ Map.merge(acc, attributes)
+ end)
+ |> maybe_put_title(html)
+
+ if Enum.empty?(meta_data) do
+ {:error, error_message}
else
- _e -> {:error, error_message}
+ {:ok, meta_data}
end
end
@@ -27,4 +35,19 @@ defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
%{String.to_atom(data[key_name]) => data[value_name]}
end
+
+ defp maybe_put_title(%{title: _} = meta, _), do: meta
+
+ defp maybe_put_title(meta, html) when meta != %{} do
+ case get_page_title(html) do
+ "" -> meta
+ title -> Map.put_new(meta, :title, title)
+ end
+ end
+
+ defp maybe_put_title(meta, _), do: meta
+
+ defp get_page_title(html) do
+ Floki.find(html, "title") |> Floki.text()
+ end
end
diff --git a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
index 2530b8c9d..875637c4d 100644
--- a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
+++ b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html),
diff --git a/lib/pleroma/web/rich_media/parsers/ogp.ex b/lib/pleroma/web/rich_media/parsers/ogp.ex
index 0e1a0e719..d40fa009f 100644
--- a/lib/pleroma/web/rich_media/parsers/ogp.ex
+++ b/lib/pleroma/web/rich_media/parsers/ogp.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.OGP do
def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
index a317c3e78..e4efe2dd0 100644
--- a/lib/pleroma/web/rich_media/parsers/twitter_card.ex
+++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index e699f6ae2..d53fa8a35 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -27,6 +27,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.UserEnabledPlug)
plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureUserKeyPlug)
+ plug(Pleroma.Plugs.IdempotencyPlug)
end
pipeline :authenticated_api do
@@ -41,6 +42,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.UserEnabledPlug)
plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
+ plug(Pleroma.Plugs.IdempotencyPlug)
end
pipeline :admin_api do
@@ -57,6 +59,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
plug(Pleroma.Plugs.UserIsAdminPlug)
+ plug(Pleroma.Plugs.IdempotencyPlug)
end
pipeline :mastodon_html do
@@ -133,8 +136,8 @@ defmodule Pleroma.Web.Router do
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
pipe_through(:pleroma_api)
- get("/password_reset/:token", UtilController, :show_password_reset)
- post("/password_reset", UtilController, :password_reset)
+ get("/password_reset/:token", PasswordController, :reset, as: :reset_password)
+ post("/password_reset", PasswordController, :do_reset, as: :reset_password)
get("/emoji", UtilController, :emoji)
get("/captcha", UtilController, :captcha)
get("/healthcheck", UtilController, :healthcheck)
@@ -202,6 +205,9 @@ defmodule Pleroma.Web.Router do
put("/statuses/:id", AdminAPIController, :status_update)
delete("/statuses/:id", AdminAPIController, :status_delete)
+
+ get("/config", AdminAPIController, :config_show)
+ post("/config", AdminAPIController, :config_update)
end
scope "/", Pleroma.Web.TwitterAPI do
@@ -316,6 +322,10 @@ defmodule Pleroma.Web.Router do
patch("/accounts/update_credentials", MastodonAPIController, :update_credentials)
+ patch("/accounts/update_avatar", MastodonAPIController, :update_avatar)
+ patch("/accounts/update_banner", MastodonAPIController, :update_banner)
+ patch("/accounts/update_background", MastodonAPIController, :update_background)
+
post("/statuses", MastodonAPIController, :post_status)
delete("/statuses/:id", MastodonAPIController, :delete_status)
@@ -412,7 +422,7 @@ defmodule Pleroma.Web.Router do
get("/trends", MastodonAPIController, :empty_array)
- get("/accounts/search", MastodonAPIController, :account_search)
+ get("/accounts/search", SearchController, :account_search)
scope [] do
pipe_through(:oauth_read_or_public)
@@ -431,7 +441,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/following", MastodonAPIController, :following)
get("/accounts/:id", MastodonAPIController, :user)
- get("/search", MastodonAPIController, :search)
+ get("/search", SearchController, :search)
get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
end
@@ -439,7 +449,7 @@ defmodule Pleroma.Web.Router do
scope "/api/v2", Pleroma.Web.MastodonAPI do
pipe_through([:api, :oauth_read_or_public])
- get("/search", MastodonAPIController, :search2)
+ get("/search", SearchController, :search2)
end
scope "/api", Pleroma.Web do
@@ -604,12 +614,6 @@ defmodule Pleroma.Web.Router do
post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
end
- scope "/", Pleroma.Web do
- pipe_through(:oembed)
-
- get("/oembed", OEmbed.OEmbedController, :url)
- end
-
pipeline :activitypub do
plug(:accepts, ["activity+json", "json"])
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
@@ -699,7 +703,7 @@ defmodule Pleroma.Web.Router do
get("/:sig/:url/:filename", MediaProxyController, :remote)
end
- if Mix.env() == :dev do
+ if Pleroma.Config.get(:env) == :dev do
scope "/dev" do
pipe_through([:mailbox_preview])
@@ -724,6 +728,7 @@ end
defmodule Fallback.RedirectController do
use Pleroma.Web, :controller
+ require Logger
alias Pleroma.User
alias Pleroma.Web.Metadata
@@ -750,7 +755,20 @@ defmodule Fallback.RedirectController do
def redirector_with_meta(conn, params) do
{:ok, index_content} = File.read(index_file_path())
- tags = Metadata.build_tags(params)
+
+ tags =
+ try do
+ Metadata.build_tags(params)
+ rescue
+ e ->
+ Logger.error(
+ "Metadata rendering for #{conn.request_path} failed.\n" <>
+ Exception.format(:error, e, __STACKTRACE__)
+ )
+
+ ""
+ end
+
response = String.replace(index_content, "<!--server-generated-meta-->", tags)
conn
diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex
index 19e3ef401..9b01ebcc6 100644
--- a/lib/pleroma/web/salmon/salmon.ex
+++ b/lib/pleroma/web/salmon/salmon.ex
@@ -161,7 +161,7 @@ defmodule Pleroma.Web.Salmon do
do: Instances.set_reachable(url)
Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
- :ok
+ {:ok, code}
else
e ->
unless params[:unreachable_since], do: Instances.set_reachable(url)
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index a23f80f26..4f325113a 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -110,23 +110,18 @@ defmodule Pleroma.Web.Streamer do
{:noreply, topics}
end
- def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do
- topic = "user:#{item.user_id}"
-
- Enum.each(topics[topic] || [], fn socket ->
- json =
- %{
- event: "notification",
- payload:
- NotificationView.render("show.json", %{
- notification: item,
- for: socket.assigns["user"]
- })
- |> Jason.encode!()
- }
- |> Jason.encode!()
-
- send(socket.transport_pid, {:text, json})
+ def handle_cast(
+ %{action: :stream, topic: topic, item: %Notification{} = item},
+ topics
+ )
+ when topic in ["user", "user:notification"] do
+ topics
+ |> Map.get("#{topic}:#{item.user_id}", [])
+ |> Enum.each(fn socket ->
+ send(
+ socket.transport_pid,
+ {:text, represent_notification(socket.assigns[:user], item)}
+ )
end)
{:noreply, topics}
@@ -216,6 +211,20 @@ defmodule Pleroma.Web.Streamer do
|> Jason.encode!()
end
+ @spec represent_notification(User.t(), Notification.t()) :: binary()
+ defp represent_notification(%User{} = user, %Notification{} = notify) do
+ %{
+ event: "notification",
+ payload:
+ NotificationView.render(
+ "show.json",
+ %{notification: notify, for: user}
+ )
+ |> Jason.encode!()
+ }
+ |> Jason.encode!()
+ end
+
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
Enum.each(topics[topic] || [], fn socket ->
# Get the current user so we have up-to-date blocks etc.
@@ -274,7 +283,7 @@ defmodule Pleroma.Web.Streamer do
end)
end
- defp internal_topic(topic, socket) when topic in ~w[user direct] do
+ defp internal_topic(topic, socket) when topic in ~w[user user:notification direct] do
"#{topic}:#{socket.assigns[:user].id}"
end
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/results.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex
index 8443d906b..8443d906b 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/results.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex
new file mode 100644
index 000000000..961aad976
--- /dev/null
+++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex
@@ -0,0 +1,2 @@
+<h1>Authorization exists</h1>
+<h2>Access token is <%= @token.token %></h2>
diff --git a/lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex
index ee84750c7..ee84750c7 100644
--- a/lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex
diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex
index a3facf017..7d3ef6b0d 100644
--- a/lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex
@@ -1,5 +1,5 @@
<h2>Password Reset for <%= @user.nickname %></h2>
-<%= form_for @conn, util_path(@conn, :password_reset), [as: "data"], fn f -> %>
+<%= form_for @conn, reset_password_path(@conn, :do_reset), [as: "data"], fn f -> %>
<div class="form-row">
<%= label f, :password, "Password" %>
<%= password_input f, :password %>
diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex
index df037c01e..df037c01e 100644
--- a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex
diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex
index f30ba3274..f30ba3274 100644
--- a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex
diff --git a/lib/pleroma/web/translation_helpers.ex b/lib/pleroma/web/translation_helpers.ex
new file mode 100644
index 000000000..8f5a43bf6
--- /dev/null
+++ b/lib/pleroma/web/translation_helpers.ex
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TranslationHelpers do
+ defmacro render_error(conn, status, msgid, bindings \\ Macro.escape(%{})) do
+ quote do
+ require Pleroma.Web.Gettext
+
+ unquote(conn)
+ |> Plug.Conn.put_status(unquote(status))
+ |> Phoenix.Controller.json(%{
+ error: Pleroma.Web.Gettext.dgettext("errors", unquote(msgid), unquote(bindings))
+ })
+ end
+ end
+end
diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
new file mode 100644
index 000000000..1941e6143
--- /dev/null
+++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.PasswordController do
+ @moduledoc """
+ The module containts functions for reset password.
+ """
+
+ use Pleroma.Web, :controller
+
+ require Logger
+
+ alias Pleroma.PasswordResetToken
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ def reset(conn, %{"token" => token}) do
+ with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
+ %User{} = user <- User.get_cached_by_id(token.user_id) do
+ render(conn, "reset.html", %{
+ token: token,
+ user: user
+ })
+ else
+ _e -> render(conn, "invalid_token.html")
+ end
+ end
+
+ def do_reset(conn, %{"data" => data}) do
+ with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
+ render(conn, "reset_success.html")
+ else
+ _e -> render(conn, "reset_failed.html")
+ end
+ end
+end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 489170d80..b1863528f 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -11,8 +11,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Activity
alias Pleroma.Emoji
alias Pleroma.Notification
- alias Pleroma.PasswordResetToken
- alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -20,26 +18,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web.OStatus
alias Pleroma.Web.WebFinger
- def show_password_reset(conn, %{"token" => token}) do
- with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
- %User{} = user <- User.get_cached_by_id(token.user_id) do
- render(conn, "password_reset.html", %{
- token: token,
- user: user
- })
- else
- _e -> render(conn, "invalid_token.html")
- end
- end
-
- def password_reset(conn, %{"data" => data}) do
- with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
- render(conn, "password_reset_success.html")
- else
- _e -> render(conn, "password_reset_failed.html")
- end
- end
-
def help_test(conn, _params) do
json(conn, "ok")
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 6cf107d17..45ef7be3d 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -456,6 +456,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
end
+ def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
+ change = Changeset.change(user, %{avatar: nil})
+ {:ok, user} = User.update_and_set_cache(change)
+ CommonAPI.update(user)
+
+ conn
+ |> put_view(UserView)
+ |> render("show.json", %{user: user, for: user})
+ end
+
def update_avatar(%{assigns: %{user: user}} = conn, params) do
{:ok, object} = ActivityPub.upload(params, type: :avatar)
change = Changeset.change(user, %{avatar: object.data})
@@ -467,6 +477,19 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|> render("show.json", %{user: user, for: user})
end
+ def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
+ with new_info <- %{"banner" => %{}},
+ info_cng <- User.Info.profile_update(user.info, new_info),
+ changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
+ {:ok, user} <- User.update_and_set_cache(changeset) do
+ CommonAPI.update(user)
+ response = %{url: nil} |> Jason.encode!()
+
+ conn
+ |> json_reply(200, response)
+ end
+ end
+
def update_banner(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
new_info <- %{"banner" => object.data},
@@ -482,6 +505,18 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
end
+ def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
+ with new_info <- %{"background" => %{}},
+ info_cng <- User.Info.profile_update(user.info, new_info),
+ changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
+ {:ok, _user} <- User.update_and_set_cache(changeset) do
+ response = %{url: nil} |> Jason.encode!()
+
+ conn
+ |> json_reply(200, response)
+ end
+ end
+
def update_background(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(params, type: :background),
new_info <- %{"background" => object.data},
diff --git a/lib/pleroma/web/twitter_api/views/password_view.ex b/lib/pleroma/web/twitter_api/views/password_view.ex
new file mode 100644
index 000000000..b166b925d
--- /dev/null
+++ b/lib/pleroma/web/twitter_api/views/password_view.ex
@@ -0,0 +1,8 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.PasswordView do
+ use Pleroma.Web, :view
+ import Phoenix.HTML.Form
+end
diff --git a/lib/pleroma/web/uploader_controller.ex b/lib/pleroma/web/uploader_controller.ex
index 5d8a77346..bf09775e6 100644
--- a/lib/pleroma/web/uploader_controller.ex
+++ b/lib/pleroma/web/uploader_controller.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.UploaderController do
use Pleroma.Web, :controller
@@ -8,7 +12,7 @@ defmodule Pleroma.Web.UploaderController do
end
def callbacks(conn, _) do
- send_resp(conn, 400, "bad request")
+ render_error(conn, :bad_request, "bad request")
end
defp process_callback(conn, pid, params) when is_pid(pid) do
@@ -20,6 +24,6 @@ defmodule Pleroma.Web.UploaderController do
end
defp process_callback(conn, _, _) do
- send_resp(conn, 400, "bad request")
+ render_error(conn, :bad_request, "bad request")
end
end
diff --git a/lib/pleroma/web/views/error_view.ex b/lib/pleroma/web/views/error_view.ex
index f4c04131c..5cb8669fe 100644
--- a/lib/pleroma/web/views/error_view.ex
+++ b/lib/pleroma/web/views/error_view.ex
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.ErrorView do
def render("500.json", assigns) do
Logger.error("Internal server error: #{inspect(assigns[:reason])}")
- if Mix.env() != :prod do
+ if Pleroma.Config.get(:env) != :prod do
%{errors: %{detail: "Internal server error", reason: inspect(assigns[:reason])}}
else
%{errors: %{detail: "Internal server error"}}
diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex
index 66813e4dd..b42f6887e 100644
--- a/lib/pleroma/web/web.ex
+++ b/lib/pleroma/web/web.ex
@@ -23,9 +23,11 @@ defmodule Pleroma.Web do
def controller do
quote do
use Phoenix.Controller, namespace: Pleroma.Web
+
import Plug.Conn
import Pleroma.Web.Gettext
import Pleroma.Web.Router.Helpers
+ import Pleroma.Web.TranslationHelpers
plug(:set_put_layout)