Browse Source

Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms

1570-levenshtein-distance-user-search
lain 4 years ago
parent
commit
ee35bb5ac2
64 changed files with 1593 additions and 701 deletions
  1. +3
    -1
      CHANGELOG.md
  2. +2
    -1
      config/config.exs
  3. +20
    -2
      config/description.exs
  4. +1
    -0
      docs/configuration/cheatsheet.md
  5. +4
    -4
      lib/mix/tasks/pleroma/instance.ex
  6. +4
    -2
      lib/pleroma/emails/new_users_digest_email.ex
  7. +3
    -0
      lib/pleroma/helpers/uri_helper.ex
  8. +7
    -2
      lib/pleroma/user.ex
  9. +0
    -30
      lib/pleroma/web/activity_pub/activity_pub.ex
  10. +5
    -4
      lib/pleroma/web/activity_pub/activity_pub_controller.ex
  11. +25
    -0
      lib/pleroma/web/activity_pub/builder.ex
  12. +12
    -1
      lib/pleroma/web/activity_pub/object_validator.ex
  13. +101
    -0
      lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
  14. +2
    -1
      lib/pleroma/web/activity_pub/pipeline.ex
  15. +5
    -4
      lib/pleroma/web/activity_pub/relay.ex
  16. +15
    -0
      lib/pleroma/web/activity_pub/side_effects.ex
  17. +2
    -16
      lib/pleroma/web/activity_pub/transmogrifier.ex
  18. +10
    -114
      lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
  19. +31
    -0
      lib/pleroma/web/admin_api/controllers/fallback_controller.ex
  20. +79
    -0
      lib/pleroma/web/admin_api/controllers/status_controller.ex
  21. +165
    -0
      lib/pleroma/web/api_spec/operations/admin/status_operation.ex
  22. +3
    -1
      lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
  23. +10
    -4
      lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
  24. +1
    -1
      lib/pleroma/web/api_spec/operations/status_operation.ex
  25. +12
    -11
      lib/pleroma/web/common_api/common_api.ex
  26. +11
    -4
      lib/pleroma/web/common_api/utils.ex
  27. +1
    -1
      lib/pleroma/web/mastodon_api/controllers/status_controller.ex
  28. +6
    -1
      lib/pleroma/web/mastodon_api/views/instance_view.ex
  29. +6
    -6
      lib/pleroma/web/ostatus/ostatus_controller.ex
  30. +2
    -0
      lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
  31. +2
    -2
      lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
  32. +8
    -4
      lib/pleroma/web/router.ex
  33. +1
    -0
      priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE
  34. +88
    -1
      test/fixtures/kroeg-announce-with-inline-actor.json
  35. +43
    -7
      test/fixtures/mastodon-note-object.json
  36. +3
    -3
      test/notification_test.exs
  37. +2
    -1
      test/tasks/instance_test.exs
  38. +4
    -1
      test/tasks/user_test.exs
  39. +14
    -2
      test/user_test.exs
  40. +60
    -1
      test/web/activity_pub/activity_pub_controller_test.exs
  41. +8
    -77
      test/web/activity_pub/activity_pub_test.exs
  42. +92
    -0
      test/web/activity_pub/object_validator_test.exs
  43. +44
    -0
      test/web/activity_pub/pipeline_test.exs
  44. +6
    -9
      test/web/activity_pub/relay_test.exs
  45. +58
    -1
      test/web/activity_pub/side_effects_test.exs
  46. +172
    -0
      test/web/activity_pub/transmogrifier/announce_handling_test.exs
  47. +61
    -170
      test/web/activity_pub/transmogrifier_test.exs
  48. +1
    -1
      test/web/activity_pub/utils_test.exs
  49. +1
    -1
      test/web/activity_pub/views/object_view_test.exs
  50. +19
    -166
      test/web/admin_api/controllers/admin_api_controller_test.exs
  51. +194
    -0
      test/web/admin_api/controllers/status_controller_test.exs
  52. +38
    -6
      test/web/common_api/common_api_test.exs
  53. +42
    -11
      test/web/common_api/common_api_utils_test.exs
  54. +3
    -3
      test/web/mastodon_api/controllers/account_controller_test.exs
  55. +4
    -4
      test/web/mastodon_api/controllers/notification_controller_test.exs
  56. +9
    -9
      test/web/mastodon_api/controllers/status_controller_test.exs
  57. +1
    -1
      test/web/mastodon_api/views/notification_view_test.exs
  58. +2
    -2
      test/web/mastodon_api/views/status_view_test.exs
  59. +45
    -0
      test/web/ostatus/ostatus_controller_test.exs
  60. +7
    -0
      test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
  61. +8
    -3
      test/web/pleroma_api/controllers/notification_controller_test.exs
  62. +1
    -1
      test/web/push/impl_test.exs
  63. +3
    -3
      test/web/streamer/streamer_test.exs
  64. +1
    -0
      test/workers/cron/new_users_digest_worker_test.exs

+ 3
- 1
CHANGELOG.md View File

@@ -15,12 +15,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- **Breaking:** removed `with_move` parameter from notifications timeline. - **Breaking:** removed `with_move` parameter from notifications timeline.


### Added ### Added
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
- Instance: Add `background_image` to configuration and `/api/v1/instance` - Instance: Add `background_image` to configuration and `/api/v1/instance`
- Instance: Extend `/api/v1/instance` with Pleroma-specific information. - Instance: Extend `/api/v1/instance` with Pleroma-specific information.
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list. - NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
- NodeInfo: `pleroma_emoji_reactions` to the `features` list. - NodeInfo: `pleroma_emoji_reactions` to the `features` list.
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses. - Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
- Configuration: Add `:database_config_whitelist` setting to whitelist settings which can be configured from AdminFE. - Configuration: Add `:database_config_whitelist` setting to whitelist settings which can be configured from AdminFE.
- Configuration: `filename_display_max_length` option to set filename truncate limit, if filename display enabled (0 = no limit).
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required. - New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
- Mix task to create trusted OAuth App. - Mix task to create trusted OAuth App.
- Notifications: Added `follow_request` notification type. - Notifications: Added `follow_request` notification type.
@@ -47,7 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).


### Fixed ### Fixed
- Healthcheck reporting the number of memory currently used, rather than allocated in total - Healthcheck reporting the number of memory currently used, rather than allocated in total
- `InsertSkeletonsForDeletedUsers` failing on some instances
- `InsertSkeletonsForDeletedUsers` failing on some instances


## [2.0.3] - 2020-05-02 ## [2.0.3] - 2020-05-02




+ 2
- 1
config/config.exs View File

@@ -71,7 +71,8 @@ config :pleroma, Pleroma.Upload,
follow_redirect: true, follow_redirect: true,
pool: :upload pool: :upload
] ]
]
],
filename_display_max_length: 30


config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads" config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"




+ 20
- 2
config/description.exs View File

@@ -119,6 +119,11 @@ config :pleroma, :config_description, [
] ]
} }
] ]
},
%{
key: :filename_display_max_length,
type: :integer,
description: "Set max length of a filename to display. 0 = no limit. Default: 30"
} }
] ]
}, },
@@ -969,6 +974,13 @@ config :pleroma, :config_description, [
] ]
} }
] ]
},
%{
key: :instance_thumbnail,
type: :string,
description:
"The instance thumbnail image. It will appear in [Pleroma Instances](http://distsn.org/pleroma-instances.html)",
suggestions: ["/instance/thumbnail.jpeg"]
} }
] ]
}, },
@@ -1112,7 +1124,7 @@ config :pleroma, :config_description, [
logoMask: true, logoMask: true,
minimalScopesMode: false, minimalScopesMode: false,
noAttachmentLinks: false, noAttachmentLinks: false,
nsfwCensorImage: "",
nsfwCensorImage: "/static/img/nsfw.74818f9.png",
postContentType: "text/plain", postContentType: "text/plain",
redirectRootLogin: "/main/friends", redirectRootLogin: "/main/friends",
redirectRootNoLogin: "/main/all", redirectRootNoLogin: "/main/all",
@@ -1226,7 +1238,7 @@ config :pleroma, :config_description, [
type: :string, type: :string,
description: description:
"URL of the image to use for hiding NSFW media attachments in the timeline.", "URL of the image to use for hiding NSFW media attachments in the timeline.",
suggestions: ["/static/img/nsfw.png"]
suggestions: ["/static/img/nsfw.74818f9.png"]
}, },
%{ %{
key: :postContentType, key: :postContentType,
@@ -1346,6 +1358,12 @@ config :pleroma, :config_description, [
suggestions: [ suggestions: [
:pleroma_fox_tan :pleroma_fox_tan
] ]
},
%{
key: :default_user_avatar,
type: :string,
description: "URL of the default user avatar.",
suggestions: ["/images/avi.png"]
} }
] ]
}, },


+ 1
- 0
docs/configuration/cheatsheet.md View File

