Merge remote-tracking branch 'remotes/origin/develop' into 1364-notifications-sending-control

This commit is contained in:
Ivan Tashkinov 2020-03-16 17:58:42 +03:00
commit 0e07c5ef57
33 changed files with 740 additions and 549 deletions

View file

@ -0,0 +1,20 @@
<!--
### Precheck
* For support use https://git.pleroma.social/pleroma/pleroma-support or [community channels](https://git.pleroma.social/pleroma/pleroma#community-channels).
* Please do a quick search to ensure no similar bug has been reported before. If the bug has not been addressed after 2 weeks, it's fine to bump it.
* Try to ensure that the bug is actually related to the Pleroma backend. For example, if a bug happens in Pleroma-FE but not in Mastodon-FE or mobile clients, it's likely that the bug should be filed in [Pleroma-FE](https://git.pleroma.social/pleroma/pleroma-fe/issues/new) repository.
-->
### Environment
* Installation type:
- [ ] OTP
- [ ] From source
* Pleroma version (could be found in the "Version" tab of settings in Pleroma-FE):
* Elixir version (`elixir -v` for from source installations, N/A for OTP):
* Operating system:
* PostgreSQL version (`postgres -V`):
### Bug description

View file

@ -0,0 +1,5 @@
### Release checklist
* [ ] Bump version in `mix.exs`
* [ ] Compile a changelog
* [ ] Create an MR with an announcement to pleroma.social
* [ ] Tag the release

View file

@ -61,8 +61,6 @@
config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
config :pleroma_job_queue, disabled: true
config :pleroma, Pleroma.ScheduledActivity, config :pleroma, Pleroma.ScheduledActivity,
daily_user_limit: 2, daily_user_limit: 2,
total_user_limit: 3, total_user_limit: 3,

View file

