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.

### Added
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
- Instance: Add `background_image` to configuration and `/api/v1/instance`
- Instance: Extend `/api/v1/instance` with Pleroma-specific information.
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` 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: 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.
- Mix task to create trusted OAuth App.
- 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
- 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
- 1
config/config.exs View File

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

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,
minimalScopesMode: false,
noAttachmentLinks: false,
nsfwCensorImage: "",
nsfwCensorImage: "/static/img/nsfw.74818f9.png",
postContentType: "text/plain",
redirectRootLogin: "/main/friends",
redirectRootNoLogin: "/main/all",
@@ -1226,7 +1238,7 @@ config :pleroma, :config_description, [
type: :string,
description:
"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,
@@ -1346,6 +1358,12 @@ config :pleroma, :config_description, [
suggestions: [
: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.
* `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.
* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.

!!! warning
`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)?",
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
)
|> Path.expand()

static_dir =
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.)?",
Pleroma.Config.get([:instance, :static_dir])
)
|> Path.expand()

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}.")
File.write(psql_path, result_psql)

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

shell_info(
"\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

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

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

unless File.exists?(static_dir) do
File.mkdir_p!(static_dir)
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])

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()
|> 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
end
end

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

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

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

def avatar_url(user, options \\ []) 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



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

@@ -363,36 +363,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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()) ::
{:ok, Activity.t()} | {:error, any()}
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.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Endpoint
alias Pleroma.Web.FederatingPlug
alias Pleroma.Web.Federator

@@ -75,8 +76,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
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),
{_, true} <- {:public?, Visibility.is_public?(object)} do
conn
@@ -101,8 +102,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
conn
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),
{_, true} <- {:public?, Visibility.is_public?(activity)} do
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.Visibility

require Pleroma.Constants

@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do
@@ -120,6 +122,29 @@ defmodule Pleroma.Web.ActivityPub.Builder do
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()}
defp object_action(actor, object) do
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.User
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
@@ -84,6 +85,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
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
ChatMessageValidator.cast_and_apply(object)
end
@@ -116,7 +127,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do

def fetch_actor_and_object(object) do
fetch_actor(object)
Object.normalize(object["object"])
Object.normalize(object["object"], true)
:ok
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
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -44,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do

defp maybe_federate(%Activity{} = activity, meta) 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
Federator.publish(activity)


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

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

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

@relay_nickname "relay"
@@ -48,11 +49,11 @@ defmodule Pleroma.Web.ActivityPub.Relay do
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
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
error -> format_error(error)
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

# 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
with undone_object <- Activity.get_by_ap_id(undone_object),
: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

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),
{:ok, activity, _meta} <-
Pipeline.common_pipeline(data, local: false) do
@@ -683,21 +684,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end

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} =
data,
_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(
OAuthScopesPlug,
%{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(
@@ -136,7 +130,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
]
)

action_fallback(:errors)
action_fallback(AdminAPI.FallbackController)

def user_delete(conn, %{"nickname" => nickname}) do
user_delete(conn, %{"nicknames" => [nickname]})
@@ -597,16 +591,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
json_response(conn, :no_content, "")
else
{: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, _} ->
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

@@ -814,71 +802,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
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
{page, page_size} = page_params(params)

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

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)

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

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()

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

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

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)

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

defp configurable_from_database(conn) do
defp configurable_from_database do
if Config.get(:configurable_from_database) do
:ok
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

@@ -1159,30 +1079,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(%{"status_visibility" => count})
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
{get_page(params["page"]), get_page_size(params["page_size"])}
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.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

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

import Pleroma.Web.ApiSpec.Helpers

def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
@@ -17,10 +19,14 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do
%Operation{
tags: ["Notifications"],
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"]}],
operationId: "PleromaAPI.NotificationController.mark_as_read",
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

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


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

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

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
_ -> {:error, :not_found}
{:existing_announce, %Activity{} = announce} ->
{:ok, announce}

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



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

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

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]), []}
else
{mentioned_users, []}
@@ -395,10 +396,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def to_masto_date(_), 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
String.slice(name, 0..30) <> "…"
_ -> name
end
end

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

recipients ++ subscriber_ids
else
_e -> recipients
end
end

@@ -478,6 +483,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> User.get_followers()
|> Enum.map(& &1.ap_id)
|> Enum.concat(recipients)
else
_e -> recipients
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"
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
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
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()
},
stats: Pleroma.Stats.get_stats(),
thumbnail: Pleroma.Web.base_url() <> "/instance/thumbnail.jpeg",
thumbnail: instance_thumbnail(),
languages: ["en"],
registrations: Keyword.get(instance, :registrations_open),
# Extra (not present in Mastodon):
@@ -88,4 +88,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
end
|> Map.put(:enabled, Config.get([:instance, :federating]))
end

defp instance_thumbnail do
Pleroma.Config.get([:instance, :instance_thumbnail]) ||
"#{Pleroma.Web.base_url()}/instance/thumbnail.jpeg"
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)

def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
def object(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do
ActivityPubController.call(conn, :object)
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.get_create_by_object_ap_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do
@@ -54,13 +54,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
end
end

def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
def activity(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do
ActivityPubController.call(conn, :activity)
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)},
{_, true} <- {:public?, Visibility.is_public?(activity)} 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

action_fallback(Pleroma.Web.MastodonAPI.FallbackController)

def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
%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

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
render(conn, "show.json", notification: notification, for: user)
else
@@ -25,7 +25,7 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do
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 =
user
|> 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)
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)
post("/config", AdminAPIController, :config_update)
@@ -564,6 +564,10 @@ defmodule Pleroma.Web.Router do
get("/notice/:id", OStatus.OStatusController, :notice)
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.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}!"
})

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

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

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

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

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



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

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

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

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
test "user is deleted" do
clear_config([:instance, :federating], true)
user = insert(:user)

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
user = insert(:user)

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

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

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



+ 14
- 2
test/user_test.exs View File

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

{: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)

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

{:ok, like} = CommonAPI.favorite(user, activity_two.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, _user} = ObanHelpers.perform(job)
@@ -1777,4 +1777,16 @@ defmodule Pleroma.UserTest do
assert result.email_notifications["digest"] == false
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

+ 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 Oban.Testing, repo: Pleroma.Repo

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

import Pleroma.Factory

require Pleroma.Constants

setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
@@ -168,6 +173,60 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
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
test "it returns a json representation of the object with accept application/json", %{
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)

{: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_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_three, _} = CommonAPI.repeat(activity_two.id, friend)
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)

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

followed_user = insert(: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 =
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"})
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
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 =
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"])
{: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_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, 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)])

@@ -846,7 +846,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
booster = insert(:user)
{: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})

@@ -860,7 +860,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, _reblog_mute} = CommonAPI.hide_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})

@@ -868,75 +868,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
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
test "copies the file to the configured folder" do
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
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

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

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

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
activity = insert(:note_activity)
meta = [local: true]
@@ -83,5 +88,44 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
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

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

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

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

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

test_with_mock "returns announce activity and publish to federate",
Pleroma.Web.Federator,
[:passthrough],
[] do
Pleroma.Config.put([:instance, :federating], true)
clear_config([:instance, :federating], true)
service_actor = Relay.get_actor()
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["actor"] == service_actor.ap_id
assert activity.data["object"] == obj.data["id"]
assert called(Pleroma.Web.Federator.publish(activity))
end

@@ -117,13 +115,12 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
Pleroma.Web.Federator,
[:passthrough],
[] do
Pleroma.Config.put([:instance, :federating], false)
clear_config([:instance, :federating], false)
service_actor = Relay.get_actor()
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["actor"] == service_actor.ap_id
assert activity.data["object"] == obj.data["id"]
refute called(Pleroma.Web.Federator.publish(activity))
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, like} = CommonAPI.favorite(user, post.id)
{: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)
User.block(user, poster)

@@ -376,4 +376,61 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
assert chat
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

+ 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])

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
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>"
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
user = insert(:user)

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

{: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)

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

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

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

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

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

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



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
test "single user", %{admin: admin, conn: conn} do
user = insert(:user)
clear_config([:instance, :federating], true)

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

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

@@ -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")

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

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")

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

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

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

@@ -1413,7 +1420,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "with invalid token", %{conn: conn} do
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

@@ -1440,7 +1447,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "returns 404 when report id is invalid", %{conn: conn} do
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

@@ -1697,115 +1704,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
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
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")

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

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" => []})

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

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

@tag capture_log: true
test "doesn't set keys not in the whitelist", %{conn: conn} do
clear_config(:database_config_whitelist, [
{:pleroma, :key1},
@@ -2998,54 +2899,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
on_exit(fn -> Restarter.Pleroma.refresh() 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
setup do
user = insert(:user)
@@ -3096,7 +2949,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "excludes reblogs by default", %{conn: conn, user: user} do
other_user = insert(:user)
{: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")
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"})

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

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

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

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

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

with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, user)
@@ -410,6 +414,32 @@ defmodule Pleroma.Web.CommonAPITest do
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
user = insert(: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.repeat(activity.id, user)
{:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
assert Visibility.is_public?(announce_activity)
end

test "can't repeat a repeat" do
@@ -499,9 +530,9 @@ defmodule Pleroma.Web.CommonAPITest do
other_user = insert(:user)
{: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

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{} = announce_activity, _} =
{:ok, %Activity{} = announce_activity} =
CommonAPI.repeat(activity.id, user, %{visibility: "private"})

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

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

{: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

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"

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

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)

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

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

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

{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 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)

{: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 =
conn
@@ -375,7 +375,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do

test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
{: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")
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)

{: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 [] ==
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} =
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 =
conn
@@ -301,7 +301,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{: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)

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, create_activity} = CommonAPI.post(user, %{status: "hey"})
{: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)

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)
{:ok, _} = CommonAPI.favorite(user2, 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 =
build_conn()
@@ -917,7 +917,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
activity = insert(:note_activity)

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

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
other_user = insert(:user)
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)

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

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

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

test "does not return users who have reblogged the status privately", %{
conn: conn,
activity: activity
conn: conn
} do
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 =
conn
@@ -1486,7 +1486,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do

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

response =
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)
another_user = insert(:user)
{: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)
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)
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})

@@ -600,7 +600,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
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})



+ 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.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint

require Pleroma.Constants

setup_all do
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)

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
describe "GET /objects/:uuid (text/html)" 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"] == [
%{"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

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 =
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)

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

[response1, response2] =
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)

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
response =
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)

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."
})

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

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)
{: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}
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"})

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 Streamer.filtered_by_user?(user1, announce_activity)
end
@@ -440,7 +440,7 @@ defmodule Pleroma.Web.StreamerTest do

{:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
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 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 =~ user2.nickname
assert email.html_body =~ "cofe"
assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png"
end

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


Loading…
Cancel
Save