@@ -498,6 +498,7 @@ the source code is here: https://github.com/koto-bank/kocaptcha. The default end
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host. * `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it. * `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation. * `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.


!!! warning !!! warning
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`. `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.


+ 4
- 4
lib/mix/tasks/pleroma/instance.ex View File

@@ -147,6 +147,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
"What directory should media uploads go in (when using the local uploader)?", "What directory should media uploads go in (when using the local uploader)?",
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads]) Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
) )
|> Path.expand()


static_dir = static_dir =
get_option( get_option(
@@ -155,6 +156,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
"What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?", "What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
Pleroma.Config.get([:instance, :static_dir]) Pleroma.Config.get([:instance, :static_dir])
) )
|> Path.expand()


Config.put([:instance, :static_dir], static_dir) Config.put([:instance, :static_dir], static_dir)


@@ -204,7 +206,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
shell_info("Writing the postgres script to #{psql_path}.") shell_info("Writing the postgres script to #{psql_path}.")
File.write(psql_path, result_psql) File.write(psql_path, result_psql)


write_robots_txt(indexable, template_dir)
write_robots_txt(static_dir, indexable, template_dir)


shell_info( shell_info(
"\n All files successfully written! Refer to the installation instructions for your platform for next steps." "\n All files successfully written! Refer to the installation instructions for your platform for next steps."
@@ -224,15 +226,13 @@ defmodule Mix.Tasks.Pleroma.Instance do
end end
end end


defp write_robots_txt(indexable, template_dir) do
defp write_robots_txt(static_dir, indexable, template_dir) do
robots_txt = robots_txt =
EEx.eval_file( EEx.eval_file(
template_dir <> "/robots_txt.eex", template_dir <> "/robots_txt.eex",
indexable: indexable indexable: indexable
) )


static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")

unless File.exists?(static_dir) do unless File.exists?(static_dir) do
File.mkdir_p!(static_dir) File.mkdir_p!(static_dir)
end end


+ 4
- 2
lib/pleroma/emails/new_users_digest_email.ex View File

@@ -14,8 +14,10 @@ defmodule Pleroma.Emails.NewUsersDigestEmail do
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling]) styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])


logo_url = logo_url =
Pleroma.Web.Endpoint.url() <>
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo])
Pleroma.Helpers.UriHelper.maybe_add_base(
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo]),
Pleroma.Web.Endpoint.url()
)


new() new()
|> to({to.name, to.email}) |> to({to.name, to.email})


+ 3
- 0
lib/pleroma/helpers/uri_helper.ex View File

@@ -24,4 +24,7 @@ defmodule Pleroma.Helpers.UriHelper do
params params
end end
end end

def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
def maybe_add_base(uri, _base), do: uri
end end

+ 7
- 2
lib/pleroma/user.ex View File

@@ -305,8 +305,13 @@ defmodule Pleroma.User do


def avatar_url(user, options \\ []) do def avatar_url(user, options \\ []) do
case user.avatar do case user.avatar do
%{"url" => [%{"href" => href} | _]} -> href
_ -> !options[:no_default] && "#{Web.base_url()}/images/avi.png"
%{"url" => [%{"href" => href} | _]} ->
href

_ ->
unless options[:no_default] do
Config.get([:assets, :default_user_avatar], "#{Web.base_url()}/images/avi.png")
end
end end
end end




+ 0
- 30
lib/pleroma/web/activity_pub/activity_pub.ex View File

@@ -363,36 +363,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
end end


@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
def announce(
%User{ap_id: _} = user,
%Object{data: %{"id" => _}} = object,
activity_id \\ nil,
local \\ true,
public \\ true
) do
with {:ok, result} <-
Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
result
end
end

defp do_announce(user, object, activity_id, local, public) do
with true <- is_announceable?(object, user, public),
object <- Object.get_by_id(object.id),
announce_data <- make_announce_data(user, object, activity_id, public),
{:ok, activity} <- insert(announce_data, local),
{:ok, object} <- add_announce_to_object(activity, object),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity, object}
else
false -> {:error, false}
{:error, error} -> Repo.rollback(error)
end
end

@spec follow(User.t(), User.t(), String.t() | nil, boolean()) :: @spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | {:error, any()} {:ok, Activity.t()} | {:error, any()}
def follow(follower, followed, activity_id \\ nil, local \\ true) do def follow(follower, followed, activity_id \\ nil, local \\ true) do


+ 5
- 4
lib/pleroma/web/activity_pub/activity_pub_controller.ex View File

@@ -21,6 +21,7 @@ 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.Endpoint
alias Pleroma.Web.FederatingPlug alias Pleroma.Web.FederatingPlug
alias Pleroma.Web.Federator alias Pleroma.Web.Federator


@@ -75,8 +76,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end end
end end


def object(conn, %{"uuid" => uuid}) do
with ap_id <- o_status_url(conn, :object, uuid),
def object(conn, _) do
with ap_id <- Endpoint.url() <> conn.request_path,
%Object{} = object <- Object.get_cached_by_ap_id(ap_id), %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
{_, true} <- {:public?, Visibility.is_public?(object)} do {_, true} <- {:public?, Visibility.is_public?(object)} do
conn conn
@@ -101,8 +102,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
conn conn
end end


def activity(conn, %{"uuid" => uuid}) do
with ap_id <- o_status_url(conn, :activity, uuid),
def activity(conn, _params) do
with ap_id <- Endpoint.url() <> conn.request_path,
%Activity{} = activity <- Activity.normalize(ap_id), %Activity{} = activity <- Activity.normalize(ap_id),
{_, true} <- {:public?, Visibility.is_public?(activity)} do {_, true} <- {:public?, Visibility.is_public?(activity)} do
conn conn


+ 25
- 0
lib/pleroma/web/activity_pub/builder.ex View File

@@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility


require Pleroma.Constants

@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()} @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
def emoji_react(actor, object, emoji) do def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do with {:ok, data, meta} <- object_action(actor, object) do
@@ -120,6 +122,29 @@ defmodule Pleroma.Web.ActivityPub.Builder do
end end
end end


def announce(actor, object, options \\ []) do
public? = Keyword.get(options, :public, false)
to = [actor.follower_address, object.data["actor"]]

to =
if public? do
[Pleroma.Constants.as_public() | to]
else
to
end

{:ok,
%{
"id" => Utils.generate_activity_id(),
"actor" => actor.ap_id,
"object" => object.data["id"],
"to" => to,
"context" => object.data["context"],
"type" => "Announce",
"published" => Utils.make_date()
}, []}
end

@spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()} @spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
defp object_action(actor, object) do defp object_action(actor, object) do
object_actor = User.get_cached_by_ap_id(object.data["actor"]) object_actor = User.get_cached_by_ap_id(object.data["actor"])


+ 12
- 1
lib/pleroma/web/activity_pub/object_validator.ex View File

@@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do


alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
@@ -84,6 +85,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
end end
end end


def validate(%{"type" => "Announce"} = object, meta) do
with {:ok, object} <-
object
|> AnnounceValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object |> Map.from_struct())
{:ok, object, meta}
end
end

def cast_and_apply(%{"type" => "ChatMessage"} = object) do def cast_and_apply(%{"type" => "ChatMessage"} = object) do
ChatMessageValidator.cast_and_apply(object) ChatMessageValidator.cast_and_apply(object)
end end
@@ -116,7 +127,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do


def fetch_actor_and_object(object) do def fetch_actor_and_object(object) do
fetch_actor(object) fetch_actor(object)
Object.normalize(object["object"])
Object.normalize(object["object"], true)
:ok :ok
end end
end end

+ 101
- 0
lib/pleroma/web/activity_pub/object_validators/announce_validator.ex View File

@@ -0,0 +1,101 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
use Ecto.Schema

alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility

import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations

require Pleroma.Constants

@primary_key false

embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:type, :string)
field(:object, Types.ObjectID)
field(:actor, Types.ObjectID)
field(:context, :string, autogenerate: {Utils, :generate_context_id, []})
field(:to, Types.Recipients, default: [])
field(:cc, Types.Recipients, default: [])
field(:published, Types.DateTime)
end

def cast_and_validate(data) do
data
|> cast_data()
|> validate_data()
end

def cast_data(data) do
%__MODULE__{}
|> changeset(data)
end

def changeset(struct, data) do
struct
|> cast(data, __schema__(:fields))
|> fix_after_cast()
end

def fix_after_cast(cng) do
cng
end

def validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Announce"])
|> validate_required([:id, :type, :object, :actor, :to, :cc])
|> validate_actor_presence()
|> validate_object_presence()
|> validate_existing_announce()
|> validate_announcable()
end

def validate_announcable(cng) do
with actor when is_binary(actor) <- get_field(cng, :actor),
object when is_binary(object) <- get_field(cng, :object),
%User{} = actor <- User.get_cached_by_ap_id(actor),
%Object{} = object <- Object.get_cached_by_ap_id(object),
false <- Visibility.is_public?(object) do
same_actor = object.data["actor"] == actor.ap_id
is_public = Pleroma.Constants.as_public() in (get_field(cng, :to) ++ get_field(cng, :cc))

cond do
same_actor && is_public ->
cng
|> add_error(:actor, "can not announce this object publicly")

!same_actor ->
cng
|> add_error(:actor, "can not announce this object")

true ->
cng
end
else
_ -> cng
end
end

def validate_existing_announce(cng) do
actor = get_field(cng, :actor)
object = get_field(cng, :object)

if actor && object && Utils.get_existing_announce(actor, %{data: %{"id" => object}}) do
cng
|> add_error(:actor, "already announced this object")
|> add_error(:object, "already announced by this actor")
else
cng
end
end
end

+ 2
- 1
lib/pleroma/web/activity_pub/pipeline.ex View File

@@ -4,6 +4,7 @@


defmodule Pleroma.Web.ActivityPub.Pipeline do defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
@@ -44,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do


defp maybe_federate(%Activity{} = activity, meta) do defp maybe_federate(%Activity{} = activity, meta) do
with {:ok, local} <- Keyword.fetch(meta, :local) do with {:ok, local} <- Keyword.fetch(meta, :local) do
do_not_federate = meta[:do_not_federate]
do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])


if !do_not_federate && local do if !do_not_federate && local do
Federator.publish(activity) Federator.publish(activity)


+ 5
- 4
lib/pleroma/web/activity_pub/relay.ex View File

@@ -4,9 +4,10 @@


defmodule Pleroma.Web.ActivityPub.Relay do defmodule Pleroma.Web.ActivityPub.Relay do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI
require Logger require Logger


@relay_nickname "relay" @relay_nickname "relay"
@@ -48,11 +49,11 @@ defmodule Pleroma.Web.ActivityPub.Relay do
end end
end end


@spec publish(any()) :: {:ok, Activity.t(), Object.t()} | {:error, any()}
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
def publish(%Activity{data: %{"type" => "Create"}} = activity) do def publish(%Activity{data: %{"type" => "Create"}} = activity) do
with %User{} = user <- get_actor(), with %User{} = user <- get_actor(),
%Object{} = object <- Object.normalize(activity) do
ActivityPub.announce(user, object, nil, true, false)
true <- Visibility.is_public?(activity) do
CommonAPI.repeat(activity.id, user)
else else
error -> format_error(error) error -> format_error(error)
end end


+ 15
- 0
lib/pleroma/web/activity_pub/side_effects.ex View File

@@ -42,6 +42,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end end
end end


# Tasks this handles:
# - Add announce to object
# - Set up notification
# - Stream out the announce
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
announced_object = Object.get_by_ap_id(object.data["object"])

Utils.add_announce_to_object(object, announced_object)

Notification.create_notifications(object)
ActivityPub.stream_out(object)

{:ok, object, meta}
end

def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do
with undone_object <- Activity.get_by_ap_id(undone_object), with undone_object <- Activity.get_by_ap_id(undone_object),
:ok <- handle_undoing(undone_object) do :ok <- handle_undoing(undone_object) do


+ 2
- 16
lib/pleroma/web/activity_pub/transmogrifier.ex View File

@@ -672,7 +672,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end


def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "EmojiReact"] do
def handle_incoming(%{"type" => type} = data, _options)
when type in ["Like", "EmojiReact", "Announce"] do
with :ok <- ObjectValidator.fetch_actor_and_object(data), with :ok <- ObjectValidator.fetch_actor_and_object(data),
{:ok, activity, _meta} <- {:ok, activity, _meta} <-
Pipeline.common_pipeline(data, local: false) do Pipeline.common_pipeline(data, local: false) do
@@ -683,21 +684,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end


def handle_incoming( def handle_incoming(
%{"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),
{:ok, object} <- get_embedded_obj_helper(object_id, actor),
public <- Visibility.is_public?(data),
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
{:ok, activity}
else
_e -> :error
end
end

def handle_incoming(
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} = %{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
data, data,
_options _options


lib/pleroma/web/admin_api/admin_api_controller.ex → lib/pleroma/web/admin_api/controllers/admin_api_controller.ex View File

@@ -98,13 +98,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["read:statuses"], admin: true} %{scopes: ["read:statuses"], admin: true}
when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show]
)

plug(
OAuthScopesPlug,
%{scopes: ["write:statuses"], admin: true}
when action in [:status_update, :status_delete]
when action in [:list_user_statuses, :list_instance_statuses]
) )


plug( plug(
@@ -136,7 +130,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
] ]
) )


action_fallback(:errors)
action_fallback(AdminAPI.FallbackController)


def user_delete(conn, %{"nickname" => nickname}) do def user_delete(conn, %{"nickname" => nickname}) do
user_delete(conn, %{"nicknames" => [nickname]}) user_delete(conn, %{"nicknames" => [nickname]})
@@ -597,16 +591,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
json_response(conn, :no_content, "") json_response(conn, :no_content, "")
else else
{:registrations_open, _} -> {:registrations_open, _} ->
errors(
conn,
{:error, "To send invites you need to set the `registrations_open` option to false."}
)
{:error, "To send invites you need to set the `registrations_open` option to false."}


{:invites_enabled, _} -> {:invites_enabled, _} ->
errors(
conn,
{:error, "To send invites you need to set the `invites_enabled` option to true."}
)
{:error, "To send invites you need to set the `invites_enabled` option to true."}
end end
end end


@@ -814,71 +802,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end end
end end


def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
godmode = params["godmode"] == "true" || params["godmode"] == true
local_only = params["local_only"] == "true" || params["local_only"] == true
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
{page, page_size} = page_params(params)

activities =
ActivityPub.fetch_statuses(nil, %{
"godmode" => godmode,
"local_only" => local_only,
"limit" => page_size,
"offset" => (page - 1) * page_size,
"exclude_reblogs" => !with_reblogs && "true"
})

conn
|> put_view(AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity})
end

def status_show(conn, %{"id" => id}) do
with %Activity{} = activity <- Activity.get_by_id(id) do
conn
|> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity})
else
_ -> errors(conn, {:error, :not_found})
end
end

def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
params =
params
|> Map.take(["sensitive", "visibility"])
|> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end)

with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
{:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive])

ModerationLog.insert_log(%{
action: "status_update",
actor: admin,
subject: activity,
sensitive: sensitive,
visibility: params[:visibility]
})

conn
|> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity})
end
end

def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
ModerationLog.insert_log(%{
action: "status_delete",
actor: user,
subject_id: id
})

json(conn, %{})
end
end

def list_log(conn, params) do def list_log(conn, params) do
{page, page_size} = page_params(params) {page, page_size} = page_params(params)


@@ -904,7 +827,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end end


def config_show(conn, %{"only_db" => true}) do def config_show(conn, %{"only_db" => true}) do
with :ok <- configurable_from_database(conn) do
with :ok <- configurable_from_database() do
configs = Pleroma.Repo.all(ConfigDB) configs = Pleroma.Repo.all(ConfigDB)


conn conn
@@ -914,7 +837,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end end


def config_show(conn, _params) do def config_show(conn, _params) do
with :ok <- configurable_from_database(conn) do
with :ok <- configurable_from_database() do
configs = ConfigDB.get_all_as_keyword() configs = ConfigDB.get_all_as_keyword()


merged = merged =
@@ -953,7 +876,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end end


def config_update(conn, %{"configs" => configs}) do def config_update(conn, %{"configs" => configs}) do
with :ok <- configurable_from_database(conn) do
with :ok <- configurable_from_database() do
{_errors, results} = {_errors, results} =
configs configs
|> Enum.filter(&whitelisted_config?/1) |> Enum.filter(&whitelisted_config?/1)
@@ -997,7 +920,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end end


def restart(conn, _params) do def restart(conn, _params) do
with :ok <- configurable_from_database(conn) do
with :ok <- configurable_from_database() do
Restarter.Pleroma.restart(Config.get(:env), 50) Restarter.Pleroma.restart(Config.get(:env), 50)


json(conn, %{}) json(conn, %{})
@@ -1008,14 +931,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()}) json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
end end


defp configurable_from_database(conn) do
defp configurable_from_database do
if Config.get(:configurable_from_database) do if Config.get(:configurable_from_database) do
:ok :ok
else else
errors(
conn,
{:error, "To use this endpoint you need to enable configuration from database."}
)
{:error, "To use this endpoint you need to enable configuration from database."}
end end
end end


@@ -1159,30 +1079,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(%{"status_visibility" => count}) |> json(%{"status_visibility" => count})
end end


defp errors(conn, {:error, :not_found}) do
conn
|> put_status(:not_found)
|> json(dgettext("errors", "Not found"))
end

defp errors(conn, {:error, reason}) do
conn
|> put_status(:bad_request)
|> json(reason)
end

defp errors(conn, {:param_cast, _}) do
conn
|> put_status(:bad_request)
|> json(dgettext("errors", "Invalid parameters"))
end

defp errors(conn, _) do
conn
|> put_status(:internal_server_error)
|> json(dgettext("errors", "Something went wrong"))
end

defp page_params(params) do defp page_params(params) do
{get_page(params["page"]), get_page_size(params["page_size"])} {get_page(params["page"]), get_page_size(params["page_size"])}
end end

+ 31
- 0
lib/pleroma/web/admin_api/controllers/fallback_controller.ex View File

@@ -0,0 +1,31 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.AdminAPI.FallbackController do
use Pleroma.Web, :controller

def call(conn, {:error, :not_found}) do
conn
|> put_status(:not_found)
|> json(%{error: dgettext("errors", "Not found")})
end

def call(conn, {:error, reason}) do
conn
|> put_status(:bad_request)
|> json(%{error: reason})
end

def call(conn, {:param_cast, _}) do
conn
|> put_status(:bad_request)
|> json(dgettext("errors", "Invalid parameters"))
end

def call(conn, _) do
conn
|> put_status(:internal_server_error)
|> json(%{error: dgettext("errors", "Something went wrong")})
end
end

+ 79
- 0
lib/pleroma/web/admin_api/controllers/status_controller.ex View File

@@ -0,0 +1,79 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.AdminAPI.StatusController do
use Pleroma.Web, :controller

alias Pleroma.Activity
alias Pleroma.ModerationLog
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI

require Logger

plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"], admin: true} when action in [:index, :show])

plug(
OAuthScopesPlug,
%{scopes: ["write:statuses"], admin: true} when action in [:update, :delete]
)

action_fallback(Pleroma.Web.AdminAPI.FallbackController)

defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.StatusOperation

def index(%{assigns: %{user: _admin}} = conn, params) do
activities =
ActivityPub.fetch_statuses(nil, %{
"godmode" => params.godmode,
"local_only" => params.local_only,
"limit" => params.page_size,
"offset" => (params.page - 1) * params.page_size,
"exclude_reblogs" => not params.with_reblogs
})

render(conn, "index.json", activities: activities, as: :activity)
end

def show(conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id(id) do
conn
|> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity})
else
nil -> {:error, :not_found}
end
end

def update(%{assigns: %{user: admin}, body_params: params} = conn, %{id: id}) do
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
ModerationLog.insert_log(%{
action: "status_update",
actor: admin,
subject: activity,
sensitive: params[:sensitive],
visibility: params[:visibility]
})

conn
|> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity})
end
end

def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
ModerationLog.insert_log(%{
action: "status_delete",
actor: user,
subject_id: id
})

json(conn, %{})
end
end
end

+ 165
- 0
lib/pleroma/web/api_spec/operations/admin/status_operation.ex View File

@@ -0,0 +1,165 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.Status
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope

import Pleroma.Web.ApiSpec.Helpers
import Pleroma.Web.ApiSpec.StatusOperation, only: [id_param: 0]

def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end

def index_operation do
%Operation{
tags: ["Admin", "Statuses"],
operationId: "AdminAPI.StatusController.index",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [
Operation.parameter(
:godmode,
:query,
%Schema{type: :boolean, default: false},
"Allows to see private statuses"
),
Operation.parameter(
:local_only,
:query,
%Schema{type: :boolean, default: false},
"Excludes remote statuses"
),
Operation.parameter(
:with_reblogs,
:query,
%Schema{type: :boolean, default: false},
"Allows to see reblogs"
),
Operation.parameter(
:page,
:query,
%Schema{type: :integer, default: 1},
"Page"
),
Operation.parameter(
:page_size,
:query,
%Schema{type: :integer, default: 50},
"Number of statuses to return"
)
],
responses: %{
200 =>
Operation.response("Array of statuses", "application/json", %Schema{
type: :array,
items: status()
})
}
}
end

def show_operation do
%Operation{
tags: ["Admin", "Statuses"],
summary: "Show Status",
operationId: "AdminAPI.StatusController.show",
parameters: [id_param()],
security: [%{"oAuth" => ["read:statuses"]}],
responses: %{
200 => Operation.response("Status", "application/json", Status),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end

def update_operation do
%Operation{
tags: ["Admin", "Statuses"],
summary: "Change the scope of an individual reported status",
operationId: "AdminAPI.StatusController.update",
parameters: [id_param()],
security: [%{"oAuth" => ["write:statuses"]}],
requestBody: request_body("Parameters", update_request(), required: true),
responses: %{
200 => Operation.response("Status", "application/json", Status),
400 => Operation.response("Error", "application/json", ApiError)
}
}
end

def delete_operation do
%Operation{
tags: ["Admin", "Statuses"],
summary: "Delete an individual reported status",
operationId: "AdminAPI.StatusController.delete",
parameters: [id_param()],
security: [%{"oAuth" => ["write:statuses"]}],
responses: %{
200 => empty_object_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end

defp status do
%Schema{
anyOf: [
Status,
%Schema{
type: :object,
properties: %{
account: %Schema{allOf: [Account, admin_account()]}
}
}
]
}
end

defp admin_account do
%Schema{
type: :object,
properties: %{
id: FlakeID,
avatar: %Schema{type: :string},
nickname: %Schema{type: :string},
display_name: %Schema{type: :string},
deactivated: %Schema{type: :boolean},
local: %Schema{type: :boolean},
roles: %Schema{
type: :object,
properties: %{
admin: %Schema{type: :boolean},
moderator: %Schema{type: :boolean}
}
},
tags: %Schema{type: :string},
confirmation_pending: %Schema{type: :string}
}
}
end

defp update_request do
%Schema{
type: :object,
properties: %{
sensitive: %Schema{
type: :boolean,
description: "Mark status and attached media as sensitive?"
},
visibility: VisibilityScope
},
example: %{
"visibility" => "private",
"sensitive" => "false"
}
}
end
end

+ 3
- 1
lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex View File

@@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
alias OpenApiSpex.Operation alias OpenApiSpex.Operation
alias OpenApiSpex.Schema alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.Status alias Pleroma.Web.ApiSpec.Schemas.Status


@@ -46,7 +47,8 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
security: [%{"oAuth" => ["write:statuses"]}], security: [%{"oAuth" => ["write:statuses"]}],
operationId: "EmojiReactionController.create", operationId: "EmojiReactionController.create",
responses: %{ responses: %{
200 => Operation.response("Status", "application/json", Status)
200 => Operation.response("Status", "application/json", Status),
400 => Operation.response("Bad Request", "application/json", ApiError)
} }
} }
end end


+ 10
- 4
lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex View File

@@ -8,6 +8,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do
alias Pleroma.Web.ApiSpec.NotificationOperation alias Pleroma.Web.ApiSpec.NotificationOperation
alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.ApiError


import Pleroma.Web.ApiSpec.Helpers

def open_api_operation(action) do def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation") operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, []) apply(__MODULE__, operation, [])
@@ -17,10 +19,14 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do
%Operation{ %Operation{
tags: ["Notifications"], tags: ["Notifications"],
summary: "Mark notifications as read. Query parameters are mutually exclusive.", summary: "Mark notifications as read. Query parameters are mutually exclusive.",
parameters: [
Operation.parameter(:id, :query, :string, "A single notification ID to read"),
Operation.parameter(:max_id, :query, :string, "Read all notifications up to this id")
],
requestBody:
request_body("Parameters", %Schema{
type: :object,
properties: %{
id: %Schema{type: :integer, description: "A single notification ID to read"},
max_id: %Schema{type: :integer, description: "Read all notifications up to this ID"}
}
}),
security: [%{"oAuth" => ["write:notifications"]}], security: [%{"oAuth" => ["write:notifications"]}],
operationId: "PleromaAPI.NotificationController.mark_as_read", operationId: "PleromaAPI.NotificationController.mark_as_read",
responses: %{ responses: %{


+ 1
- 1
lib/pleroma/web/api_spec/operations/status_operation.ex View File

@@ -487,7 +487,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
} }
end end


defp id_param do
def id_param do
Operation.parameter(:id, :path, FlakeID, "Status ID", Operation.parameter(:id, :path, FlakeID, "Status ID",
example: "9umDrYheeY451cQnEe", example: "9umDrYheeY451cQnEe",
required: true required: true


+ 12
- 11
lib/pleroma/web/common_api/common_api.ex View File

@@ -167,18 +167,19 @@ defmodule Pleroma.Web.CommonAPI do
end end


def repeat(id, user, params \\ %{}) do def repeat(id, user, params \\ %{}) do
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do
object = Object.normalize(activity)
announce_activity = Utils.get_existing_announce(user.ap_id, object)
public = public_announce?(object, params)

if announce_activity do
{:ok, announce_activity, object}
else
ActivityPub.announce(user, object, nil, true, public)
end
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
object = %Object{} <- Object.normalize(activity, false),
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
public = public_announce?(object, params),
{:ok, announce, _} <- Builder.announce(user, object, public: public),
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
{:ok, activity}
else else
_ -> {:error, :not_found}
{:existing_announce, %Activity{} = announce} ->
{:ok, announce}

_ ->
{:error, :not_found}
end end
end end




+ 11
- 4
lib/pleroma/web/common_api/utils.ex View File

@@ -102,7 +102,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end end


def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct", _) do def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct", _) do
if inReplyTo do
# If the OP is a DM already, add the implicit actor.
if inReplyTo && Visibility.is_direct?(inReplyTo) do
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []} {Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
else else
{mentioned_users, []} {mentioned_users, []}
@@ -395,10 +396,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def to_masto_date(_), do: "" def to_masto_date(_), do: ""


defp shortname(name) do defp shortname(name) do
if String.length(name) < 30 do
name
with max_length when max_length > 0 <-
Config.get([Pleroma.Upload, :filename_display_max_length], 30),
true <- String.length(name) > max_length do
String.slice(name, 0..max_length) <> "…"
else else
String.slice(name, 0..30) <> "…"
_ -> name
end end
end end


@@ -467,6 +470,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> Enum.map(& &1.ap_id) |> Enum.map(& &1.ap_id)


recipients ++ subscriber_ids recipients ++ subscriber_ids
else
_e -> recipients
end end
end end


@@ -478,6 +483,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> User.get_followers() |> User.get_followers()
|> Enum.map(& &1.ap_id) |> Enum.map(& &1.ap_id)
|> Enum.concat(recipients) |> Enum.concat(recipients)
else
_e -> recipients
end end
end end




+ 1
- 1
lib/pleroma/web/mastodon_api/controllers/status_controller.ex View File

@@ -210,7 +210,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do


@doc "POST /api/v1/statuses/:id/reblog" @doc "POST /api/v1/statuses/:id/reblog"
def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params),
with {:ok, announce} <- CommonAPI.repeat(ap_id_or_id, user, params),
%Activity{} = announce <- Activity.normalize(announce.data) do %Activity{} = announce <- Activity.normalize(announce.data) do
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity}) try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
end end


+ 6
- 1
lib/pleroma/web/mastodon_api/views/instance_view.ex View File

@@ -23,7 +23,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
streaming_api: Pleroma.Web.Endpoint.websocket_url() streaming_api: Pleroma.Web.Endpoint.websocket_url()
}, },
stats: Pleroma.Stats.get_stats(), stats: Pleroma.Stats.get_stats(),
thumbnail: Pleroma.Web.base_url() <> "/instance/thumbnail.jpeg",
thumbnail: instance_thumbnail(),
languages: ["en"], languages: ["en"],
registrations: Keyword.get(instance, :registrations_open), registrations: Keyword.get(instance, :registrations_open),
# Extra (not present in Mastodon): # Extra (not present in Mastodon):
@@ -88,4 +88,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
end end
|> Map.put(:enabled, Config.get([:instance, :federating])) |> Map.put(:enabled, Config.get([:instance, :federating]))
end end

defp instance_thumbnail do
Pleroma.Config.get([:instance, :instance_thumbnail]) ||
"#{Pleroma.Web.base_url()}/instance/thumbnail.jpeg"
end
end end

+ 6
- 6
lib/pleroma/web/ostatus/ostatus_controller.ex View File

@@ -32,13 +32,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do


action_fallback(:errors) action_fallback(:errors)


def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
def object(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do when format in ["json", "activity+json"] do
ActivityPubController.call(conn, :object) ActivityPubController.call(conn, :object)
end end


def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
with id <- o_status_url(conn, :object, uuid),
def object(%{assigns: %{format: format}} = conn, _params) do
with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <- {_, %Activity{} = activity} <-
{:activity, Activity.get_create_by_object_ap_id_with_object(id)}, {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do {_, true} <- {:public?, Visibility.is_public?(activity)} do
@@ -54,13 +54,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
end end
end end


def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
def activity(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do when format in ["json", "activity+json"] do
ActivityPubController.call(conn, :activity) ActivityPubController.call(conn, :activity)
end end


def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
with id <- o_status_url(conn, :activity, uuid),
def activity(%{assigns: %{format: format}} = conn, _params) do
with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do {_, true} <- {:public?, Visibility.is_public?(activity)} do
case format do case format do


+ 2
- 0
lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex View File

@@ -22,6 +22,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do


defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.EmojiReactionOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.EmojiReactionOperation


action_fallback(Pleroma.Web.MastodonAPI.FallbackController)

def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
%Object{data: %{"reactions" => reactions}} when is_list(reactions) <- %Object{data: %{"reactions" => reactions}} when is_list(reactions) <-


+ 2
- 2
lib/pleroma/web/pleroma_api/controllers/notification_controller.ex View File

@@ -14,7 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do


defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaNotificationOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaNotificationOperation


def mark_as_read(%{assigns: %{user: user}} = conn, %{id: notification_id}) do
def mark_as_read(%{assigns: %{user: user}, body_params: %{id: notification_id}} = conn, _) do
with {:ok, notification} <- Notification.read_one(user, notification_id) do with {:ok, notification} <- Notification.read_one(user, notification_id) do
render(conn, "show.json", notification: notification, for: user) render(conn, "show.json", notification: notification, for: user)
else else
@@ -25,7 +25,7 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do
end end
end end


def mark_as_read(%{assigns: %{user: user}} = conn, %{max_id: max_id}) do
def mark_as_read(%{assigns: %{user: user}, body_params: %{max_id: max_id}} = conn, _) do
notifications = notifications =
user user
|> Notification.set_read_up_to(max_id) |> Notification.set_read_up_to(max_id)


+ 8
- 4
lib/pleroma/web/router.ex View File

@@ -189,10 +189,10 @@ defmodule Pleroma.Web.Router do
post("/reports/:id/notes", AdminAPIController, :report_notes_create) post("/reports/:id/notes", AdminAPIController, :report_notes_create)
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete) delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)


get("/statuses/:id", AdminAPIController, :status_show)
put("/statuses/:id", AdminAPIController, :status_update)
delete("/statuses/:id", AdminAPIController, :status_delete)
get("/statuses", AdminAPIController, :list_statuses)
get("/statuses/:id", StatusController, :show)
put("/statuses/:id", StatusController, :update)
delete("/statuses/:id", StatusController, :delete)
get("/statuses", StatusController, :index)


get("/config", AdminAPIController, :config_show) get("/config", AdminAPIController, :config_show)
post("/config", AdminAPIController, :config_update) post("/config", AdminAPIController, :config_update)
@@ -564,6 +564,10 @@ defmodule Pleroma.Web.Router do
get("/notice/:id", OStatus.OStatusController, :notice) get("/notice/:id", OStatus.OStatusController, :notice)
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)


# Mastodon compatibility routes
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)

get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed) get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed) get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)




+ 1
- 0
priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE View File

@@ -0,0 +1 @@
If you are an instance admin and you want to modify the instace static files, this is probably not the right place to do it. This directory is checked in version control, so don't be surprised if you get merge conflicts after modifying anything here. Please use instance static directory instead, it has the same directory structure and files placed there will override files placed here. See https://docs.pleroma.social/backend/configuration/static_dir/ for more info

+ 88
- 1
test/fixtures/kroeg-announce-with-inline-actor.json View File

@@ -1 +1,88 @@
{"@context":["https://www.w3.org/ns/activitystreams","https://puckipedia.com/-/context"],"actor":{"endpoints":"https://puckipedia.com/#endpoints","followers":"https://puckipedia.com/followers","following":"https://puckipedia.com/following","icon":{"mediaType":"image/png","type":"Image","url":"https://puckipedia.com/images/avatar.png"},"id":"https://puckipedia.com/","inbox":"https://puckipedia.com/inbox","kroeg:blocks":{"id":"https://puckipedia.com/blocks"},"liked":"https://puckipedia.com/liked","manuallyApprovesFollowers":false,"name":"HACKER TEEN PUCKIPEDIA 👩‍💻","outbox":"https://puckipedia.com/outbox","preferredUsername":"puckipedia","publicKey":{"id":"https://puckipedia.com/#key","owner":"https://puckipedia.com/","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----","type":[]},"summary":"<p>federated hacker teen<br/>\n[<a href=\"https://pronoun.is/she\">she</a>/<a href=\"https://pronoun.is/they\">they</a>]</p>","type":"Person","updated":"2017-12-19T16:56:29.7576707+00:00"},"cc":"http://mastodon.example.org/users/admin","id":"https://puckipedia.com/cc56a9658e","object":{"as:sensitive":false,"attributedTo":{"endpoints":{"sharedInbox":"https://mastodon.social/inbox","type":[]},"followers":"http://mastodon.example.org/users/admin/followers","following":"http://mastodon.example.org/users/admin/following","icon":{"mediaType":"image/png","type":"Image","url":"https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"},"id":"http://mastodon.example.org/users/admin","inbox":"http://mastodon.example.org/users/admin/inbox","manuallyApprovesFollowers":{"@value":"False","type":"xsd:boolean"},"name":"","outbox":"http://mastodon.example.org/users/admin/outbox","preferredUsername":"revenant","publicKey":{"id":"http://mastodon.example.org/users/admin#main-key","owner":"http://mastodon.example.org/users/admin","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n","type":[]},"summary":"<p>neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net</p>","type":"Person","url":"https://mastodon.social/@revenant"},"cc":"http://mastodon.example.org/users/admin/followers","content":"<p>the name&apos;s jond (jeans bond)</p>","contentMap":{"en":"<p>the name&apos;s jond (jeans bond)</p>"},"conversation":"tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation","id":"http://mastodon.example.org/users/admin/statuses/100787282858396771","ostatus:atomUri":"http://mastodon.example.org/users/admin/statuses/100787282858396771","published":"2018-09-25T16:11:29Z","to":"https://www.w3.org/ns/activitystreams#Public","type":"Note","url":"https://mastodon.social/@revenant/100787282858396771"},"to":["https://www.w3.org/ns/activitystreams#Public","https://puckipedia.com/followers"],"type":"Announce"}
{
"@context" : [
"https://www.w3.org/ns/activitystreams",
"https://puckipedia.com/-/context"
],
"actor" : {
"endpoints" : "https://puckipedia.com/#endpoints",
"followers" : "https://puckipedia.com/followers",
"following" : "https://puckipedia.com/following",
"icon" : {
"mediaType" : "image/png",
"type" : "Image",
"url" : "https://puckipedia.com/images/avatar.png"
},
"id" : "https://puckipedia.com/",
"inbox" : "https://puckipedia.com/inbox",
"kroeg:blocks" : {
"id" : "https://puckipedia.com/blocks"
},
"liked" : "https://puckipedia.com/liked",
"manuallyApprovesFollowers" : false,
"name" : "HACKER TEEN PUCKIPEDIA 👩‍💻",
"outbox" : "https://puckipedia.com/outbox",
"preferredUsername" : "puckipedia",
"publicKey" : {
"id" : "https://puckipedia.com/#key",
"owner" : "https://puckipedia.com/",
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----",
"type" : []
},
"summary" : "<p>federated hacker teen<br/>\n[<a href=\"https://pronoun.is/she\">she</a>/<a href=\"https://pronoun.is/they\">they</a>]</p>",
"type" : "Person",
"updated" : "2017-12-19T16:56:29.7576707+00:00"
},
"cc" : "http://mastodon.example.org/users/admin",
"id" : "https://puckipedia.com/cc56a9658e",
"object" : {
"as:sensitive" : false,
"attributedTo" : {
"endpoints" : {
"sharedInbox" : "https://mastodon.social/inbox",
"type" : []
},
"followers" : "http://mastodon.example.org/users/admin/followers",
"following" : "http://mastodon.example.org/users/admin/following",
"icon" : {
"mediaType" : "image/png",
"type" : "Image",
"url" : "https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"
},
"id" : "http://mastodon.example.org/users/admin",
"inbox" : "http://mastodon.example.org/users/admin/inbox",
"manuallyApprovesFollowers" : {
"@value" : "False",
"type" : "xsd:boolean"
},
"name" : "",
"outbox" : "http://mastodon.example.org/users/admin/outbox",
"preferredUsername" : "revenant",
"publicKey" : {
"id" : "http://mastodon.example.org/users/admin#main-key",
"owner" : "http://mastodon.example.org/users/admin",
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n",
"type" : []
},
"summary" : "<p>neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net</p>",
"type" : "Person",
"url" : "https://mastodon.social/@revenant"
},
"cc" : "http://mastodon.example.org/users/admin/followers",
"content" : "<p>the name&apos;s jond (jeans bond)</p>",
"contentMap" : {
"en" : "<p>the name&apos;s jond (jeans bond)</p>"
},
"conversation" : "tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation",
"id" : "http://mastodon.example.org/users/admin/statuses/100787282858396771",
"ostatus:atomUri" : "http://mastodon.example.org/users/admin/statuses/100787282858396771",
"published" : "2018-09-25T16:11:29Z",
"to" : "https://www.w3.org/ns/activitystreams#Public",
"type" : "Note",
"url" : "https://mastodon.social/@revenant/100787282858396771"
},
"to" : [
"https://www.w3.org/ns/activitystreams#Public",
"https://puckipedia.com/followers"
],
"type" : "Announce"
}

+ 43
- 7
test/fixtures/mastodon-note-object.json View File

@@ -1,9 +1,45 @@
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":"as:movedTo","Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji"}],"id":"http://mastodon.example.org/users/admin/statuses/99541947525187367","type":"Note","summary":null,"content":"\u003cp\u003eyeah.\u003c/p\u003e","inReplyTo":null,"published":"2018-02-17T17:46:20Z","url":"http://mastodon.example.org/@admin/99541947525187367","attributedTo":"http://mastodon.example.org/users/admin","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["http://mastodon.example.org/users/admin/followers"],"sensitive":false,"atomUri":"http://mastodon.example.org/users/admin/statuses/99541947525187367","inReplyToAtomUri":null,"conversation":"tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation","tag":[],
"attachment": [
{
"@context" : [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{ {
"url": "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
"type": "Document",
"name": null,
"mediaType": "image/jpeg"
"Emoji" : "toot:Emoji",
"Hashtag" : "as:Hashtag",
"atomUri" : "ostatus:atomUri",
"conversation" : "ostatus:conversation",
"inReplyToAtomUri" : "ostatus:inReplyToAtomUri",
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
"movedTo" : "as:movedTo",
"ostatus" : "http://ostatus.org#",
"sensitive" : "as:sensitive",
"toot" : "http://joinmastodon.org/ns#"
} }
]}
],
"atomUri" : "http://mastodon.example.org/users/admin/statuses/99541947525187367",
"attachment" : [
{
"mediaType" : "image/jpeg",
"name" : null,
"type" : "Document",
"url" : "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg"
}
],
"attributedTo" : "http://mastodon.example.org/users/admin",
"cc" : [
"http://mastodon.example.org/users/admin/followers"
],
"content" : "<p>yeah.</p>",
"conversation" : "tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation",
"id" : "http://mastodon.example.org/users/admin/statuses/99541947525187367",
"inReplyTo" : null,
"inReplyToAtomUri" : null,
"published" : "2018-02-17T17:46:20Z",
"sensitive" : false,
"summary" : null,
"tag" : [],
"to" : [
"https://www.w3.org/ns/activitystreams#Public"
],
"type" : "Note",
"url" : "http://mastodon.example.org/@admin/99541947525187367"
}

+ 3
- 3
test/notification_test.exs View File

@@ -648,7 +648,7 @@ defmodule Pleroma.NotificationTest do
status: "hey @#{other_user.nickname}!" status: "hey @#{other_user.nickname}!"
}) })


{:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user)
{:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)


{enabled_receivers, _disabled_receivers} = {enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two) Notification.get_notified_from_activity(activity_two)
@@ -778,7 +778,7 @@ defmodule Pleroma.NotificationTest do


assert Enum.empty?(Notification.for_user(user)) assert Enum.empty?(Notification.for_user(user))


{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)


assert length(Notification.for_user(user)) == 1 assert length(Notification.for_user(user)) == 1


@@ -795,7 +795,7 @@ defmodule Pleroma.NotificationTest do


assert Enum.empty?(Notification.for_user(user)) assert Enum.empty?(Notification.for_user(user))


{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)


assert length(Notification.for_user(user)) == 1 assert length(Notification.for_user(user)) == 1




+ 2
- 1
test/tasks/instance_test.exs View File

@@ -63,7 +63,7 @@ defmodule Pleroma.InstanceTest do
"--uploads-dir", "--uploads-dir",
"test/uploads", "test/uploads",
"--static-dir", "--static-dir",
"instance/static/"
"./test/../test/instance/static/"
]) ])
end end


@@ -83,6 +83,7 @@ defmodule Pleroma.InstanceTest do
assert generated_config =~ "configurable_from_database: true" assert generated_config =~ "configurable_from_database: true"
assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
assert File.exists?(Path.expand("./test/instance/static/robots.txt"))
end end


defp generated_setup_psql do defp generated_setup_psql do


+ 4
- 1
test/tasks/user_test.exs View File

@@ -91,6 +91,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do


describe "running rm" do describe "running rm" do
test "user is deleted" do test "user is deleted" do
clear_config([:instance, :federating], true)
user = insert(:user) user = insert(:user)


with_mock Pleroma.Web.Federator, with_mock Pleroma.Web.Federator,
@@ -108,8 +109,10 @@ defmodule Mix.Tasks.Pleroma.UserTest do


test "a remote user's create activity is deleted when the object has been pruned" do test "a remote user's create activity is deleted when the object has been pruned" do
user = insert(:user) user = insert(:user)

{:ok, post} = CommonAPI.post(user, %{status: "uguu"}) {:ok, post} = CommonAPI.post(user, %{status: "uguu"})

clear_config([:instance, :federating], true)

object = Object.normalize(post) object = Object.normalize(post)
Object.prune(object) Object.prune(object)




+ 14
- 2
test/user_test.exs View File

@@ -992,7 +992,7 @@ defmodule Pleroma.UserTest do
user = insert(:user, local: true) user = insert(:user, local: true)


{:ok, activity} = CommonAPI.post(actor, %{status: "hello"}) {:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
{:ok, announce} = CommonAPI.repeat(activity.id, user)


recipients = User.get_recipients_from_activity(announce) recipients = User.get_recipients_from_activity(announce)


@@ -1147,7 +1147,7 @@ defmodule Pleroma.UserTest do


{:ok, like} = CommonAPI.favorite(user, activity_two.id) {:ok, like} = CommonAPI.favorite(user, activity_two.id)
{:ok, like_two} = CommonAPI.favorite(follower, activity.id) {:ok, like_two} = CommonAPI.favorite(follower, activity.id)
{:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
{:ok, repeat} = CommonAPI.repeat(activity_two.id, user)


{:ok, job} = User.delete(user) {:ok, job} = User.delete(user)
{:ok, _user} = ObanHelpers.perform(job) {:ok, _user} = ObanHelpers.perform(job)
@@ -1777,4 +1777,16 @@ defmodule Pleroma.UserTest do
assert result.email_notifications["digest"] == false assert result.email_notifications["digest"] == false
end end
end end

test "avatar fallback" do
user = insert(:user)
assert User.avatar_url(user) =~ "/images/avi.png"

Pleroma.Config.put([:assets, :default_user_avatar], "avatar.png")

user = User.get_cached_by_nickname_or_id(user.nickname)
assert User.avatar_url(user) =~ "avatar.png"

assert User.avatar_url(user, no_default: true) == nil
end
end end

+ 60
- 1
test/web/activity_pub/activity_pub_controller_test.exs View File

@@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo


import Pleroma.Factory
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Delivery alias Pleroma.Delivery
@@ -14,13 +13,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Tests.ObanHelpers alias Pleroma.Tests.ObanHelpers
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.UserView alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint
alias Pleroma.Workers.ReceiverWorker alias Pleroma.Workers.ReceiverWorker


import Pleroma.Factory

require Pleroma.Constants

setup_all do setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok :ok
@@ -168,6 +173,60 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
end end
end end


describe "mastodon compatibility routes" do
test "it returns a json representation of the object with accept application/json", %{
conn: conn
} do
{:ok, object} =
%{
"type" => "Note",
"content" => "hey",
"id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
"actor" => Endpoint.url() <> "/users/raymoo",
"to" => [Pleroma.Constants.as_public()]
}
|> Object.create()

conn =
conn
|> put_req_header("accept", "application/json")
|> get("/users/raymoo/statuses/999999999")

assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object})
end

test "it returns a json representation of the activity with accept application/json", %{
conn: conn
} do
{:ok, object} =
%{
"type" => "Note",
"content" => "hey",
"id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
"actor" => Endpoint.url() <> "/users/raymoo",
"to" => [Pleroma.Constants.as_public()]
}
|> Object.create()

{:ok, activity, _} =
%{
"id" => object.data["id"] <> "/activity",
"type" => "Create",
"object" => object.data["id"],
"actor" => object.data["actor"],
"to" => object.data["to"]
}
|> ActivityPub.persist(local: true)

conn =
conn
|> put_req_header("accept", "application/json")
|> get("/users/raymoo/statuses/999999999/activity")

assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
end
end

describe "/objects/:uuid" do 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


+ 8
- 77
test/web/activity_pub/activity_pub_test.exs View File

@@ -537,7 +537,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert Enum.member?(activities, activity_one) assert Enum.member?(activities, activity_one)


{:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]}) {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id) %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
activity_three = Activity.get_by_id(activity_three.id) activity_three = Activity.get_by_id(activity_three.id)


@@ -592,7 +592,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do


{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})


{:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)


activities = activities =
ActivityPub.fetch_activities([], %{"blocking_user" => blocker}) ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
@@ -618,7 +618,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do


followed_user = insert(:user) followed_user = insert(:user)
ActivityPub.follow(user, followed_user) ActivityPub.follow(user, followed_user)
{:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)


activities = activities =
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
@@ -651,7 +651,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"}) another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}}) bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
bad_activity = insert(:note_activity, %{note: bad_note}) bad_activity = insert(:note_activity, %{note: bad_note})
{:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)


activities = activities =
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true}) ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
@@ -699,7 +699,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do


activity_three_actor = User.get_by_ap_id(activity_three.data["actor"]) activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
{:ok, _user_relationships} = User.mute(user, activity_three_actor) {:ok, _user_relationships} = User.mute(user, activity_three_actor)
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id) %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
activity_three = Activity.get_by_id(activity_three.id) activity_three = Activity.get_by_id(activity_three.id)


@@ -749,7 +749,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do


{:ok, user} = User.follow(user, booster) {:ok, user} = User.follow(user, booster)


{:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
{:ok, announce} = CommonAPI.repeat(activity_three.id, booster)


[announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)]) [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])


@@ -846,7 +846,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
booster = insert(:user) booster = insert(:user)
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster) {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)


{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
{:ok, activity} = CommonAPI.repeat(activity.id, booster)


activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) activities = ActivityPub.fetch_activities([], %{"muting_user" => user})


@@ -860,7 +860,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster) {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
{:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster) {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)


{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
{:ok, activity} = CommonAPI.repeat(activity.id, booster)


activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) activities = ActivityPub.fetch_activities([], %{"muting_user" => user})


@@ -868,75 +868,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end end
end end


describe "announcing an object" do
test "adds an announce activity to the db" do
note_activity = insert(:note_activity)
object = Object.normalize(note_activity)
user = insert(:user)

{:ok, announce_activity, object} = ActivityPub.announce(user, object)
assert object.data["announcement_count"] == 1
assert object.data["announcements"] == [user.ap_id]

assert announce_activity.data["to"] == [
User.ap_followers(user),
note_activity.data["actor"]
]

assert announce_activity.data["object"] == object.data["id"]
assert announce_activity.data["actor"] == user.ap_id
assert announce_activity.data["context"] == object.data["context"]
end

test "reverts annouce from object on error" do
note_activity = insert(:note_activity)
object = Object.normalize(note_activity)
user = insert(:user)

with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
assert {:error, :reverted} = ActivityPub.announce(user, object)
end

reloaded_object = Object.get_by_ap_id(object.data["id"])
assert reloaded_object == object
refute reloaded_object.data["announcement_count"]
refute reloaded_object.data["announcements"]
end
end

describe "announcing a private object" do
test "adds an announce activity to the db if the audience is not widened" do
user = insert(:user)
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
object = Object.normalize(note_activity)

{:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)

assert announce_activity.data["to"] == [User.ap_followers(user)]

assert announce_activity.data["object"] == object.data["id"]
assert announce_activity.data["actor"] == user.ap_id
assert announce_activity.data["context"] == object.data["context"]
end

test "does not add an announce activity to the db if the audience is widened" do
user = insert(:user)
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
object = Object.normalize(note_activity)

assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
end

test "does not add an announce activity to the db if the announcer is not the author" do
user = insert(:user)
announcer = insert(:user)
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
object = Object.normalize(note_activity)

assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
end
end

describe "uploading files" do describe "uploading files" do
test "copies the file to the configured folder" do test "copies the file to the configured folder" do
file = %Plug.Upload{ file = %Plug.Upload{


+ 92
- 0
test/web/activity_pub/object_validator_test.exs View File

@@ -516,4 +516,96 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
assert {:object, valid_like["object"]} in validated.changes assert {:object, valid_like["object"]} in validated.changes
end end
end end

describe "announces" do
setup do
user = insert(:user)
announcer = insert(:user)
{:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"})

object = Object.normalize(post_activity, false)
{:ok, valid_announce, []} = Builder.announce(announcer, object)

%{
valid_announce: valid_announce,
user: user,
post_activity: post_activity,
announcer: announcer
}
end

test "returns ok for a valid announce", %{valid_announce: valid_announce} do
assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, [])
end

test "returns an error if the object can't be found", %{valid_announce: valid_announce} do
without_object =
valid_announce
|> Map.delete("object")

{:error, cng} = ObjectValidator.validate(without_object, [])

assert {:object, {"can't be blank", [validation: :required]}} in cng.errors

nonexisting_object =
valid_announce
|> Map.put("object", "https://gensokyo.2hu/objects/99999999")

{:error, cng} = ObjectValidator.validate(nonexisting_object, [])

assert {:object, {"can't find object", []}} in cng.errors
end

test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do
nonexisting_actor =
valid_announce
|> Map.put("actor", "https://gensokyo.2hu/users/raymoo")

{:error, cng} = ObjectValidator.validate(nonexisting_actor, [])

assert {:actor, {"can't find user", []}} in cng.errors
end

test "returns an error if the actor already announced the object", %{
valid_announce: valid_announce,
announcer: announcer,
post_activity: post_activity
} do
_announce = CommonAPI.repeat(post_activity.id, announcer)

{:error, cng} = ObjectValidator.validate(valid_announce, [])

assert {:actor, {"already announced this object", []}} in cng.errors
assert {:object, {"already announced by this actor", []}} in cng.errors
end

test "returns an error if the actor can't announce the object", %{
announcer: announcer,
user: user
} do
{:ok, post_activity} =
CommonAPI.post(user, %{status: "a secret post", visibility: "private"})

object = Object.normalize(post_activity, false)

# Another user can't announce it
{:ok, announce, []} = Builder.announce(announcer, object, public: false)

{:error, cng} = ObjectValidator.validate(announce, [])

assert {:actor, {"can not announce this object", []}} in cng.errors

# The actor of the object can announce it
{:ok, announce, []} = Builder.announce(user, object, public: false)

assert {:ok, _, _} = ObjectValidator.validate(announce, [])

# The actor of the object can not announce it publicly
{:ok, announce, []} = Builder.announce(user, object, public: true)

{:error, cng} = ObjectValidator.validate(announce, [])

assert {:actor, {"can not announce this object publicly", []}} in cng.errors
end
end
end end

+ 44
- 0
test/web/activity_pub/pipeline_test.exs View File

@@ -9,6 +9,11 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
import Pleroma.Factory import Pleroma.Factory


describe "common_pipeline/2" do describe "common_pipeline/2" do
setup do
clear_config([:instance, :federating], true)
:ok
end

test "it goes through validation, filtering, persisting, side effects and federation for local activities" do test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
activity = insert(:note_activity) activity = insert(:note_activity)
meta = [local: true] meta = [local: true]
@@ -83,5 +88,44 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
end end
end end

test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do
clear_config([:instance, :federating], false)

activity = insert(:note_activity)
meta = [local: true]

with_mocks([
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
{
Pleroma.Web.ActivityPub.MRF,
[],
[filter: fn o -> {:ok, o} end]
},
{
Pleroma.Web.ActivityPub.ActivityPub,
[],
[persist: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.ActivityPub.SideEffects,
[],
[handle: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.Federator,
[],
[]
}
]) do
assert {:ok, ^activity, ^meta} =
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)

assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
end
end
end end
end end

+ 6
- 9
test/web/activity_pub/relay_test.exs View File

@@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
use Pleroma.DataCase use Pleroma.DataCase


alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Relay
@@ -95,21 +94,20 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
end) end)


assert capture_log(fn -> assert capture_log(fn ->
assert Relay.publish(activity) == {:error, nil}
end) =~ "[error] error: nil"
assert Relay.publish(activity) == {:error, false}
end) =~ "[error] error: false"
end end


test_with_mock "returns announce activity and publish to federate", test_with_mock "returns announce activity and publish to federate",
Pleroma.Web.Federator, Pleroma.Web.Federator,
[:passthrough], [:passthrough],
[] do [] do
Pleroma.Config.put([:instance, :federating], true)
clear_config([:instance, :federating], true)
service_actor = Relay.get_actor() service_actor = Relay.get_actor()
note = insert(:note_activity) note = insert(:note_activity)
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
assert {:ok, %Activity{} = activity} = Relay.publish(note)
assert activity.data["type"] == "Announce" assert activity.data["type"] == "Announce"
assert activity.data["actor"] == service_actor.ap_id assert activity.data["actor"] == service_actor.ap_id
assert activity.data["object"] == obj.data["id"]
assert called(Pleroma.Web.Federator.publish(activity)) assert called(Pleroma.Web.Federator.publish(activity))
end end


@@ -117,13 +115,12 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
Pleroma.Web.Federator, Pleroma.Web.Federator,
[:passthrough], [:passthrough],
[] do [] do
Pleroma.Config.put([:instance, :federating], false)
clear_config([:instance, :federating], false)
service_actor = Relay.get_actor() service_actor = Relay.get_actor()
note = insert(:note_activity) note = insert(:note_activity)
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
assert {:ok, %Activity{} = activity} = Relay.publish(note)
assert activity.data["type"] == "Announce" assert activity.data["type"] == "Announce"
assert activity.data["actor"] == service_actor.ap_id assert activity.data["actor"] == service_actor.ap_id
assert activity.data["object"] == obj.data["id"]
refute called(Pleroma.Web.Federator.publish(activity)) refute called(Pleroma.Web.Federator.publish(activity))
end end
end end


+ 58
- 1
test/web/activity_pub/side_effects_test.exs View File

@@ -173,7 +173,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
{:ok, post} = CommonAPI.post(poster, %{status: "hey"}) {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
{:ok, like} = CommonAPI.favorite(user, post.id) {:ok, like} = CommonAPI.favorite(user, post.id)
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
{:ok, announce, _} = CommonAPI.repeat(post.id, user)
{:ok, announce} = CommonAPI.repeat(post.id, user)
{:ok, block} = ActivityPub.block(user, poster) {:ok, block} = ActivityPub.block(user, poster)
User.block(user, poster) User.block(user, poster)


@@ -376,4 +376,61 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
assert chat assert chat
end end
end end

describe "announce objects" do
setup do
poster = insert(:user)
user = insert(:user)
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})

{:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)

{:ok, private_announce_data, _meta} =
Builder.announce(user, private_post.object, public: false)

{:ok, relay_announce_data, _meta} =
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)

{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
{:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)

%{
announce: announce,
user: user,
poster: poster,
private_announce: private_announce,
relay_announce: relay_announce
}
end

test "adds the announce to the original object", %{announce: announce, user: user} do
{:ok, announce, _} = SideEffects.handle(announce)
object = Object.get_by_ap_id(announce.data["object"])
assert object.data["announcement_count"] == 1
assert user.ap_id in object.data["announcements"]
end

test "does not add the announce to the original object if the actor is a service actor", %{
relay_announce: announce
} do
{:ok, announce, _} = SideEffects.handle(announce)
object = Object.get_by_ap_id(announce.data["object"])
assert object.data["announcement_count"] == nil
end

test "creates a notification", %{announce: announce, poster: poster} do
{:ok, announce, _} = SideEffects.handle(announce)
assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
end

test "it streams out the announce", %{announce: announce} do
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
{:ok, announce, _} = SideEffects.handle(announce)

assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))
end
end
end
end end

+ 172
- 0
test/web/activity_pub/transmogrifier/announce_handling_test.exs View File

@@ -0,0 +1,172 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do
use Pleroma.DataCase

alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

test "it works for incoming honk announces" do
user = insert(:user, ap_id: "https://honktest/u/test", local: false)
other_user = insert(:user)
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})

announce = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"actor" => "https://honktest/u/test",
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
"object" => post.data["object"],
"published" => "2019-06-25T19:33:58Z",
"to" => "https://www.w3.org/ns/activitystreams#Public",
"type" => "Announce"
}

{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce)

object = Object.get_by_ap_id(post.data["object"])

assert length(object.data["announcements"]) == 1
assert user.ap_id in object.data["announcements"]
end

test "it works for incoming announces with actor being inlined (kroeg)" do
data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()

_user = insert(:user, local: false, ap_id: data["actor"]["id"])
other_user = insert(:user)

{:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"})

data =
data
|> put_in(["object", "id"], post.data["object"])

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "https://puckipedia.com/"
end

test "it works for incoming announces, fetching the announced object" do
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
|> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367")

Tesla.Mock.mock(fn
%{method: :get} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")}
end)

_user = insert(:user, local: false, ap_id: data["actor"])

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce"

assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"

assert data["object"] ==
"http://mastodon.example.org/users/admin/statuses/99541947525187367"

assert(Activity.get_create_by_object_ap_id(data["object"]))
end

@tag capture_log: true
test "it works for incoming announces with an existing activity" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})

data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
|> Map.put("object", activity.data["object"])

_user = insert(:user, local: false, ap_id: data["actor"])

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce"

assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"

assert data["object"] == activity.data["object"]

assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
end

# Ignore inlined activities for now
@tag skip: true
test "it works for incoming announces with an inlined activity" do
data =
File.read!("test/fixtures/mastodon-announce-private.json")
|> Poison.decode!()

_user =
insert(:user,
local: false,
ap_id: data["actor"],
follower_address: data["actor"] <> "/followers"
)

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce"

assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"

object = Object.normalize(data["object"])

assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
assert object.data["content"] == "this is a private toot"
end

@tag capture_log: true
test "it rejects incoming announces with an inlined activity from another origin" do
Tesla.Mock.mock(fn
%{method: :get} -> %Tesla.Env{status: 404, body: ""}
end)

data =
File.read!("test/fixtures/bogus-mastodon-announce.json")
|> Poison.decode!()

_user = insert(:user, local: false, ap_id: data["actor"])

assert {:error, e} = Transmogrifier.handle_incoming(data)
end

test "it does not clobber the addressing on announce activities" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})

data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
|> Map.put("object", Object.normalize(activity).data["id"])
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|> Map.put("cc", [])

_user =
insert(:user,
local: false,
ap_id: data["actor"],
follower_address: "http://mastodon.example.org/users/admin/followers"
)

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
end
end

+ 61
- 170
test/web/activity_pub/transmogrifier_test.exs View File

@@ -28,6 +28,63 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
setup do: clear_config([:instance, :max_remote_account_fields]) setup do: clear_config([:instance, :max_remote_account_fields])


describe "handle_incoming" do describe "handle_incoming" do
test "it works for incoming notices with tag not being an array (kroeg)" do
data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"])

assert object.data["emoji"] == %{
"icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
}

data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"])

assert "test" in object.data["tag"]
end

test "it works for incoming notices with url not being a string (prismo)" do
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"])

assert object.data["url"] == "https://prismo.news/posts/83"
end

test "it cleans up incoming notices which are not really DMs" do
user = insert(:user)
other_user = insert(:user)

to = [user.ap_id, other_user.ap_id]

data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("to", to)
|> Map.put("cc", [])

object =
data["object"]
|> Map.put("to", to)
|> Map.put("cc", [])

data = Map.put(data, "object", object)

{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)

assert data["to"] == []
assert data["cc"] == to

object_data = Object.normalize(activity).data

assert object_data["to"] == []
assert object_data["cc"] == to
end

test "it ignores an incoming notice if we already have it" do test "it ignores an incoming notice if we already have it" do
activity = insert(:note_activity) activity = insert(:note_activity)


@@ -260,172 +317,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
"<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>" "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
end end


test "it works for incoming honk announces" do
_user = insert(:user, ap_id: "https://honktest/u/test", local: false)
other_user = insert(:user)
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})

announce = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"actor" => "https://honktest/u/test",
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
"object" => post.data["object"],
"published" => "2019-06-25T19:33:58Z",
"to" => "https://www.w3.org/ns/activitystreams#Public",
"type" => "Announce"
}

{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce)
end

test "it works for incoming announces with actor being inlined (kroeg)" do
data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "https://puckipedia.com/"
end

test "it works for incoming notices with tag not being an array (kroeg)" do
data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"])

assert object.data["emoji"] == %{
"icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
}

data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"])

assert "test" in object.data["tag"]
end

test "it works for incoming notices with url not being a string (prismo)" do
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"])

assert object.data["url"] == "https://prismo.news/posts/83"
end

test "it cleans up incoming notices which are not really DMs" do
user = insert(:user)
other_user = insert(:user)

to = [user.ap_id, other_user.ap_id]

data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("to", to)
|> Map.put("cc", [])

object =
data["object"]
|> Map.put("to", to)
|> Map.put("cc", [])

data = Map.put(data, "object", object)

{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)

assert data["to"] == []
assert data["cc"] == to

object_data = Object.normalize(activity).data

assert object_data["to"] == []
assert object_data["cc"] == to
end

test "it works for incoming announces" do
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce"

assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"

assert data["object"] ==
"http://mastodon.example.org/users/admin/statuses/99541947525187367"

assert Activity.get_create_by_object_ap_id(data["object"])
end

test "it works for incoming announces with an existing activity" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})

data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
|> Map.put("object", activity.data["object"])

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce"

assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"

assert data["object"] == activity.data["object"]

assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
end

test "it works for incoming announces with an inlined activity" do
data =
File.read!("test/fixtures/mastodon-announce-private.json")
|> Poison.decode!()

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce"

assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"

object = Object.normalize(data["object"])

assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
assert object.data["content"] == "this is a private toot"
end

@tag capture_log: true
test "it rejects incoming announces with an inlined activity from another origin" do
data =
File.read!("test/fixtures/bogus-mastodon-announce.json")
|> Poison.decode!()

assert :error = Transmogrifier.handle_incoming(data)
end

test "it does not clobber the addressing on announce activities" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})

data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
|> Map.put("object", Object.normalize(activity).data["id"])
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|> Map.put("cc", [])

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)

assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
end

test "it ensures that as:Public activities make it to their followers collection" do test "it ensures that as:Public activities make it to their followers collection" do
user = insert(:user) user = insert(:user)


@@ -1188,7 +1079,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do


{:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"}) {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})


{:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)


{:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data) {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)


@@ -1438,7 +1329,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
} }


assert capture_log(fn -> assert capture_log(fn ->
:error = Transmogrifier.handle_incoming(data)
{:error, _} = Transmogrifier.handle_incoming(data)
end) =~ "Object containment failed" end) =~ "Object containment failed"
end end


@@ -1453,7 +1344,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
} }


assert capture_log(fn -> assert capture_log(fn ->
:error = Transmogrifier.handle_incoming(data)
{:error, _} = Transmogrifier.handle_incoming(data)
end) =~ "Object containment failed" end) =~ "Object containment failed"
end end


@@ -1468,7 +1359,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
} }


assert capture_log(fn -> assert capture_log(fn ->
:error = Transmogrifier.handle_incoming(data)
{:error, _} = Transmogrifier.handle_incoming(data)
end) =~ "Object containment failed" end) =~ "Object containment failed"
end end
end end


+ 1
- 1
test/web/activity_pub/utils_test.exs View File

@@ -334,7 +334,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
assert object = Object.normalize(note_activity) assert object = Object.normalize(note_activity)
actor = insert(:user) actor = insert(:user)


{:ok, announce, _object} = ActivityPub.announce(actor, object)
{:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
assert Utils.get_existing_announce(actor.ap_id, object) == announce assert Utils.get_existing_announce(actor.ap_id, object) == announce
end end
end end


+ 1
- 1
test/web/activity_pub/views/object_view_test.exs View File

@@ -73,7 +73,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
object = Object.normalize(note) object = Object.normalize(note)
user = insert(:user) user = insert(:user)


{:ok, announce_activity, _} = CommonAPI.repeat(note.id, user)
{:ok, announce_activity} = CommonAPI.repeat(note.id, user)


result = ObjectView.render("object.json", %{object: announce_activity}) result = ObjectView.render("object.json", %{object: announce_activity})




test/web/admin_api/admin_api_controller_test.exs → test/web/admin_api/controllers/admin_api_controller_test.exs View File

@@ -148,6 +148,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "DELETE /api/pleroma/admin/users" do describe "DELETE /api/pleroma/admin/users" do
test "single user", %{admin: admin, conn: conn} do test "single user", %{admin: admin, conn: conn} do
user = insert(:user) user = insert(:user)
clear_config([:instance, :federating], true)


with_mock Pleroma.Web.Federator, with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do publish: fn _ -> nil end do
@@ -350,7 +351,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do


conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")


assert "Not found" == json_response(conn, 404)
assert %{"error" => "Not found"} == json_response(conn, 404)
end end
end end


@@ -683,7 +684,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")


assert json_response(conn, :bad_request) == assert json_response(conn, :bad_request) ==
"To send invites you need to set the `invites_enabled` option to true."
%{
"error" =>
"To send invites you need to set the `invites_enabled` option to true."
}
end end


test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
@@ -693,7 +697,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")


assert json_response(conn, :bad_request) == assert json_response(conn, :bad_request) ==
"To send invites you need to set the `registrations_open` option to false."
%{
"error" =>
"To send invites you need to set the `registrations_open` option to false."
}
end end
end end


@@ -1307,7 +1314,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
|> json_response(404) |> json_response(404)


assert response == "Not found"
assert response == %{"error" => "Not found"}
end end
end end


@@ -1413,7 +1420,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "with invalid token", %{conn: conn} do test "with invalid token", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})


assert json_response(conn, :not_found) == "Not found"
assert json_response(conn, :not_found) == %{"error" => "Not found"}
end end
end end


@@ -1440,7 +1447,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "returns 404 when report id is invalid", %{conn: conn} do test "returns 404 when report id is invalid", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/reports/test") conn = get(conn, "/api/pleroma/admin/reports/test")


assert json_response(conn, :not_found) == "Not found"
assert json_response(conn, :not_found) == %{"error" => "Not found"}
end end
end end


@@ -1697,115 +1704,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end
end end


describe "GET /api/pleroma/admin/statuses/:id" do
test "not found", %{conn: conn} do
assert conn
|> get("/api/pleroma/admin/statuses/not_found")
|> json_response(:not_found)
end

test "shows activity", %{conn: conn} do
activity = insert(:note_activity)

response =
conn
|> get("/api/pleroma/admin/statuses/#{activity.id}")
|> json_response(200)

assert response["id"] == activity.id
end
end

describe "PUT /api/pleroma/admin/statuses/:id" do
setup do
activity = insert(:note_activity)

%{id: activity.id}
end

test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|> json_response(:ok)

assert response["sensitive"]

log_entry = Repo.one(ModerationLog)

assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"

response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|> json_response(:ok)

refute response["sensitive"]
end

test "change visibility flag", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"})
|> json_response(:ok)

assert response["visibility"] == "public"

log_entry = Repo.one(ModerationLog)

assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated status ##{id}, set visibility: 'public'"

response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"})
|> json_response(:ok)

assert response["visibility"] == "private"

response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"})
|> json_response(:ok)

assert response["visibility"] == "unlisted"
end

test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{visibility: "test"})

assert json_response(conn, :bad_request) == "Unsupported visibility"
end
end

describe "DELETE /api/pleroma/admin/statuses/:id" do
setup do
activity = insert(:note_activity)

%{id: activity.id}
end

test "deletes status", %{conn: conn, id: id, admin: admin} do
conn
|> delete("/api/pleroma/admin/statuses/#{id}")
|> json_response(:ok)

refute Activity.get_by_id(id)

log_entry = Repo.one(ModerationLog)

assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deleted status ##{id}"
end

test "returns 404 when the status does not exist", %{conn: conn} do
conn = delete(conn, "/api/pleroma/admin/statuses/test")

assert json_response(conn, :not_found) == "Not found"
end
end

describe "GET /api/pleroma/admin/config" do describe "GET /api/pleroma/admin/config" do
setup do: clear_config(:configurable_from_database, true) setup do: clear_config(:configurable_from_database, true)


@@ -1814,7 +1712,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
conn = get(conn, "/api/pleroma/admin/config") conn = get(conn, "/api/pleroma/admin/config")


assert json_response(conn, 400) == assert json_response(conn, 400) ==
"To use this endpoint you need to enable configuration from database."
%{
"error" => "To use this endpoint you need to enable configuration from database."
}
end end


test "with settings only in db", %{conn: conn} do test "with settings only in db", %{conn: conn} do
@@ -1936,7 +1836,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []}) conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})


assert json_response(conn, 400) == assert json_response(conn, 400) ==
"To use this endpoint you need to enable configuration from database."
%{"error" => "To use this endpoint you need to enable configuration from database."}
end end


describe "POST /api/pleroma/admin/config" do describe "POST /api/pleroma/admin/config" do
@@ -2944,6 +2844,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert ":proxy_url" in db assert ":proxy_url" in db
end end


@tag capture_log: true
test "doesn't set keys not in the whitelist", %{conn: conn} do test "doesn't set keys not in the whitelist", %{conn: conn} do
clear_config(:database_config_whitelist, [ clear_config(:database_config_whitelist, [
{:pleroma, :key1}, {:pleroma, :key1},
@@ -2998,54 +2899,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
on_exit(fn -> Restarter.Pleroma.refresh() end) on_exit(fn -> Restarter.Pleroma.refresh() end)
end end


describe "GET /api/pleroma/admin/statuses" do
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
blocked = insert(:user)
user = insert(:user)
User.block(admin, blocked)

{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})

{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"})

response =
conn
|> get("/api/pleroma/admin/statuses")
|> json_response(200)

refute "private" in Enum.map(response, & &1["visibility"])
assert length(response) == 3
end

test "returns only local statuses with local_only on", %{conn: conn} do
user = insert(:user)
remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
insert(:note_activity, user: user, local: true)
insert(:note_activity, user: remote_user, local: false)

response =
conn
|> get("/api/pleroma/admin/statuses?local_only=true")
|> json_response(200)

assert length(response) == 1
end

test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
user = insert(:user)

{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})

{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
assert json_response(conn, 200) |> length() == 3
end
end

describe "GET /api/pleroma/admin/users/:nickname/statuses" do describe "GET /api/pleroma/admin/users/:nickname/statuses" do
setup do setup do
user = insert(:user) user = insert(:user)
@@ -3096,7 +2949,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "excludes reblogs by default", %{conn: conn, user: user} do test "excludes reblogs by default", %{conn: conn, user: user} do
other_user = insert(:user) other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "."}) {:ok, activity} = CommonAPI.post(user, %{status: "."})
{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)


conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses") conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
assert json_response(conn_res, 200) |> length() == 0 assert json_response(conn_res, 200) |> length() == 0

+ 194
- 0
test/web/admin_api/controllers/status_controller_test.exs View File

@@ -0,0 +1,194 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
use Pleroma.Web.ConnCase

import Pleroma.Factory

alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.ModerationLog
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI

setup do
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)

conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)

{:ok, %{admin: admin, token: token, conn: conn}}
end

describe "GET /api/pleroma/admin/statuses/:id" do
test "not found", %{conn: conn} do
assert conn
|> get("/api/pleroma/admin/statuses/not_found")
|> json_response_and_validate_schema(:not_found)
end

test "shows activity", %{conn: conn} do
activity = insert(:note_activity)

response =
conn
|> get("/api/pleroma/admin/statuses/#{activity.id}")
|> json_response_and_validate_schema(200)

assert response["id"] == activity.id
end
end

describe "PUT /api/pleroma/admin/statuses/:id" do
setup do
activity = insert(:note_activity)

%{id: activity.id}
end

test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|> json_response_and_validate_schema(:ok)

assert response["sensitive"]

log_entry = Repo.one(ModerationLog)

assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"

response =
conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|> json_response_and_validate_schema(:ok)

refute response["sensitive"]
end

test "change visibility flag", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"})
|> json_response_and_validate_schema(:ok)

assert response["visibility"] == "public"

log_entry = Repo.one(ModerationLog)

assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated status ##{id}, set visibility: 'public'"

response =
conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"})
|> json_response_and_validate_schema(:ok)

assert response["visibility"] == "private"

response =
conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"})
|> json_response_and_validate_schema(:ok)

assert response["visibility"] == "unlisted"
end

test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "test"})

assert %{"error" => "test - Invalid value for enum."} =
json_response_and_validate_schema(conn, :bad_request)
end
end

describe "DELETE /api/pleroma/admin/statuses/:id" do
setup do
activity = insert(:note_activity)

%{id: activity.id}
end

test "deletes status", %{conn: conn, id: id, admin: admin} do
conn
|> delete("/api/pleroma/admin/statuses/#{id}")
|> json_response_and_validate_schema(:ok)

refute Activity.get_by_id(id)

log_entry = Repo.one(ModerationLog)

assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deleted status ##{id}"
end

test "returns 404 when the status does not exist", %{conn: conn} do
conn = delete(conn, "/api/pleroma/admin/statuses/test")

assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
end
end

describe "GET /api/pleroma/admin/statuses" do
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
blocked = insert(:user)
user = insert(:user)
User.block(admin, blocked)

{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})

{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"})

response =
conn
|> get("/api/pleroma/admin/statuses")
|> json_response_and_validate_schema(200)

refute "private" in Enum.map(response, & &1["visibility"])
assert length(response) == 3
end

test "returns only local statuses with local_only on", %{conn: conn} do
user = insert(:user)
remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
insert(:note_activity, user: user, local: true)
insert(:note_activity, user: remote_user, local: false)

response =
conn
|> get("/api/pleroma/admin/statuses?local_only=true")
|> json_response_and_validate_schema(200)

assert length(response) == 1
end

test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
user = insert(:user)

{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})

{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
assert json_response_and_validate_schema(conn, 200) |> length() == 3
end
end
end

+ 38
- 6
test/web/common_api/common_api_test.exs View File

@@ -116,6 +116,8 @@ defmodule Pleroma.Web.CommonAPITest do


{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})


clear_config([:instance, :federating], true)

Object.normalize(post, false) Object.normalize(post, false)
|> Object.prune() |> Object.prune()


@@ -134,6 +136,8 @@ defmodule Pleroma.Web.CommonAPITest do


{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})


clear_config([:instance, :federating], true)

with_mock Pleroma.Web.Federator, with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, user) assert {:ok, delete} = CommonAPI.delete(post.id, user)
@@ -410,6 +414,32 @@ defmodule Pleroma.Web.CommonAPITest do
end) end)
end end


test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
user = insert(:user)
other_user = insert(:user)
third_user = insert(:user)

{:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})

{:ok, open_answer} =
CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})

# The OP is implicitly added
assert user.ap_id in open_answer.recipients

{:ok, secret_answer} =
CommonAPI.post(other_user, %{
status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
in_reply_to_status_id: post.id,
visibility: "direct"
})

assert third_user.ap_id in secret_answer.recipients

# The OP is not added
refute user.ap_id in secret_answer.recipients
end

test "it allows to address a list" do test "it allows to address a list" do
user = insert(:user) user = insert(:user)
{:ok, list} = Pleroma.List.create("foo", user) {:ok, list} = Pleroma.List.create("foo", user)
@@ -491,7 +521,8 @@ defmodule Pleroma.Web.CommonAPITest do


{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})


{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
{:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
assert Visibility.is_public?(announce_activity)
end end


test "can't repeat a repeat" do test "can't repeat a repeat" do
@@ -499,9 +530,9 @@ defmodule Pleroma.Web.CommonAPITest do
other_user = insert(:user) other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})


{:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)


refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user))
refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
end end


test "repeating a status privately" do test "repeating a status privately" do
@@ -510,10 +541,11 @@ defmodule Pleroma.Web.CommonAPITest do


{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})


{:ok, %Activity{} = announce_activity, _} =
{:ok, %Activity{} = announce_activity} =
CommonAPI.repeat(activity.id, user, %{visibility: "private"}) CommonAPI.repeat(activity.id, user, %{visibility: "private"})


assert Visibility.is_private?(announce_activity) assert Visibility.is_private?(announce_activity)
refute Visibility.visible_for_user?(announce_activity, nil)
end end


test "favoriting a status" do test "favoriting a status" do
@@ -533,8 +565,8 @@ defmodule Pleroma.Web.CommonAPITest do
other_user = insert(:user) other_user = insert(:user)


{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user)
{:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
{:ok, ^announce} = CommonAPI.repeat(activity.id, user)
end end


test "favoriting a status twice returns ok, but without the like activity" do test "favoriting a status twice returns ok, but without the like activity" do


+ 42
- 11
test/web/common_api/common_api_utils_test.exs View File

@@ -14,18 +14,41 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do


@public_address "https://www.w3.org/ns/activitystreams#Public" @public_address "https://www.w3.org/ns/activitystreams#Public"


test "it adds attachment links to a given text and attachment set" do
name =
"Sakura%20Mana%20%E2%80%93%20Turned%20on%20by%20a%20Senior%20OL%20with%20a%20Temptating%20Tight%20Skirt-s%20Full%20Hipline%20and%20Panty%20Shot-%20Beautiful%20Thick%20Thighs-%20and%20Erotic%20Ass-%20-2015-%20--%20Oppaitime%208-28-2017%206-50-33%20PM.png"
describe "add_attachments/2" do
setup do
name =
"Sakura Mana – Turned on by a Senior OL with a Temptating Tight Skirt-s Full Hipline and Panty Shot- Beautiful Thick Thighs- and Erotic Ass- -2015- -- Oppaitime 8-28-2017 6-50-33 PM.png"


attachment = %{
"url" => [%{"href" => name}]
}
attachment = %{
"url" => [%{"href" => URI.encode(name)}]
}


res = Utils.add_attachments("", [attachment])
%{name: name, attachment: attachment}
end

test "it adds attachment links to a given text and attachment set", %{
name: name,
attachment: attachment
} do
len = 10
clear_config([Pleroma.Upload, :filename_display_max_length], len)

expected =
"<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{String.slice(name, 0..len)}…</a>"

assert Utils.add_attachments("", [attachment]) == expected
end

test "doesn't truncate file name if config for truncate is set to 0", %{
name: name,
attachment: attachment
} do
clear_config([Pleroma.Upload, :filename_display_max_length], 0)

expected = "<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{name}</a>"


assert res ==
"<br><a href=\"#{name}\" class='attachment'>Sakura Mana – Turned on by a Se…</a>"
assert Utils.add_attachments("", [attachment]) == expected
end
end end


describe "it confirms the password given is the current users password" do describe "it confirms the password given is the current users password" do
@@ -297,11 +320,10 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do


{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil) {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil)


assert length(to) == 3
assert length(to) == 2
assert Enum.empty?(cc) assert Enum.empty?(cc)


assert mentioned_user.ap_id in to assert mentioned_user.ap_id in to
assert third_user.ap_id in to
assert user.follower_address in to assert user.follower_address in to
end end


@@ -327,6 +349,15 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do


{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil) {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil)


assert length(to) == 1
assert Enum.empty?(cc)

assert mentioned_user.ap_id in to

{:ok, direct_activity} = CommonAPI.post(third_user, %{status: "uguu", visibility: "direct"})

{to, cc} = Utils.get_to_and_cc(user, mentions, direct_activity, "direct", nil)

assert length(to) == 2 assert length(to) == 2
assert Enum.empty?(cc) assert Enum.empty?(cc)




+ 3
- 3
test/web/mastodon_api/controllers/account_controller_test.exs View File

@@ -256,7 +256,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
User.block(user_one, user_two) User.block(user_one, user_two)


{:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"}) {:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
{:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three)
{:ok, repeat} = CommonAPI.repeat(activity.id, user_three)


assert resp = assert resp =
conn conn
@@ -375,7 +375,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do


test "gets a user's statuses without reblogs", %{user: user, conn: conn} do test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"}) {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
{:ok, _, _} = CommonAPI.repeat(post_id, user)
{:ok, _} = CommonAPI.repeat(post_id, user)


conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true") conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200) assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
@@ -678,7 +678,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200) assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)


{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
{:ok, %{id: reblog_id}, _} = CommonAPI.repeat(activity.id, followed)
{:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)


assert [] == assert [] ==
conn conn


+ 4
- 4
test/web/mastodon_api/controllers/notification_controller_test.exs View File

@@ -280,8 +280,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
{:ok, unlisted_activity} = {:ok, unlisted_activity} =
CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"})


{:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
{:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
{:ok, _} = CommonAPI.repeat(public_activity.id, user)
{:ok, _} = CommonAPI.repeat(unlisted_activity.id, user)


activity_ids = activity_ids =
conn conn
@@ -301,7 +301,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)


mention_notification_id = get_notification_id_by_activity(mention_activity) mention_notification_id = get_notification_id_by_activity(mention_activity)
@@ -339,7 +339,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)


mention_notification_id = get_notification_id_by_activity(mention_activity) mention_notification_id = get_notification_id_by_activity(mention_activity)


+ 9
- 9
test/web/mastodon_api/controllers/status_controller_test.exs View File

@@ -878,8 +878,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
user3 = insert(:user) user3 = insert(:user)
{:ok, _} = CommonAPI.favorite(user2, activity.id) {:ok, _} = CommonAPI.favorite(user2, activity.id)
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id) {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
{:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
{:ok, _} = CommonAPI.repeat(activity.id, user2)


conn_res = conn_res =
build_conn() build_conn()
@@ -917,7 +917,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
activity = insert(:note_activity) activity = insert(:note_activity)


{:ok, _, _} = CommonAPI.repeat(activity.id, user)
{:ok, _} = CommonAPI.repeat(activity.id, user)


conn = conn =
conn conn
@@ -1427,7 +1427,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do


test "returns users who have reblogged the status", %{conn: conn, activity: activity} do test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
other_user = insert(:user) other_user = insert(:user)
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)


response = response =
conn conn
@@ -1458,7 +1458,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
other_user = insert(:user) other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user) {:ok, _user_relationship} = User.block(user, other_user)


{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)


response = response =
conn conn
@@ -1469,12 +1469,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
end end


test "does not return users who have reblogged the status privately", %{ test "does not return users who have reblogged the status privately", %{
conn: conn,
activity: activity
conn: conn
} do } do
other_user = insert(:user) other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})


{:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
{:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})


response = response =
conn conn
@@ -1486,7 +1486,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do


test "does not fail on an unauthenticated request", %{activity: activity} do test "does not fail on an unauthenticated request", %{activity: activity} do
other_user = insert(:user) other_user = insert(:user)
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)


response = response =
build_conn() build_conn()


+ 1
- 1
test/web/mastodon_api/views/notification_view_test.exs View File

@@ -104,7 +104,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
user = insert(:user) user = insert(:user)
another_user = insert(:user) another_user = insert(:user)
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, reblog_activity, _object} = CommonAPI.repeat(create_activity.id, another_user)
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, another_user)
{:ok, [notification]} = Notification.create_notifications(reblog_activity) {:ok, [notification]} = Notification.create_notifications(reblog_activity)
reblog_activity = Activity.get_by_id(create_activity.id) reblog_activity = Activity.get_by_id(create_activity.id)




+ 2
- 2
test/web/mastodon_api/views/status_view_test.exs View File

@@ -442,7 +442,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
user = insert(:user) user = insert(:user)
activity = insert(:note_activity) activity = insert(:note_activity)


{:ok, reblog, _} = CommonAPI.repeat(activity.id, user)
{:ok, reblog} = CommonAPI.repeat(activity.id, user)


represented = StatusView.render("show.json", %{for: user, activity: reblog}) represented = StatusView.render("show.json", %{for: user, activity: reblog})


@@ -600,7 +600,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
status: "˙˙ɐʎns" status: "˙˙ɐʎns"
}) })


{:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
{:ok, activity} = CommonAPI.repeat(activity.id, other_user)


result = StatusView.render("show.json", %{activity: activity, for: user}) result = StatusView.render("show.json", %{activity: activity, for: user})




+ 45
- 0
test/web/ostatus/ostatus_controller_test.exs View File

@@ -10,7 +10,11 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint

require Pleroma.Constants


setup_all do setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -19,6 +23,47 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do


setup do: clear_config([:instance, :federating], true) setup do: clear_config([:instance, :federating], true)


describe "Mastodon compatibility routes" do
setup %{conn: conn} do
conn = put_req_header(conn, "accept", "text/html")

{:ok, object} =
%{
"type" => "Note",
"content" => "hey",
"id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
"actor" => Endpoint.url() <> "/users/raymoo",
"to" => [Pleroma.Constants.as_public()]
}
|> Object.create()

{:ok, activity, _} =
%{
"id" => object.data["id"] <> "/activity",
"type" => "Create",
"object" => object.data["id"],
"actor" => object.data["actor"],
"to" => object.data["to"]
}
|> ActivityPub.persist(local: true)

%{conn: conn, activity: activity}
end

test "redirects to /notice/:id for html format", %{conn: conn, activity: activity} do
conn = get(conn, "/users/raymoo/statuses/999999999")
assert redirected_to(conn) == "/notice/#{activity.id}"
end

test "redirects to /notice/:id for html format for activity", %{
conn: conn,
activity: activity
} do
conn = get(conn, "/users/raymoo/statuses/999999999/activity")
assert redirected_to(conn) == "/notice/#{activity.id}"
end
end

# Note: see ActivityPubControllerTest for JSON format tests # Note: see ActivityPubControllerTest for JSON format tests
describe "GET /objects/:uuid (text/html)" do describe "GET /objects/:uuid (text/html)" do
setup %{conn: conn} do setup %{conn: conn} do


+ 7
- 0
test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs View File

@@ -33,6 +33,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do
assert result["pleroma"]["emoji_reactions"] == [ assert result["pleroma"]["emoji_reactions"] == [
%{"name" => "☕", "count" => 1, "me" => true} %{"name" => "☕", "count" => 1, "me" => true}
] ]

# Reacting with a non-emoji
assert conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/x")
|> json_response_and_validate_schema(400)
end end


test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do


+ 8
- 3
test/web/pleroma_api/controllers/notification_controller_test.exs View File

@@ -23,7 +23,8 @@ defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do


response = response =
conn conn
|> post("/api/v1/pleroma/notifications/read?id=#{notification1.id}")
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/notifications/read", %{id: notification1.id})
|> json_response_and_validate_schema(:ok) |> json_response_and_validate_schema(:ok)


assert %{"pleroma" => %{"is_seen" => true}} = response assert %{"pleroma" => %{"is_seen" => true}} = response
@@ -41,7 +42,8 @@ defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do


[response1, response2] = [response1, response2] =
conn conn
|> post("/api/v1/pleroma/notifications/read?max_id=#{notification2.id}")
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/notifications/read", %{max_id: notification2.id})
|> json_response_and_validate_schema(:ok) |> json_response_and_validate_schema(:ok)


assert %{"pleroma" => %{"is_seen" => true}} = response1 assert %{"pleroma" => %{"is_seen" => true}} = response1
@@ -54,7 +56,10 @@ defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do
test "it returns error when notification not found", %{conn: conn} do test "it returns error when notification not found", %{conn: conn} do
response = response =
conn conn
|> post("/api/v1/pleroma/notifications/read?id=22222222222222")
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/notifications/read", %{
id: 22_222_222_222_222
})
|> json_response_and_validate_schema(:bad_request) |> json_response_and_validate_schema(:bad_request)


assert response == %{"error" => "Cannot get notification"} assert response == %{"error" => "Cannot get notification"}


+ 1
- 1
test/web/push/impl_test.exs View File

@@ -151,7 +151,7 @@ defmodule Pleroma.Web.Push.ImplTest do
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
}) })


{:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
object = Object.normalize(activity) object = Object.normalize(activity)


assert Impl.format_body(%{activity: announce_activity}, user, object) == assert Impl.format_body(%{activity: announce_activity}, user, object) ==


+ 3
- 3
test/web/streamer/streamer_test.exs View File

@@ -106,7 +106,7 @@ defmodule Pleroma.Web.StreamerTest do


other_user = insert(:user) other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
{:ok, announce} = CommonAPI.repeat(activity.id, user)


assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
refute Streamer.filtered_by_user?(user, announce) refute Streamer.filtered_by_user?(user, announce)
@@ -427,7 +427,7 @@ defmodule Pleroma.Web.StreamerTest do
{:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"}) {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"})


Streamer.get_topic_and_add_socket("user", user1) Streamer.get_topic_and_add_socket("user", user1)
{:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
{:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2)
assert_receive {:render_with_user, _, _, ^announce_activity} assert_receive {:render_with_user, _, _, ^announce_activity}
assert Streamer.filtered_by_user?(user1, announce_activity) assert Streamer.filtered_by_user?(user1, announce_activity)
end end
@@ -440,7 +440,7 @@ defmodule Pleroma.Web.StreamerTest do


{:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
Streamer.get_topic_and_add_socket("user", user1) Streamer.get_topic_and_add_socket("user", user1)
{:ok, _favorite_activity, _} = CommonAPI.repeat(create_activity.id, user2)
{:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2)


assert_receive {:render_with_user, _, "notification.json", notif} assert_receive {:render_with_user, _, "notification.json", notif}
assert Streamer.filtered_by_user?(user1, notif) assert Streamer.filtered_by_user?(user1, notif)


+ 1
- 0
test/workers/cron/new_users_digest_worker_test.exs View File

@@ -28,6 +28,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do
assert email.html_body =~ user.nickname assert email.html_body =~ user.nickname
assert email.html_body =~ user2.nickname assert email.html_body =~ user2.nickname
assert email.html_body =~ "cofe" assert email.html_body =~ "cofe"
assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png"
end end


test "it doesn't fail when admin has no email" do test "it doesn't fail when admin has no email" do


Loading…
Cancel
Save