@ -1780,25 +1780,6 @@
} }
] ]
}, },
%{
group: :pleroma_job_queue,
key: :queues,
type: :group,
description: "[Deprecated] Replaced with `Oban`/`:queues` (keeping the same format)"
},
%{
group: :pleroma,
key: Pleroma.Web.Federator.RetryQueue,
type: :group,
description: "[Deprecated] See `Oban` and `:workers` sections for configuration notes",
children: [
%{
key: :max_retries,
type: :integer,
description: "[Deprecated] Replaced as `Oban`/`:queues`/`:outgoing_federation` value"
}
]
},
%{ %{
group: :pleroma, group: :pleroma,
key: Oban, key: Oban,
@ -2577,19 +2558,6 @@
} }
] ]
}, },
%{
group: :tesla,
type: :group,
description: "Tesla settings",
children: [
%{
key: :adapter,
type: :module,
description: "Tesla adapter",
suggestions: [Tesla.Adapter.Hackney]
}
]
},
%{ %{
group: :pleroma, group: :pleroma,
key: :chat, key: :chat,

View file

@ -180,7 +180,7 @@ Post here request with grant_type=refresh_token to obtain new access token. Retu
## Account Registration ## Account Registration
`POST /api/v1/accounts` `POST /api/v1/accounts`
Has theses additionnal parameters (which are the same as in Pleroma-API): Has theses additional parameters (which are the same as in Pleroma-API):
* `fullname`: optional * `fullname`: optional
* `bio`: optional * `bio`: optional
* `captcha_solution`: optional, contains provider-specific captcha solution, * `captcha_solution`: optional, contains provider-specific captcha solution,

View file

@ -1,5 +1,5 @@
# Pleroma Clients # Pleroma Clients
Note: Additionnal clients may be working but theses are officially supporting Pleroma. Note: Additional clients may be working but theses are officially supporting Pleroma.
Feel free to contact us to be added to this list! Feel free to contact us to be added to this list!
## Desktop ## Desktop

View file

@ -15,9 +15,24 @@ def call(%{assigns: %{user: %User{}}} = conn, _) do
conn conn
end end
def call(conn, _) do def call(conn, options) do
perform =
cond do
options[:if_func] -> options[:if_func].()
options[:unless_func] -> !options[:unless_func].()
true -> true
end
if perform do
fail(conn)
else
conn
end
end
def fail(conn) do
conn conn
|> render_error(:forbidden, "Invalid credentials.") |> render_error(:forbidden, "Invalid credentials.")
|> halt |> halt()
end end
end end

View file

@ -10,14 +10,20 @@ def init(options) do
end end
def call(conn, _opts) do def call(conn, _opts) do
if Pleroma.Config.get([:instance, :federating]) do if federating?() do
conn conn
else else
fail(conn)
end
end
def federating?, do: Pleroma.Config.get([:instance, :federating])
defp fail(conn) do
conn conn
|> put_status(404) |> put_status(404)
|> Phoenix.Controller.put_view(Pleroma.Web.ErrorView) |> Phoenix.Controller.put_view(Pleroma.Web.ErrorView)
|> Phoenix.Controller.render("404.json") |> Phoenix.Controller.render("404.json")
|> halt() |> halt()
end end
end
end end

View file

@ -16,6 +16,7 @@ defmodule Pleroma.User do
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
alias Pleroma.Delivery alias Pleroma.Delivery
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
alias Pleroma.HTML
alias Pleroma.Keys alias Pleroma.Keys
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
@ -2066,4 +2067,27 @@ def set_invisible(user, invisible) do
|> validate_required([:invisible]) |> validate_required([:invisible])
|> update_and_set_cache() |> update_and_set_cache()
end end
def sanitize_html(%User{} = user) do
sanitize_html(user, nil)
end
# User data that mastodon isn't filtering (treated as plaintext):
# - field name
# - display name
def sanitize_html(%User{} = user, filter) do
fields =
user
|> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{
"name" => name,
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
}
end)
user
|> Map.put(:bio, HTML.filter_tags(user.bio, filter))
|> Map.put(:fields, fields)
end
end end

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Delivery alias Pleroma.Delivery
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Object.Fetcher alias Pleroma.Object.Fetcher
alias Pleroma.Plugs.EnsureAuthenticatedPlug
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.InternalFetchActor
@ -18,23 +19,37 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Web.ActivityPub.UserView alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.FederatingPlug
alias Pleroma.Web.Federator alias Pleroma.Web.Federator
require Logger require Logger
action_fallback(:errors) action_fallback(:errors)
@federating_only_actions [:internal_fetch, :relay, :relay_following, :relay_followers]
plug(FederatingPlug when action in @federating_only_actions)
plug(
EnsureAuthenticatedPlug,
[unless_func: &FederatingPlug.federating?/0] when action not in @federating_only_actions
)
plug(
EnsureAuthenticatedPlug
when action in [:read_inbox, :update_outbox, :whoami, :upload_media, :following, :followers]
)
plug( plug(
Pleroma.Plugs.Cache, Pleroma.Plugs.Cache,
[query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2] [query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2]
when action in [:activity, :object] when action in [:activity, :object]
) )
plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay])
plug(:set_requester_reachable when action in [:inbox]) plug(:set_requester_reachable when action in [:inbox])
plug(:relay_active? when action in [:relay]) plug(:relay_active? when action in [:relay])
def relay_active?(conn, _) do defp relay_active?(conn, _) do
if Pleroma.Config.get([:instance, :allow_relay]) do if Pleroma.Config.get([:instance, :allow_relay]) do
conn conn
else else
@ -127,12 +142,14 @@ defp set_cache_ttl_for(conn, entity) do
end end
# GET /relay/following # GET /relay/following
def following(%{assigns: %{relay: true}} = conn, _params) do def relay_following(conn, _params) do
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
conn conn
|> put_resp_content_type("application/activity+json") |> put_resp_content_type("application/activity+json")
|> put_view(UserView) |> put_view(UserView)
|> render("following.json", %{user: Relay.get_actor()}) |> render("following.json", %{user: Relay.get_actor()})
end end
end
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
@ -164,12 +181,14 @@ def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) d
end end
# GET /relay/followers # GET /relay/followers
def followers(%{assigns: %{relay: true}} = conn, _params) do def relay_followers(conn, _params) do
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
conn conn
|> put_resp_content_type("application/activity+json") |> put_resp_content_type("application/activity+json")
|> put_view(UserView) |> put_view(UserView)
|> render("followers.json", %{user: Relay.get_actor()}) |> render("followers.json", %{user: Relay.get_actor()})
end end
end
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
@ -200,13 +219,16 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) d
end end
end end
def outbox(conn, %{"nickname" => nickname, "page" => page?} = params) def outbox(
%{assigns: %{user: for_user}} = conn,
%{"nickname" => nickname, "page" => page?} = params
)
when page? in [true, "true"] do when page? in [true, "true"] do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- User.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
activities = activities =
if params["max_id"] do if params["max_id"] do
ActivityPub.fetch_user_activities(user, nil, %{ ActivityPub.fetch_user_activities(user, for_user, %{
"max_id" => params["max_id"], "max_id" => params["max_id"],
# This is a hack because postgres generates inefficient queries when filtering by # This is a hack because postgres generates inefficient queries when filtering by
# 'Answer', poll votes will be hidden by the visibility filter in this case anyway # 'Answer', poll votes will be hidden by the visibility filter in this case anyway
@ -214,7 +236,7 @@ def outbox(conn, %{"nickname" => nickname, "page" => page?} = params)
"limit" => 10 "limit" => 10
}) })
else else
ActivityPub.fetch_user_activities(user, nil, %{ ActivityPub.fetch_user_activities(user, for_user, %{
"limit" => 10, "limit" => 10,
"include_poll_votes" => true "include_poll_votes" => true
}) })
@ -255,8 +277,16 @@ def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
json(conn, "ok") json(conn, "ok")
end end
# only accept relayed Creates # POST /relay/inbox -or- POST /internal/fetch/inbox
def inbox(conn, %{"type" => "Create"} = params) do def inbox(conn, params) do
if params["type"] == "Create" && FederatingPlug.federating?() do
post_inbox_relayed_create(conn, params)
else
post_inbox_fallback(conn, params)
end
end
defp post_inbox_relayed_create(conn, params) do
Logger.debug( Logger.debug(
"Signature missing or not from author, relayed Create message, fetching object from source" "Signature missing or not from author, relayed Create message, fetching object from source"
) )
@ -266,10 +296,11 @@ def inbox(conn, %{"type" => "Create"} = params) do
json(conn, "ok") json(conn, "ok")
end end
def inbox(conn, params) do defp post_inbox_fallback(conn, params) do
headers = Enum.into(conn.req_headers, %{}) headers = Enum.into(conn.req_headers, %{})
if String.contains?(headers["signature"], params["actor"]) do if headers["signature"] && params["actor"] &&
String.contains?(headers["signature"], params["actor"]) do
Logger.debug( Logger.debug(
"Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!" "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
) )
@ -277,7 +308,9 @@ def inbox(conn, params) do
Logger.debug(inspect(conn.req_headers)) Logger.debug(inspect(conn.req_headers))
end end
json(conn, dgettext("errors", "error")) conn
|> put_status(:bad_request)
|> json(dgettext("errors", "error"))
end end
defp represent_service_actor(%User{} = user, conn) do defp represent_service_actor(%User{} = user, conn) do
@ -311,10 +344,8 @@ def whoami(%{assigns: %{user: %User{} = user}} = conn, _params) do
|> render("user.json", %{user: user}) |> render("user.json", %{user: user})
end end
def whoami(_conn, _params), do: {:error, :not_found}
def read_inbox( def read_inbox(
%{assigns: %{user: %{nickname: nickname} = user}} = conn, %{assigns: %{user: %User{nickname: nickname} = user}} = conn,
%{"nickname" => nickname, "page" => page?} = params %{"nickname" => nickname, "page" => page?} = params
) )
when page? in [true, "true"] do when page? in [true, "true"] do
@ -337,7 +368,7 @@ def read_inbox(
}) })
end end
def read_inbox(%{assigns: %{user: %{nickname: nickname} = user}} = conn, %{ def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
"nickname" => nickname "nickname" => nickname
}) do }) do
with {:ok, user} <- User.ensure_keys_present(user) do with {:ok, user} <- User.ensure_keys_present(user) do
@ -348,15 +379,7 @@ def read_inbox(%{assigns: %{user: %{nickname: nickname} = user}} = conn, %{
end end
end end
def read_inbox(%{assigns: %{user: nil}} = conn, %{"nickname" => nickname}) do def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
err = dgettext("errors", "can't read inbox of %{nickname}", nickname: nickname)
conn
|> put_status(:forbidden)
|> json(err)
end
def read_inbox(%{assigns: %{user: %{nickname: as_nickname}}} = conn, %{
"nickname" => nickname "nickname" => nickname
}) do }) do
err = err =
@ -370,7 +393,7 @@ def read_inbox(%{assigns: %{user: %{nickname: as_nickname}}} = conn, %{
|> json(err) |> json(err)
end end
def handle_user_activity(user, %{"type" => "Create"} = params) do defp handle_user_activity(%User{} = user, %{"type" => "Create"} = params) do
object = object =
params["object"] params["object"]
|> Map.merge(Map.take(params, ["to", "cc"])) |> Map.merge(Map.take(params, ["to", "cc"]))
@ -386,7 +409,7 @@ def handle_user_activity(user, %{"type" => "Create"} = params) do
}) })
end end
def handle_user_activity(user, %{"type" => "Delete"} = params) do defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do
with %Object{} = object <- Object.normalize(params["object"]), with %Object{} = object <- Object.normalize(params["object"]),
true <- user.is_moderator || user.ap_id == object.data["actor"], true <- user.is_moderator || user.ap_id == object.data["actor"],
{:ok, delete} <- ActivityPub.delete(object) do {:ok, delete} <- ActivityPub.delete(object) do
@ -396,7 +419,7 @@ def handle_user_activity(user, %{"type" => "Delete"} = params) do
end end
end end
def handle_user_activity(user, %{"type" => "Like"} = params) do defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
with %Object{} = object <- Object.normalize(params["object"]), with %Object{} = object <- Object.normalize(params["object"]),
{:ok, activity, _object} <- ActivityPub.like(user, object) do {:ok, activity, _object} <- ActivityPub.like(user, object) do
{:ok, activity} {:ok, activity}
@ -405,7 +428,7 @@ def handle_user_activity(user, %{"type" => "Like"} = params) do
end end
end end
def handle_user_activity(_, _) do defp handle_user_activity(_, _) do
{:error, dgettext("errors", "Unhandled activity type")} {:error, dgettext("errors", "Unhandled activity type")}
end end
@ -434,7 +457,7 @@ def update_outbox(
end end
end end
def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = _) do def update_outbox(%{assigns: %{user: %User{} = user}} = conn, %{"nickname" => nickname}) do
err = err =
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}", dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
nickname: nickname, nickname: nickname,
@ -446,13 +469,13 @@ def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} =
|> json(err) |> json(err)
end end
def errors(conn, {:error, :not_found}) do defp errors(conn, {:error, :not_found}) do
conn conn
|> put_status(:not_found) |> put_status(:not_found)
|> json(dgettext("errors", "Not found")) |> json(dgettext("errors", "Not found"))
end end
def errors(conn, _e) do defp errors(conn, _e) do
conn conn
|> put_status(:internal_server_error) |> put_status(:internal_server_error)
|> json(dgettext("errors", "error")) |> json(dgettext("errors", "error"))
@ -492,7 +515,7 @@ defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
- HTTP Code: 201 Created - HTTP Code: 201 Created
- HTTP Body: ActivityPub object to be inserted into another's `attachment` field - HTTP Body: ActivityPub object to be inserted into another's `attachment` field
""" """
def upload_media(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
with {:ok, object} <- with {:ok, object} <-
ActivityPub.upload( ActivityPub.upload(
file, file,

View file

@ -73,6 +73,7 @@ def render("user.json", %{user: user}) do
{:ok, _, public_key} = Keys.keys_from_pem(user.keys) {:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key]) public_key = :public_key.pem_encode([public_key])
user = User.sanitize_html(user)
endpoints = render("endpoints.json", %{user: user}) endpoints = render("endpoints.json", %{user: user})
@ -81,12 +82,6 @@ def render("user.json", %{user: user}) do
fields = fields =
user user
|> User.fields() |> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{
"name" => Pleroma.HTML.strip_tags(name),
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
}
end)
|> Enum.map(&Map.put(&1, "type", "PropertyValue")) |> Enum.map(&Map.put(&1, "type", "PropertyValue"))
%{ %{

View file

@ -5,7 +5,6 @@
defmodule Pleroma.Web.AdminAPI.AccountView do defmodule Pleroma.Web.AdminAPI.AccountView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.HTML
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy
@ -26,7 +25,8 @@ def render("index.json", %{users: users}) do
def render("show.json", %{user: user}) do def render("show.json", %{user: user}) do
avatar = User.avatar_url(user) |> MediaProxy.url() avatar = User.avatar_url(user) |> MediaProxy.url()
display_name = HTML.strip_tags(user.name || user.nickname) display_name = Pleroma.HTML.strip_tags(user.name || user.nickname)
user = User.sanitize_html(user, FastSanitize.Sanitizer.StripTags)
%{ %{
"id" => user.id, "id" => user.id,

View file

@ -25,8 +25,13 @@ def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname
def feed_redirect(%{assigns: %{format: format}} = conn, _params) def feed_redirect(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do when format in ["json", "activity+json"] do
with %{halted: false} = conn <-
Pleroma.Plugs.EnsureAuthenticatedPlug.call(conn,
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
) do
ActivityPubController.call(conn, :user) ActivityPubController.call(conn, :user)
end end
end
def feed_redirect(conn, %{"nickname" => nickname}) do def feed_redirect(conn, %{"nickname" => nickname}) do
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do

View file

@ -5,7 +5,6 @@
defmodule Pleroma.Web.MastodonAPI.AccountView do defmodule Pleroma.Web.MastodonAPI.AccountView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.HTML
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.AccountView
@ -67,6 +66,7 @@ def render("relationships.json", %{user: user, targets: targets}) do
end end
defp do_render("show.json", %{user: user} = opts) do defp do_render("show.json", %{user: user} = opts) do
user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
display_name = user.name || user.nickname display_name = user.name || user.nickname
image = User.avatar_url(user) |> MediaProxy.url() image = User.avatar_url(user) |> MediaProxy.url()
@ -100,17 +100,6 @@ defp do_render("show.json", %{user: user} = opts) do
} }
end) end)
fields =
user
|> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{
"name" => name,
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
}
end)
bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
relationship = render("relationship.json", %{user: opts[:for], target: user}) relationship = render("relationship.json", %{user: opts[:for], target: user})
%{ %{
@ -123,17 +112,17 @@ defp do_render("show.json", %{user: user} = opts) do
followers_count: followers_count, followers_count: followers_count,
following_count: following_count, following_count: following_count,
statuses_count: user.note_count, statuses_count: user.note_count,
note: bio || "", note: user.bio || "",
url: User.profile_url(user), url: User.profile_url(user),
avatar: image, avatar: image,
avatar_static: image, avatar_static: image,
header: header, header: header,
header_static: header, header_static: header,
emojis: emojis, emojis: emojis,
fields: fields, fields: user.fields,
bot: bot, bot: bot,
source: %{ source: %{
note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")), note: Pleroma.HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
sensitive: false, sensitive: false,
fields: user.raw_fields, fields: user.raw_fields,
pleroma: %{ pleroma: %{

View file

@ -16,6 +16,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.Metadata.PlayerView alias Pleroma.Web.Metadata.PlayerView
alias Pleroma.Web.Router alias Pleroma.Web.Router
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
)
plug( plug(
RateLimiter, RateLimiter,
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity] [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
@ -135,13 +139,13 @@ def notice_player(conn, %{"id" => id}) do
end end
end end
def errors(conn, {:error, :not_found}) do defp errors(conn, {:error, :not_found}) do
render_error(conn, :not_found, "Not found") render_error(conn, :not_found, "Not found")
end end
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) defp errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
def errors(conn, _) do defp errors(conn, _) do
render_error(conn, :internal_server_error, "Something went wrong") render_error(conn, :internal_server_error, "Something went wrong")
end end
end end

View file

@ -541,6 +541,7 @@ defmodule Pleroma.Web.Router do
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe) get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
end end
# Server to Server (S2S) AP interactions
pipeline :activitypub do pipeline :activitypub do
plug(:accepts, ["activity+json", "json"]) plug(:accepts, ["activity+json", "json"])
plug(Pleroma.Web.Plugs.HTTPSignaturePlug) plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
@ -554,6 +555,7 @@ defmodule Pleroma.Web.Router do
get("/users/:nickname/outbox", ActivityPubController, :outbox) get("/users/:nickname/outbox", ActivityPubController, :outbox)
end end
# Client to Server (C2S) AP interactions
pipeline :activitypub_client do pipeline :activitypub_client do
plug(:accepts, ["activity+json", "json"]) plug(:accepts, ["activity+json", "json"])
plug(:fetch_session) plug(:fetch_session)
@ -597,8 +599,8 @@ defmodule Pleroma.Web.Router do
post("/inbox", ActivityPubController, :inbox) post("/inbox", ActivityPubController, :inbox)
end end
get("/following", ActivityPubController, :following, assigns: %{relay: true}) get("/following", ActivityPubController, :relay_following)
get("/followers", ActivityPubController, :followers, assigns: %{relay: true}) get("/followers", ActivityPubController, :relay_followers)
end end
scope "/internal/fetch", Pleroma.Web.ActivityPub do scope "/internal/fetch", Pleroma.Web.ActivityPub do

View file

@ -17,6 +17,10 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView) plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
plug(:assign_id) plug(:assign_id)
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
)
@page_keys ["max_id", "min_id", "limit", "since_id", "order"] @page_keys ["max_id", "min_id", "limit", "since_id", "order"]
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name), defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
@ -33,7 +37,7 @@ defp not_found(conn, message) do
|> render("error.html", %{message: message, meta: ""}) |> render("error.html", %{message: message, meta: ""})
end end
def get_counts(%Activity{} = activity) do defp get_counts(%Activity{} = activity) do
%Object{data: data} = Object.normalize(activity) %Object{data: data} = Object.normalize(activity)
%{ %{
@ -43,9 +47,9 @@ def get_counts(%Activity{} = activity) do
} }
end end
def represent(%Activity{} = activity), do: represent(activity, false) defp represent(%Activity{} = activity), do: represent(activity, false)
def represent(%Activity{object: %Object{data: data}} = activity, selected) do defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
{:ok, user} = User.get_or_fetch(activity.object.data["actor"]) {:ok, user} = User.get_or_fetch(activity.object.data["actor"])
link = link =
@ -54,10 +58,17 @@ def represent(%Activity{object: %Object{data: data}} = activity, selected) do
_ -> data["url"] || data["external_url"] || data["id"] _ -> data["url"] || data["external_url"] || data["id"]
end end
content =
if data["content"] do
Pleroma.HTML.filter_tags(data["content"])
else
nil
end
%{ %{
user: user, user: User.sanitize_html(user),
title: get_title(activity.object), title: get_title(activity.object),
content: data["content"] || nil, content: content,
attachment: data["attachment"], attachment: data["attachment"],
link: link, link: link,
published: data["published"], published: data["published"],
@ -109,7 +120,7 @@ def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
next_page_id = List.last(timeline) && List.last(timeline).id next_page_id = List.last(timeline) && List.last(timeline).id
render(conn, "profile.html", %{ render(conn, "profile.html", %{
user: user, user: User.sanitize_html(user),
timeline: timeline, timeline: timeline,
prev_page_id: prev_page_id, prev_page_id: prev_page_id,
next_page_id: next_page_id, next_page_id: next_page_id,
@ -147,17 +158,17 @@ def show(%{assigns: %{activity_id: _}} = conn, _params) do
end end
end end
def assign_id(%{path_info: ["notice", notice_id]} = conn, _opts), defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
do: assign(conn, :notice_id, notice_id) do: assign(conn, :notice_id, notice_id)
def assign_id(%{path_info: ["users", user_id]} = conn, _opts), defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
do: assign(conn, :username_or_id, user_id) do: assign(conn, :username_or_id, user_id)
def assign_id(%{path_info: ["objects", object_id]} = conn, _opts), defp assign_id(%{path_info: ["objects", object_id]} = conn, _opts),
do: assign(conn, :object_id, object_id) do: assign(conn, :object_id, object_id)
def assign_id(%{path_info: ["activities", activity_id]} = conn, _opts), defp assign_id(%{path_info: ["activities", activity_id]} = conn, _opts),
do: assign(conn, :activity_id, activity_id) do: assign(conn, :activity_id, activity_id)
def assign_id(conn, _opts), do: conn defp assign_id(conn, _opts), do: conn
end end

View file

@ -16,6 +16,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"] @status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
plug(Pleroma.Web.FederatingPlug)
# Note: follower can submit the form (with password auth) not being signed in (having no token) # Note: follower can submit the form (with password auth) not being signed in (having no token)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,

View file

@ -17,6 +17,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.WebFinger alias Pleroma.Web.WebFinger
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["follow", "write:follows"]} %{scopes: ["follow", "write:follows"]}

View file

@ -4,10 +4,10 @@
"base62": {:hex, :base62, "1.2.1", "4866763e08555a7b3917064e9eef9194c41667276c51b59de2bc42c6ea65f806", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "3b29948de2013d3f93aa898c884a9dff847e7aec75d9d6d8c1dc4c61c2716c42"}, "base62": {:hex, :base62, "1.2.1", "4866763e08555a7b3917064e9eef9194c41667276c51b59de2bc42c6ea65f806", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "3b29948de2013d3f93aa898c884a9dff847e7aec75d9d6d8c1dc4c61c2716c42"},
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
"bbcode": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/bbcode.git", "f2d267675e9a7e1ad1ea9beb4cc23382762b66c2", [ref: "v0.2.0"]}, "bbcode": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/bbcode.git", "f2d267675e9a7e1ad1ea9beb4cc23382762b66c2", [ref: "v0.2.0"]},
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, "bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
"benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "3ad58ae787e9c7c94dd7ceda3b587ec2c64604563e049b2a0e8baafae832addb"}, "benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "3ad58ae787e9c7c94dd7ceda3b587ec2c64604563e049b2a0e8baafae832addb"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"cachex": {:hex, :cachex, "3.2.0", "a596476c781b0646e6cb5cd9751af2e2974c3e0d5498a8cab71807618b74fe2f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"}, "cachex": {:hex, :cachex, "3.2.0", "a596476c781b0646e6cb5cd9751af2e2974c3e0d5498a8cab71807618b74fe2f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "aef93694067a43697ae0531727e097754a9e992a1e7946296f5969d6dd9ac986"},
"calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "738d0e17a93c2ccfe4ddc707bdc8e672e9074c8569498483feb1c4530fb91b2b"}, "calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "738d0e17a93c2ccfe4ddc707bdc8e672e9074c8569498483feb1c4530fb91b2b"},
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]}, "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},

View file

@ -8,24 +8,62 @@ defmodule Pleroma.Plugs.EnsureAuthenticatedPlugTest do
alias Pleroma.Plugs.EnsureAuthenticatedPlug alias Pleroma.Plugs.EnsureAuthenticatedPlug
alias Pleroma.User alias Pleroma.User
test "it halts if no user is assigned", %{conn: conn} do describe "without :if_func / :unless_func options" do
conn = test "it halts if user is NOT assigned", %{conn: conn} do
conn conn = EnsureAuthenticatedPlug.call(conn, %{})
|> EnsureAuthenticatedPlug.call(%{})
assert conn.status == 403 assert conn.status == 403
assert conn.halted == true assert conn.halted == true
end end
test "it continues if a user is assigned", %{conn: conn} do test "it continues if a user is assigned", %{conn: conn} do
conn = conn = assign(conn, :user, %User{})
conn ret_conn = EnsureAuthenticatedPlug.call(conn, %{})
|> assign(:user, %User{})
ret_conn =
conn
|> EnsureAuthenticatedPlug.call(%{})
assert ret_conn == conn assert ret_conn == conn
end end
end
describe "with :if_func / :unless_func options" do
setup do
%{
true_fn: fn -> true end,
false_fn: fn -> false end
}
end
test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do
conn = assign(conn, :user, %User{})
assert EnsureAuthenticatedPlug.call(conn, if_func: true_fn) == conn
assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn
assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn
assert EnsureAuthenticatedPlug.call(conn, unless_func: false_fn) == conn
end
test "it continues if a user is NOT assigned but :if_func evaluates to `false`",
%{conn: conn, false_fn: false_fn} do
assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn
end
test "it continues if a user is NOT assigned but :unless_func evaluates to `true`",
%{conn: conn, true_fn: true_fn} do
assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn
end
test "it halts if a user is NOT assigned and :if_func evaluates to `true`",
%{conn: conn, true_fn: true_fn} do
conn = EnsureAuthenticatedPlug.call(conn, if_func: true_fn)
assert conn.status == 403
assert conn.halted == true
end
test "it halts if a user is NOT assigned and :unless_func evaluates to `false`",
%{conn: conn, false_fn: false_fn} do
conn = EnsureAuthenticatedPlug.call(conn, unless_func: false_fn)
assert conn.status == 403
assert conn.halted == true
end
end
end end

View file

@ -38,7 +38,7 @@ test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do
assert conn.assigns[:user] == opts[:user] assert conn.assigns[:user] == opts[:user]
end end
test "with valid token(downcase) in url parameters, it assings the user", opts do test "with valid token(downcase) in url parameters, it assigns the user", opts do
conn = conn =
:get :get
|> build_conn("/?access_token=#{opts[:token]}") |> build_conn("/?access_token=#{opts[:token]}")

View file

@ -51,7 +51,7 @@ test "it restricts based on config values" do
Config.put([:rate_limit, limiter_name], {scale, limit}) Config.put([:rate_limit, limiter_name], {scale, limit})
plug_opts = RateLimiter.init(name: limiter_name) plug_opts = RateLimiter.init(name: limiter_name)
conn = conn(:get, "/") conn = build_conn(:get, "/")
for i <- 1..5 do for i <- 1..5 do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
@ -65,7 +65,7 @@ test "it restricts based on config values" do
Process.sleep(50) Process.sleep(50)
conn = conn(:get, "/") conn = build_conn(:get, "/")
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
@ -85,7 +85,7 @@ test "`bucket_name` option overrides default bucket name" do
base_bucket_name = "#{limiter_name}:group1" base_bucket_name = "#{limiter_name}:group1"
plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name) plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name)
conn = conn(:get, "/") conn = build_conn(:get, "/")
RateLimiter.call(conn, plug_opts) RateLimiter.call(conn, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts)
@ -99,9 +99,9 @@ test "`params` option allows different queries to be tracked independently" do
plug_opts = RateLimiter.init(name: limiter_name, params: ["id"]) plug_opts = RateLimiter.init(name: limiter_name, params: ["id"])
conn = conn(:get, "/?id=1") conn = build_conn(:get, "/?id=1")
conn = Plug.Conn.fetch_query_params(conn) conn = Plug.Conn.fetch_query_params(conn)
conn_2 = conn(:get, "/?id=2") conn_2 = build_conn(:get, "/?id=2")
RateLimiter.call(conn, plug_opts) RateLimiter.call(conn, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
@ -120,9 +120,9 @@ test "it supports combination of options modifying bucket name" do
id = "100" id = "100"
conn = conn(:get, "/?id=#{id}") conn = build_conn(:get, "/?id=#{id}")
conn = Plug.Conn.fetch_query_params(conn) conn = Plug.Conn.fetch_query_params(conn)
conn_2 = conn(:get, "/?id=#{101}") conn_2 = build_conn(:get, "/?id=#{101}")
RateLimiter.call(conn, plug_opts) RateLimiter.call(conn, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts)
@ -138,8 +138,8 @@ test "are restricted based on remote IP" do
plug_opts = RateLimiter.init(name: limiter_name) plug_opts = RateLimiter.init(name: limiter_name)
conn = %{conn(:get, "/") | remote_ip: {127, 0, 0, 2}} conn = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 2}}
conn_2 = %{conn(:get, "/") | remote_ip: {127, 0, 0, 3}} conn_2 = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 3}}
for i <- 1..5 do for i <- 1..5 do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
@ -179,7 +179,7 @@ test "can have limits separate from unauthenticated connections" do
plug_opts = RateLimiter.init(name: limiter_name) plug_opts = RateLimiter.init(name: limiter_name)
user = insert(:user) user = insert(:user)
conn = conn(:get, "/") |> assign(:user, user) conn = build_conn(:get, "/") |> assign(:user, user)
for i <- 1..5 do for i <- 1..5 do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
@ -201,10 +201,10 @@ test "different users are counted independently" do
plug_opts = RateLimiter.init(name: limiter_name) plug_opts = RateLimiter.init(name: limiter_name)
user = insert(:user) user = insert(:user)
conn = conn(:get, "/") |> assign(:user, user) conn = build_conn(:get, "/") |> assign(:user, user)
user_2 = insert(:user) user_2 = insert(:user)
conn_2 = conn(:get, "/") |> assign(:user, user_2) conn_2 = build_conn(:get, "/") |> assign(:user, user_2)
for i <- 1..5 do for i <- 1..5 do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
@ -230,8 +230,8 @@ test "doesn't crash due to a race condition when multiple requests are made at t
opts = RateLimiter.init(name: limiter_name) opts = RateLimiter.init(name: limiter_name)
conn = conn(:get, "/") conn = build_conn(:get, "/")
conn_2 = conn(:get, "/") conn_2 = build_conn(:get, "/")
%Task{pid: pid1} = %Task{pid: pid1} =
task1 = task1 =

View file

@ -26,6 +26,8 @@ defmodule Pleroma.Web.ConnCase do
use Pleroma.Tests.Helpers use Pleroma.Tests.Helpers
import Pleroma.Web.Router.Helpers import Pleroma.Web.Router.Helpers
alias Pleroma.Config
# The default endpoint for testing # The default endpoint for testing
@endpoint Pleroma.Web.Endpoint @endpoint Pleroma.Web.Endpoint
@ -48,6 +50,28 @@ defp oauth_access(scopes, opts \\ []) do
%{user: user, token: token, conn: conn} %{user: user, token: token, conn: conn}
end end
defp ensure_federating_or_authenticated(conn, url, user) do
initial_setting = Config.get([:instance, :federating])
on_exit(fn -> Config.put([:instance, :federating], initial_setting) end)
Config.put([:instance, :federating], false)
conn
|> get(url)
|> response(403)
conn
|> assign(:user, user)
|> get(url)
|> response(200)
Config.put([:instance, :federating], true)
conn
|> get(url)
|> response(200)
end
end end
end end

View file

@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Delivery alias Pleroma.Delivery
alias Pleroma.Instances alias Pleroma.Instances
alias Pleroma.Object alias Pleroma.Object
@ -25,9 +26,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
:ok :ok
end end
clear_config_all([:instance, :federating], clear_config([:instance, :federating]) do
do: Pleroma.Config.put([:instance, :federating], true) Config.put([:instance, :federating], true)
) end
describe "/relay" do describe "/relay" do
clear_config([:instance, :allow_relay]) clear_config([:instance, :allow_relay])
@ -42,12 +43,21 @@ test "with the relay active, it returns the relay user", %{conn: conn} do
end end
test "with the relay disabled, it returns 404", %{conn: conn} do test "with the relay disabled, it returns 404", %{conn: conn} do
Pleroma.Config.put([:instance, :allow_relay], false) Config.put([:instance, :allow_relay], false)
conn conn
|> get(activity_pub_path(conn, :relay)) |> get(activity_pub_path(conn, :relay))
|> json_response(404) |> json_response(404)
|> assert end
test "on non-federating instance, it returns 404", %{conn: conn} do
Config.put([:instance, :federating], false)
user = insert(:user)
conn
|> assign(:user, user)
|> get(activity_pub_path(conn, :relay))
|> json_response(404)
end end
end end
@ -60,6 +70,16 @@ test "it returns the internal fetch user", %{conn: conn} do
assert res["id"] =~ "/fetch" assert res["id"] =~ "/fetch"
end end
test "on non-federating instance, it returns 404", %{conn: conn} do
Config.put([:instance, :federating], false)
user = insert(:user)
conn
|> assign(:user, user)
|> get(activity_pub_path(conn, :internal_fetch))
|> json_response(404)
end
end end
describe "/users/:nickname" do describe "/users/:nickname" do
@ -123,9 +143,34 @@ test "it returns 404 for remote users", %{
assert json_response(conn, 404) assert json_response(conn, 404)
end end
test "it returns error when user is not found", %{conn: conn} do
response =
conn
|> put_req_header("accept", "application/json")
|> get("/users/jimm")
|> json_response(404)
assert response == "Not found"
end end
describe "/object/:uuid" do test "it requires authentication if instance is NOT federating", %{
conn: conn
} do
user = insert(:user)
conn =
put_req_header(
conn,
"accept",
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
)
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user)
end
end
describe "/objects/:uuid" do
test "it returns a json representation of the object with accept application/json", %{ test "it returns a json representation of the object with accept application/json", %{
conn: conn conn: conn
} do } do
@ -236,6 +281,18 @@ test "cached purged after object deletion", %{conn: conn} do
assert "Not found" == json_response(conn2, :not_found) assert "Not found" == json_response(conn2, :not_found)
end end
test "it requires authentication if instance is NOT federating", %{
conn: conn
} do
user = insert(:user)
note = insert(:note)
uuid = String.split(note.data["id"], "/") |> List.last()
conn = put_req_header(conn, "accept", "application/activity+json")
ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user)
end
end end
describe "/activities/:uuid" do describe "/activities/:uuid" do
@ -307,6 +364,18 @@ test "cached purged after activity deletion", %{conn: conn} do
assert "Not found" == json_response(conn2, :not_found) assert "Not found" == json_response(conn2, :not_found)
end end
test "it requires authentication if instance is NOT federating", %{
conn: conn
} do
user = insert(:user)
activity = insert(:note_activity)
uuid = String.split(activity.data["id"], "/") |> List.last()
conn = put_req_header(conn, "accept", "application/activity+json")
ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user)
end
end end
describe "/inbox" do describe "/inbox" do
@ -379,6 +448,34 @@ test "accept follow activity", %{conn: conn} do
:ok = Mix.Tasks.Pleroma.Relay.run(["list"]) :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
assert_receive {:mix_shell, :info, ["relay.mastodon.host"]} assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
end end
test "without valid signature, " <>
"it only accepts Create activities and requires enabled federation",
%{conn: conn} do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
conn = put_req_header(conn, "content-type", "application/activity+json")
Config.put([:instance, :federating], false)
conn
|> post("/inbox", data)
|> json_response(403)
conn
|> post("/inbox", non_create_data)
|> json_response(403)
Config.put([:instance, :federating], true)
ret_conn = post(conn, "/inbox", data)
assert "ok" == json_response(ret_conn, 200)
conn
|> post("/inbox", non_create_data)
|> json_response(400)
end
end end
describe "/users/:nickname/inbox" do describe "/users/:nickname/inbox" do
@ -517,22 +614,11 @@ test "it accepts messages from actors that are followed by the user", %{
test "it rejects reads from other users", %{conn: conn} do test "it rejects reads from other users", %{conn: conn} do
user = insert(:user) user = insert(:user)
otheruser = insert(:user) other_user = insert(:user)
conn =
conn
|> assign(:user, otheruser)
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/inbox")
assert json_response(conn, 403)
end
test "it doesn't crash without an authenticated user", %{conn: conn} do
user = insert(:user)
conn = conn =
conn conn
|> assign(:user, other_user)
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/inbox") |> get("/users/#{user.nickname}/inbox")
@ -613,14 +699,30 @@ test "it removes all follower collections but actor's", %{conn: conn} do
refute recipient.follower_address in activity.data["cc"] refute recipient.follower_address in activity.data["cc"]
refute recipient.follower_address in activity.data["to"] refute recipient.follower_address in activity.data["to"]
end end
test "it requires authentication", %{conn: conn} do
user = insert(:user)
conn = put_req_header(conn, "accept", "application/activity+json")
ret_conn = get(conn, "/users/#{user.nickname}/inbox")
assert json_response(ret_conn, 403)
ret_conn =
conn
|> assign(:user, user)
|> get("/users/#{user.nickname}/inbox")
assert json_response(ret_conn, 200)
end
end end
describe "/users/:nickname/outbox" do describe "GET /users/:nickname/outbox" do
test "it will not bomb when there is no activity", %{conn: conn} do test "it returns 200 even if there're no activities", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn =
conn conn
|> assign(:user, user)
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox") |> get("/users/#{user.nickname}/outbox")
@ -635,6 +737,7 @@ test "it returns a note activity in a collection", %{conn: conn} do
conn = conn =
conn conn
|> assign(:user, user)
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox?page=true") |> get("/users/#{user.nickname}/outbox?page=true")
@ -647,24 +750,38 @@ test "it returns an announce activity in a collection", %{conn: conn} do
conn = conn =
conn conn
|> assign(:user, user)
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox?page=true") |> get("/users/#{user.nickname}/outbox?page=true")
assert response(conn, 200) =~ announce_activity.data["object"] assert response(conn, 200) =~ announce_activity.data["object"]
end end
test "it rejects posts from other users", %{conn: conn} do test "it requires authentication if instance is NOT federating", %{
conn: conn
} do
user = insert(:user)
conn = put_req_header(conn, "accept", "application/activity+json")
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user)
end
end
describe "POST /users/:nickname/outbox" do
test "it rejects posts from other users / unauuthenticated users", %{conn: conn} do
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!() data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
user = insert(:user) user = insert(:user)
otheruser = insert(:user) other_user = insert(:user)
conn = put_req_header(conn, "content-type", "application/activity+json")
conn =
conn conn
|> assign(:user, otheruser)
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/outbox", data) |> post("/users/#{user.nickname}/outbox", data)
|> json_response(403)
assert json_response(conn, 403) conn
|> assign(:user, other_user)
|> post("/users/#{user.nickname}/outbox", data)
|> json_response(403)
end end
test "it inserts an incoming create activity into the database", %{conn: conn} do test "it inserts an incoming create activity into the database", %{conn: conn} do
@ -779,24 +896,42 @@ test "it returns relay followers", %{conn: conn} do
result = result =
conn conn
|> assign(:relay, true)
|> get("/relay/followers") |> get("/relay/followers")
|> json_response(200) |> json_response(200)
assert result["first"]["orderedItems"] == [user.ap_id] assert result["first"]["orderedItems"] == [user.ap_id]
end end
test "on non-federating instance, it returns 404", %{conn: conn} do
Config.put([:instance, :federating], false)
user = insert(:user)
conn
|> assign(:user, user)
|> get("/relay/followers")
|> json_response(404)
end
end end
describe "/relay/following" do describe "/relay/following" do
test "it returns relay following", %{conn: conn} do test "it returns relay following", %{conn: conn} do
result = result =
conn conn
|> assign(:relay, true)
|> get("/relay/following") |> get("/relay/following")
|> json_response(200) |> json_response(200)
assert result["first"]["orderedItems"] == [] assert result["first"]["orderedItems"] == []
end end
test "on non-federating instance, it returns 404", %{conn: conn} do
Config.put([:instance, :federating], false)
user = insert(:user)
conn
|> assign(:user, user)
|> get("/relay/following")
|> json_response(404)
end
end end
describe "/users/:nickname/followers" do describe "/users/:nickname/followers" do
@ -807,32 +942,36 @@ test "it returns the followers in a collection", %{conn: conn} do
result = result =
conn conn
|> assign(:user, user_two)
|> get("/users/#{user_two.nickname}/followers") |> get("/users/#{user_two.nickname}/followers")
|> json_response(200) |> json_response(200)
assert result["first"]["orderedItems"] == [user.ap_id] assert result["first"]["orderedItems"] == [user.ap_id]
end end
test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
user = insert(:user) user = insert(:user)
user_two = insert(:user, hide_followers: true) user_two = insert(:user, hide_followers: true)
User.follow(user, user_two) User.follow(user, user_two)
result = result =
conn conn
|> assign(:user, user)
|> get("/users/#{user_two.nickname}/followers") |> get("/users/#{user_two.nickname}/followers")
|> json_response(200) |> json_response(200)
assert is_binary(result["first"]) assert is_binary(result["first"])
end end
test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated", test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
%{conn: conn} do %{conn: conn} do
user = insert(:user, hide_followers: true) user = insert(:user)
other_user = insert(:user, hide_followers: true)
result = result =
conn conn
|> get("/users/#{user.nickname}/followers?page=1") |> assign(:user, user)
|> get("/users/#{other_user.nickname}/followers?page=1")
assert result.status == 403 assert result.status == 403
assert result.resp_body == "" assert result.resp_body == ""
@ -864,6 +1003,7 @@ test "it works for more than 10 users", %{conn: conn} do
result = result =
conn conn
|> assign(:user, user)
|> get("/users/#{user.nickname}/followers") |> get("/users/#{user.nickname}/followers")
|> json_response(200) |> json_response(200)
@ -873,12 +1013,21 @@ test "it works for more than 10 users", %{conn: conn} do
result = result =
conn conn
|> assign(:user, user)
|> get("/users/#{user.nickname}/followers?page=2") |> get("/users/#{user.nickname}/followers?page=2")
|> json_response(200) |> json_response(200)
assert length(result["orderedItems"]) == 5 assert length(result["orderedItems"]) == 5
assert result["totalItems"] == 15 assert result["totalItems"] == 15
end end
test "returns 403 if requester is not logged in", %{conn: conn} do
user = insert(:user)
conn
|> get("/users/#{user.nickname}/followers")
|> json_response(403)
end
end end
describe "/users/:nickname/following" do describe "/users/:nickname/following" do
@ -889,6 +1038,7 @@ test "it returns the following in a collection", %{conn: conn} do
result = result =
conn conn
|> assign(:user, user)
|> get("/users/#{user.nickname}/following") |> get("/users/#{user.nickname}/following")
|> json_response(200) |> json_response(200)
@ -896,25 +1046,28 @@ test "it returns the following in a collection", %{conn: conn} do
end end
test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
user = insert(:user, hide_follows: true) user = insert(:user)
user_two = insert(:user) user_two = insert(:user, hide_follows: true)
User.follow(user, user_two) User.follow(user, user_two)
result = result =
conn conn
|> get("/users/#{user.nickname}/following") |> assign(:user, user)
|> get("/users/#{user_two.nickname}/following")
|> json_response(200) |> json_response(200)
assert is_binary(result["first"]) assert is_binary(result["first"])
end end
test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated", test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
%{conn: conn} do %{conn: conn} do
user = insert(:user, hide_follows: true) user = insert(:user)
user_two = insert(:user, hide_follows: true)
result = result =
conn conn
|> get("/users/#{user.nickname}/following?page=1") |> assign(:user, user)
|> get("/users/#{user_two.nickname}/following?page=1")
assert result.status == 403 assert result.status == 403
assert result.resp_body == "" assert result.resp_body == ""
@ -947,6 +1100,7 @@ test "it works for more than 10 users", %{conn: conn} do
result = result =
conn conn
|> assign(:user, user)
|> get("/users/#{user.nickname}/following") |> get("/users/#{user.nickname}/following")
|> json_response(200) |> json_response(200)
@ -956,12 +1110,21 @@ test "it works for more than 10 users", %{conn: conn} do
result = result =
conn conn
|> assign(:user, user)
|> get("/users/#{user.nickname}/following?page=2") |> get("/users/#{user.nickname}/following?page=2")
|> json_response(200) |> json_response(200)
assert length(result["orderedItems"]) == 5 assert length(result["orderedItems"]) == 5
assert result["totalItems"] == 15 assert result["totalItems"] == 15
end end
test "returns 403 if requester is not logged in", %{conn: conn} do
user = insert(:user)
conn
|> get("/users/#{user.nickname}/following")
|> json_response(403)
end
end end
describe "delivery tracking" do describe "delivery tracking" do
@ -1046,8 +1209,8 @@ test "it tracks a signed activity fetch when the json is cached", %{conn: conn}
end end
end end
describe "Additionnal ActivityPub C2S endpoints" do describe "Additional ActivityPub C2S endpoints" do
test "/api/ap/whoami", %{conn: conn} do test "GET /api/ap/whoami", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn =
@ -1058,12 +1221,16 @@ test "/api/ap/whoami", %{conn: conn} do
user = User.get_cached_by_id(user.id) user = User.get_cached_by_id(user.id)
assert UserView.render("user.json", %{user: user}) == json_response(conn, 200) assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
conn
|> get("/api/ap/whoami")
|> json_response(403)
end end
clear_config([:media_proxy]) clear_config([:media_proxy])
clear_config([Pleroma.Upload]) clear_config([Pleroma.Upload])
test "uploadMedia", %{conn: conn} do test "POST /api/ap/upload_media", %{conn: conn} do
user = insert(:user) user = insert(:user)
desc = "Description of the image" desc = "Description of the image"
@ -1083,6 +1250,10 @@ test "uploadMedia", %{conn: conn} do
assert object["name"] == desc assert object["name"] == desc
assert object["type"] == "Document" assert object["type"] == "Document"
assert object["actor"] == user.ap_id assert object["actor"] == user.ap_id
conn
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|> json_response(403)
end end
end end
end end

View file

@ -23,6 +23,10 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
:ok :ok
end end
clear_config_all([:instance, :federating]) do
Pleroma.Config.put([:instance, :federating], true)
end
describe "gather_webfinger_links/1" do describe "gather_webfinger_links/1" do
test "it returns links" do test "it returns links" do
user = insert(:user) user = insert(:user)

View file

@ -8,13 +8,19 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
import Pleroma.Factory import Pleroma.Factory
import SweetXml import SweetXml
alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
clear_config([:instance, :federating]) do
Config.put([:instance, :federating], true)
end
describe "feed" do
clear_config([:feed]) clear_config([:feed])
test "gets a feed", %{conn: conn} do test "gets a feed", %{conn: conn} do
Pleroma.Config.put( Config.put(
[:feed, :post_title], [:feed, :post_title],
%{max_length: 10, omission: "..."} %{max_length: 10, omission: "..."}
) )
@ -27,7 +33,9 @@ test "gets a feed", %{conn: conn} do
"content" => "This is :moominmamma: note ", "content" => "This is :moominmamma: note ",
"attachment" => [ "attachment" => [
%{ %{
"url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}] "url" => [
%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}
]
} }
], ],
"inReplyTo" => activity.data["id"] "inReplyTo" => activity.data["id"]
@ -40,7 +48,10 @@ test "gets a feed", %{conn: conn} do
note2 = note2 =
insert(:note, insert(:note,
user: user, user: user,
data: %{"content" => "42 This is :moominmamma: note ", "inReplyTo" => activity.data["id"]} data: %{
"content" => "42 This is :moominmamma: note ",
"inReplyTo" => activity.data["id"]
}
) )
_note_activity2 = insert(:note_activity, note: note2) _note_activity2 = insert(:note_activity, note: note2)
@ -69,161 +80,11 @@ test "returns 404 for a missing feed", %{conn: conn} do
assert response(conn, 404) assert response(conn, 404)
end end
end
# Note: see ActivityPubControllerTest for JSON format tests
describe "feed_redirect" do describe "feed_redirect" do
test "undefined format. it redirects to feed", %{conn: conn} do test "with html format, it redirects to user feed", %{conn: conn} do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
response =
conn
|> put_req_header("accept", "application/xml")
|> get("/users/#{user.nickname}")
|> response(302)
assert response ==
"<html><body>You are being <a href=\"#{Pleroma.Web.base_url()}/users/#{
user.nickname
}/feed.atom\">redirected</a>.</body></html>"
end
test "undefined format. it returns error when user not found", %{conn: conn} do
response =
conn
|> put_req_header("accept", "application/xml")
|> get(user_feed_path(conn, :feed, "jimm"))
|> response(404)
assert response == ~S({"error":"Not found"})
end
test "activity+json format. it redirects on actual feed of user", %{conn: conn} do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
response =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}")
|> json_response(200)
assert response["endpoints"] == %{
"oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
"oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
"oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
"sharedInbox" => "#{Pleroma.Web.base_url()}/inbox",
"uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"
}
assert response["@context"] == [
"https://www.w3.org/ns/activitystreams",
"http://localhost:4001/schemas/litepub-0.1.jsonld",
%{"@language" => "und"}
]
assert Map.take(response, [
"followers",
"following",
"id",
"inbox",
"manuallyApprovesFollowers",
"name",
"outbox",
"preferredUsername",
"summary",
"tag",
"type",
"url"
]) == %{
"followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
"following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
"id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
"inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
"manuallyApprovesFollowers" => false,
"name" => user.name,
"outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
"preferredUsername" => user.nickname,
"summary" => user.bio,
"tag" => [],
"type" => "Person",
"url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
}
end
test "activity+json format. it returns error whe use not found", %{conn: conn} do
response =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/users/jimm")
|> json_response(404)
assert response == "Not found"
end
test "json format. it redirects on actual feed of user", %{conn: conn} do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
response =
conn
|> put_req_header("accept", "application/json")
|> get("/users/#{user.nickname}")
|> json_response(200)
assert response["endpoints"] == %{
"oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
"oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
"oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
"sharedInbox" => "#{Pleroma.Web.base_url()}/inbox",
"uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"
}
assert response["@context"] == [
"https://www.w3.org/ns/activitystreams",
"http://localhost:4001/schemas/litepub-0.1.jsonld",
%{"@language" => "und"}
]
assert Map.take(response, [
"followers",
"following",
"id",
"inbox",
"manuallyApprovesFollowers",
"name",
"outbox",
"preferredUsername",
"summary",
"tag",
"type",
"url"
]) == %{
"followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
"following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
"id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
"inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
"manuallyApprovesFollowers" => false,
"name" => user.name,
"outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
"preferredUsername" => user.nickname,
"summary" => user.bio,
"tag" => [],
"type" => "Person",
"url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
}
end
test "json format. it returns error whe use not found", %{conn: conn} do
response =
conn
|> put_req_header("accept", "application/json")
|> get("/users/jimm")
|> json_response(404)
assert response == "Not found"
end
test "html format. it redirects on actual feed of user", %{conn: conn} do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"]) user = User.get_cached_by_ap_id(note_activity.data["actor"])
@ -239,7 +100,7 @@ test "html format. it redirects on actual feed of user", %{conn: conn} do
).resp_body ).resp_body
end end
test "html format. it returns error when user not found", %{conn: conn} do test "with html format, it returns error when user is not found", %{conn: conn} do
response = response =
conn conn
|> get("/users/jimm") |> get("/users/jimm")
@ -247,5 +108,30 @@ test "html format. it returns error when user not found", %{conn: conn} do
assert response == %{"error" => "Not found"} assert response == %{"error" => "Not found"}
end end
test "with non-html / non-json format, it redirects to user feed in atom format", %{
conn: conn
} do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
conn =
conn
|> put_req_header("accept", "application/xml")
|> get("/users/#{user.nickname}")
assert conn.status == 302
assert redirected_to(conn) == "#{Pleroma.Web.base_url()}/users/#{user.nickname}/feed.atom"
end
test "with non-html / non-json format, it returns error when user is not found", %{conn: conn} do
response =
conn
|> put_req_header("accept", "application/xml")
|> get(user_feed_path(conn, :feed, "jimm"))
|> response(404)
assert response == ~S({"error":"Not found"})
end
end end
end end

View file

@ -52,9 +52,8 @@ test "redirects on valid url when filename invalidated", %{conn: conn} do
url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
invalid_url = String.replace(url, "test.png", "test-file.png") invalid_url = String.replace(url, "test.png", "test-file.png")
response = get(conn, invalid_url) response = get(conn, invalid_url)
html = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
assert response.status == 302 assert response.status == 302
assert response.resp_body == html assert redirected_to(response) == url
end end
test "it performs ReverseProxy.call when signature valid", %{conn: conn} do test "it performs ReverseProxy.call when signature valid", %{conn: conn} do

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
@ -16,22 +17,24 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
:ok :ok
end end
clear_config_all([:instance, :federating]) do clear_config([:instance, :federating]) do
Pleroma.Config.put([:instance, :federating], true) Config.put([:instance, :federating], true)
end
# Note: see ActivityPubControllerTest for JSON format tests
describe "GET /objects/:uuid (text/html)" do
setup %{conn: conn} do
conn = put_req_header(conn, "accept", "text/html")
%{conn: conn}
end end
describe "GET object/2" do
test "redirects to /notice/id for html format", %{conn: conn} do test "redirects to /notice/id for html format", %{conn: conn} do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
object = Object.normalize(note_activity) object = Object.normalize(note_activity)
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
url = "/objects/#{uuid}" url = "/objects/#{uuid}"
conn = conn = get(conn, url)
conn
|> put_req_header("accept", "text/html")
|> get(url)
assert redirected_to(conn) == "/notice/#{note_activity.id}" assert redirected_to(conn) == "/notice/#{note_activity.id}"
end end
@ -45,23 +48,25 @@ test "404s on private objects", %{conn: conn} do
|> response(404) |> response(404)
end end
test "404s on nonexisting objects", %{conn: conn} do test "404s on non-existing objects", %{conn: conn} do
conn conn
|> get("/objects/123") |> get("/objects/123")
|> response(404) |> response(404)
end end
end end
describe "GET activity/2" do # Note: see ActivityPubControllerTest for JSON format tests
describe "GET /activities/:uuid (text/html)" do
setup %{conn: conn} do
conn = put_req_header(conn, "accept", "text/html")
%{conn: conn}
end
test "redirects to /notice/id for html format", %{conn: conn} do test "redirects to /notice/id for html format", %{conn: conn} do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"])) [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
conn = conn = get(conn, "/activities/#{uuid}")
conn
|> put_req_header("accept", "text/html")
|> get("/activities/#{uuid}")
assert redirected_to(conn) == "/notice/#{note_activity.id}" assert redirected_to(conn) == "/notice/#{note_activity.id}"
end end
@ -79,19 +84,6 @@ test "404s on nonexistent activities", %{conn: conn} do
|> get("/activities/123") |> get("/activities/123")
|> response(404) |> response(404)
end end
test "gets an activity in AS2 format", %{conn: conn} do
note_activity = insert(:note_activity)
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
url = "/activities/#{uuid}"
conn =
conn
|> put_req_header("accept", "application/activity+json")
|> get(url)
assert json_response(conn, 200)
end
end end
describe "GET notice/2" do describe "GET notice/2" do
@ -170,7 +162,7 @@ test "404s a private notice", %{conn: conn} do
assert response(conn, 404) assert response(conn, 404)
end end
test "404s a nonexisting notice", %{conn: conn} do test "404s a non-existing notice", %{conn: conn} do
url = "/notice/123" url = "/notice/123"
conn = conn =
@ -179,10 +171,21 @@ test "404s a nonexisting notice", %{conn: conn} do
assert response(conn, 404) assert response(conn, 404)
end end
test "it requires authentication if instance is NOT federating", %{
conn: conn
} do
user = insert(:user)
note_activity = insert(:note_activity)
conn = put_req_header(conn, "accept", "text/html")
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user)
end
end end
describe "GET /notice/:id/embed_player" do describe "GET /notice/:id/embed_player" do
test "render embed player", %{conn: conn} do setup do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
object = Pleroma.Object.normalize(note_activity) object = Pleroma.Object.normalize(note_activity)
@ -204,9 +207,11 @@ test "render embed player", %{conn: conn} do
|> Ecto.Changeset.change(data: object_data) |> Ecto.Changeset.change(data: object_data)
|> Pleroma.Repo.update() |> Pleroma.Repo.update()
conn = %{note_activity: note_activity}
conn end
|> get("/notice/#{note_activity.id}/embed_player")
test "renders embed player", %{conn: conn, note_activity: note_activity} do
conn = get(conn, "/notice/#{note_activity.id}/embed_player")
assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"] assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"]
@ -272,9 +277,19 @@ test "404s when attachment isn't audio or video", %{conn: conn} do
|> Ecto.Changeset.change(data: object_data) |> Ecto.Changeset.change(data: object_data)
|> Pleroma.Repo.update() |> Pleroma.Repo.update()
assert conn conn
|> get("/notice/#{note_activity.id}/embed_player") |> get("/notice/#{note_activity.id}/embed_player")
|> response(404) |> response(404)
end end
test "it requires authentication if instance is NOT federating", %{
conn: conn,
note_activity: note_activity
} do
user = insert(:user)
conn = put_req_header(conn, "accept", "text/html")
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user)
end
end end
end end

View file

@ -1,56 +1,46 @@
defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
import Pleroma.Factory import Pleroma.Factory
clear_config_all([:static_fe, :enabled]) do clear_config_all([:static_fe, :enabled]) do
Pleroma.Config.put([:static_fe, :enabled], true) Config.put([:static_fe, :enabled], true)
end end
describe "user profile page" do clear_config([:instance, :federating]) do
test "just the profile as HTML", %{conn: conn} do Config.put([:instance, :federating], true)
end
setup %{conn: conn} do
conn = put_req_header(conn, "accept", "text/html")
user = insert(:user) user = insert(:user)
conn = %{conn: conn, user: user}
conn end
|> put_req_header("accept", "text/html")
|> get("/users/#{user.nickname}") describe "user profile html" do
test "just the profile as HTML", %{conn: conn, user: user} do
conn = get(conn, "/users/#{user.nickname}")
assert html_response(conn, 200) =~ user.nickname assert html_response(conn, 200) =~ user.nickname
end end
test "renders json unless there's an html accept header", %{conn: conn} do
user = insert(:user)
conn =
conn
|> put_req_header("accept", "application/json")
|> get("/users/#{user.nickname}")
assert json_response(conn, 200)
end
test "404 when user not found", %{conn: conn} do test "404 when user not found", %{conn: conn} do
conn = conn = get(conn, "/users/limpopo")
conn
|> put_req_header("accept", "text/html")
|> get("/users/limpopo")
assert html_response(conn, 404) =~ "not found" assert html_response(conn, 404) =~ "not found"
end end
test "profile does not include private messages", %{conn: conn} do test "profile does not include private messages", %{conn: conn, user: user} do
user = insert(:user)
CommonAPI.post(user, %{"status" => "public"}) CommonAPI.post(user, %{"status" => "public"})
CommonAPI.post(user, %{"status" => "private", "visibility" => "private"}) CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
conn = conn = get(conn, "/users/#{user.nickname}")
conn
|> put_req_header("accept", "text/html")
|> get("/users/#{user.nickname}")
html = html_response(conn, 200) html = html_response(conn, 200)
@ -58,14 +48,10 @@ test "profile does not include private messages", %{conn: conn} do
refute html =~ ">private<" refute html =~ ">private<"
end end
test "pagination", %{conn: conn} do test "pagination", %{conn: conn, user: user} do
user = insert(:user)
Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end) Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end)
conn = conn = get(conn, "/users/#{user.nickname}")
conn
|> put_req_header("accept", "text/html")
|> get("/users/#{user.nickname}")
html = html_response(conn, 200) html = html_response(conn, 200)
@ -75,15 +61,11 @@ test "pagination", %{conn: conn} do
refute html =~ ">test1<" refute html =~ ">test1<"
end end
test "pagination, page 2", %{conn: conn} do test "pagination, page 2", %{conn: conn, user: user} do
user = insert(:user)
activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end) activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end)
{:ok, a11} = Enum.at(activities, 11) {:ok, a11} = Enum.at(activities, 11)
conn = conn = get(conn, "/users/#{user.nickname}?max_id=#{a11.id}")
conn
|> put_req_header("accept", "text/html")
|> get("/users/#{user.nickname}?max_id=#{a11.id}")
html = html_response(conn, 200) html = html_response(conn, 200)
@ -92,17 +74,17 @@ test "pagination, page 2", %{conn: conn} do
refute html =~ ">test20<" refute html =~ ">test20<"
refute html =~ ">test29<" refute html =~ ">test29<"
end end
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user)
end
end end
describe "notice rendering" do describe "notice html" do
test "single notice page", %{conn: conn} do test "single notice page", %{conn: conn, user: user} do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "testing a thing!"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "testing a thing!"})
conn = conn = get(conn, "/notice/#{activity.id}")
conn
|> put_req_header("accept", "text/html")
|> get("/notice/#{activity.id}")
html = html_response(conn, 200) html = html_response(conn, 200)
assert html =~ "<header>" assert html =~ "<header>"
@ -110,79 +92,68 @@ test "single notice page", %{conn: conn} do
assert html =~ "testing a thing!" assert html =~ "testing a thing!"
end end
test "shows the whole thread", %{conn: conn} do test "filters HTML tags", %{conn: conn} do
user = insert(:user) user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "space: the final frontier"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "<script>alert('xss')</script>"})
CommonAPI.post(user, %{
"status" => "these are the voyages or something",
"in_reply_to_status_id" => activity.id
})
conn = conn =
conn conn
|> put_req_header("accept", "text/html") |> put_req_header("accept", "text/html")
|> get("/notice/#{activity.id}") |> get("/notice/#{activity.id}")
html = html_response(conn, 200)
assert html =~ ~s[&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;]
end
test "shows the whole thread", %{conn: conn, user: user} do
{:ok, activity} = CommonAPI.post(user, %{"status" => "space: the final frontier"})
CommonAPI.post(user, %{
"status" => "these are the voyages or something",
"in_reply_to_status_id" => activity.id
})
conn = get(conn, "/notice/#{activity.id}")
html = html_response(conn, 200) html = html_response(conn, 200)
assert html =~ "the final frontier" assert html =~ "the final frontier"
assert html =~ "voyages" assert html =~ "voyages"
end end
test "redirect by AP object ID", %{conn: conn} do test "redirect by AP object ID", %{conn: conn, user: user} do
user = insert(:user)
{:ok, %Activity{data: %{"object" => object_url}}} = {:ok, %Activity{data: %{"object" => object_url}}} =
CommonAPI.post(user, %{"status" => "beam me up"}) CommonAPI.post(user, %{"status" => "beam me up"})
conn = conn = get(conn, URI.parse(object_url).path)
conn
|> put_req_header("accept", "text/html")
|> get(URI.parse(object_url).path)
assert html_response(conn, 302) =~ "redirected" assert html_response(conn, 302) =~ "redirected"
end end
test "redirect by activity ID", %{conn: conn} do test "redirect by activity ID", %{conn: conn, user: user} do
user = insert(:user)
{:ok, %Activity{data: %{"id" => id}}} = {:ok, %Activity{data: %{"id" => id}}} =
CommonAPI.post(user, %{"status" => "I'm a doctor, not a devops!"}) CommonAPI.post(user, %{"status" => "I'm a doctor, not a devops!"})
conn = conn = get(conn, URI.parse(id).path)
conn
|> put_req_header("accept", "text/html")
|> get(URI.parse(id).path)
assert html_response(conn, 302) =~ "redirected" assert html_response(conn, 302) =~ "redirected"
end end
test "404 when notice not found", %{conn: conn} do test "404 when notice not found", %{conn: conn} do
conn = conn = get(conn, "/notice/88c9c317")
conn
|> put_req_header("accept", "text/html")
|> get("/notice/88c9c317")
assert html_response(conn, 404) =~ "not found" assert html_response(conn, 404) =~ "not found"
end end
test "404 for private status", %{conn: conn} do test "404 for private status", %{conn: conn, user: user} do
user = insert(:user)
{:ok, activity} = {:ok, activity} =
CommonAPI.post(user, %{"status" => "don't show me!", "visibility" => "private"}) CommonAPI.post(user, %{"status" => "don't show me!", "visibility" => "private"})
conn = conn = get(conn, "/notice/#{activity.id}")
conn
|> put_req_header("accept", "text/html")
|> get("/notice/#{activity.id}")
assert html_response(conn, 404) =~ "not found" assert html_response(conn, 404) =~ "not found"
end end
test "302 for remote cached status", %{conn: conn} do test "302 for remote cached status", %{conn: conn, user: user} do
user = insert(:user)
message = %{ message = %{
"@context" => "https://www.w3.org/ns/activitystreams", "@context" => "https://www.w3.org/ns/activitystreams",
"to" => user.follower_address, "to" => user.follower_address,
@ -199,12 +170,15 @@ test "302 for remote cached status", %{conn: conn} do
assert {:ok, activity} = Transmogrifier.handle_incoming(message) assert {:ok, activity} = Transmogrifier.handle_incoming(message)
conn = conn = get(conn, "/notice/#{activity.id}")
conn
|> put_req_header("accept", "text/html")
|> get("/notice/#{activity.id}")
assert html_response(conn, 302) =~ "redirected" assert html_response(conn, 302) =~ "redirected"
end end
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
{:ok, activity} = CommonAPI.post(user, %{"status" => "testing a thing!"})
ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user)
end
end end
end end

View file

@ -5,8 +5,10 @@
defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
import ExUnit.CaptureLog import ExUnit.CaptureLog
import Pleroma.Factory import Pleroma.Factory
@ -15,6 +17,10 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
:ok :ok
end end
clear_config_all([:instance, :federating]) do
Config.put([:instance, :federating], true)
end
clear_config([:instance]) clear_config([:instance])
clear_config([:frontend_configurations, :pleroma_fe]) clear_config([:frontend_configurations, :pleroma_fe])
clear_config([:user, :deny_follow_blocked]) clear_config([:user, :deny_follow_blocked])

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Config
alias Pleroma.Tests.ObanHelpers alias Pleroma.Tests.ObanHelpers
alias Pleroma.User alias Pleroma.User
@ -178,7 +179,7 @@ test "it updates notification privacy option", %{user: user, conn: conn} do
describe "GET /api/statusnet/config" do describe "GET /api/statusnet/config" do
test "it returns config in xml format", %{conn: conn} do test "it returns config in xml format", %{conn: conn} do
instance = Pleroma.Config.get(:instance) instance = Config.get(:instance)
response = response =
conn conn
@ -195,12 +196,12 @@ test "it returns config in xml format", %{conn: conn} do
end end
test "it returns config in json format", %{conn: conn} do test "it returns config in json format", %{conn: conn} do
instance = Pleroma.Config.get(:instance) instance = Config.get(:instance)
Pleroma.Config.put([:instance, :managed_config], true) Config.put([:instance, :managed_config], true)
Pleroma.Config.put([:instance, :registrations_open], false) Config.put([:instance, :registrations_open], false)
Pleroma.Config.put([:instance, :invites_enabled], true) Config.put([:instance, :invites_enabled], true)
Pleroma.Config.put([:instance, :public], false) Config.put([:instance, :public], false)
Pleroma.Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"}) Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"})
response = response =
conn conn
@ -234,7 +235,7 @@ test "it returns config in json format", %{conn: conn} do
end end
test "returns the state of safe_dm_mentions flag", %{conn: conn} do test "returns the state of safe_dm_mentions flag", %{conn: conn} do
Pleroma.Config.put([:instance, :safe_dm_mentions], true) Config.put([:instance, :safe_dm_mentions], true)
response = response =
conn conn
@ -243,7 +244,7 @@ test "returns the state of safe_dm_mentions flag", %{conn: conn} do
assert response["site"]["safeDMMentionsEnabled"] == "1" assert response["site"]["safeDMMentionsEnabled"] == "1"
Pleroma.Config.put([:instance, :safe_dm_mentions], false) Config.put([:instance, :safe_dm_mentions], false)
response = response =
conn conn
@ -254,8 +255,8 @@ test "returns the state of safe_dm_mentions flag", %{conn: conn} do
end end
test "it returns the managed config", %{conn: conn} do test "it returns the managed config", %{conn: conn} do
Pleroma.Config.put([:instance, :managed_config], false) Config.put([:instance, :managed_config], false)
Pleroma.Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"}) Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"})
response = response =
conn conn
@ -264,7 +265,7 @@ test "it returns the managed config", %{conn: conn} do
refute response["site"]["pleromafe"] refute response["site"]["pleromafe"]
Pleroma.Config.put([:instance, :managed_config], true) Config.put([:instance, :managed_config], true)
response = response =
conn conn
@ -287,7 +288,7 @@ test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} d
} }
] ]
Pleroma.Config.put(:frontend_configurations, config) Config.put(:frontend_configurations, config)
response = response =
conn conn
@ -320,7 +321,7 @@ test "returns json with custom emoji with tags", %{conn: conn} do
clear_config([:instance, :healthcheck]) clear_config([:instance, :healthcheck])
test "returns 503 when healthcheck disabled", %{conn: conn} do test "returns 503 when healthcheck disabled", %{conn: conn} do
Pleroma.Config.put([:instance, :healthcheck], false) Config.put([:instance, :healthcheck], false)
response = response =
conn conn
@ -331,7 +332,7 @@ test "returns 503 when healthcheck disabled", %{conn: conn} do
end end
test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do
Pleroma.Config.put([:instance, :healthcheck], true) Config.put([:instance, :healthcheck], true)
with_mock Pleroma.Healthcheck, with_mock Pleroma.Healthcheck,
system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do
@ -351,7 +352,7 @@ test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do
end end
test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do
Pleroma.Config.put([:instance, :healthcheck], true) Config.put([:instance, :healthcheck], true)
with_mock Pleroma.Healthcheck, with_mock Pleroma.Healthcheck,
system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do
@ -426,6 +427,10 @@ test "it returns version in json format", %{conn: conn} do
end end
describe "POST /main/ostatus - remote_subscribe/2" do describe "POST /main/ostatus - remote_subscribe/2" do
clear_config([:instance, :federating]) do
Config.put([:instance, :federating], true)
end
test "renders subscribe form", %{conn: conn} do test "renders subscribe form", %{conn: conn} do
user = insert(:user) user = insert(:user)