Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop

This commit is contained in:
sadposter 2019-08-07 08:59:31 +01:00
commit 325372b1e1
101 changed files with 2391 additions and 396 deletions

12
.dockerignore Normal file
View file

@ -0,0 +1,12 @@
.*
*.md
AGPL-3
CC-BY-SA-4.0
COPYING
*file
elixir_buildpack.config
docs/
test/
# Required to get version
!.git

View file

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Security
- OStatus: eliminate the possibility of a protocol downgrade attack.
- OStatus: prevent following locked accounts, bypassing the approval process.
### Changed ### Changed
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired - **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
@ -17,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed ### Fixed
- Not being able to pin unlisted posts - Not being able to pin unlisted posts
- Objects being re-embedded to activities after being updated (e.g faved/reposted). Running 'mix pleroma.database prune_objects' again is advised.
- Metadata rendering errors resulting in the entire page being inaccessible - Metadata rendering errors resulting in the entire page being inaccessible
- Federation/MediaProxy not working with instances that have wrong certificate order - Federation/MediaProxy not working with instances that have wrong certificate order
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`) - Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
@ -30,6 +35,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification. - ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
- ActivityPub S2S: remote user deletions now work the same as local user deletions. - ActivityPub S2S: remote user deletions now work the same as local user deletions.
- Not being able to access the Mastodon FE login page on private instances - Not being able to access the Mastodon FE login page on private instances
- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag
- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
- Report email not being sent to admins when the reporter is a remote user
### Added ### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`) - MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
@ -63,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- ActivityPub: Optional signing of ActivityPub object fetches. - ActivityPub: Optional signing of ActivityPub object fetches.
- Admin API: Endpoint for fetching latest user's statuses - Admin API: Endpoint for fetching latest user's statuses
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation. - Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
- Relays: Added a task to list relay subscriptions.
### Changed ### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
@ -70,6 +79,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- RichMedia: parsers and their order are configured in `rich_media` config. - RichMedia: parsers and their order are configured in `rich_media` config.
- RichMedia: add the rich media ttl based on image expiration time. - RichMedia: add the rich media ttl based on image expiration time.
### Removed
- Emoji: Remove longfox emojis.
- Remove `Reply-To` header from report emails for admins.
## [1.0.1] - 2019-07-14 ## [1.0.1] - 2019-07-14
### Security ### Security
- OStatus: fix an object spoofing vulnerability. - OStatus: fix an object spoofing vulnerability.

39
Dockerfile Normal file
View file

@ -0,0 +1,39 @@
FROM rinpatch/elixir:1.9.0-rc.0-alpine as build
COPY . .
ENV MIX_ENV=prod
RUN apk add git gcc g++ musl-dev make &&\
echo "import Mix.Config" > config/prod.secret.exs &&\
mix local.hex --force &&\
mix local.rebar --force &&\
mix deps.get --only prod &&\
mkdir release &&\
mix release --path release
FROM alpine:latest
ARG HOME=/opt/pleroma
ARG DATA=/var/lib/pleroma
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
apk update &&\
apk add ncurses postgresql-client &&\
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
mkdir -p ${DATA}/uploads &&\
mkdir -p ${DATA}/static &&\
chown -R pleroma ${DATA} &&\
mkdir -p /etc/pleroma &&\
chown -R pleroma /etc/pleroma
USER pleroma
COPY --from=build --chown=pleroma:0 /release ${HOME}
COPY ./config/docker.exs /etc/pleroma/config.exs
COPY ./docker-entrypoint.sh ${HOME}
EXPOSE 4000
ENTRYPOINT ["/opt/pleroma/docker-entrypoint.sh"]

View file

@ -21,7 +21,7 @@ If you want to run your own server, feel free to contact us at @lain@pleroma.soy
Currently Pleroma is not packaged by any OS/Distros, but feel free to reach out to us at [#pleroma-dev on freenode](https://webchat.freenode.net/?channels=%23pleroma-dev) or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma-dev:matrix.org> for assistance. If you want to change default options in your Pleroma package, please **discuss it with us first**. Currently Pleroma is not packaged by any OS/Distros, but feel free to reach out to us at [#pleroma-dev on freenode](https://webchat.freenode.net/?channels=%23pleroma-dev) or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma-dev:matrix.org> for assistance. If you want to change default options in your Pleroma package, please **discuss it with us first**.
### Docker ### Docker
While we dont provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://github.com/sn0w/pleroma-docker>. While we dont provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
### Dependencies ### Dependencies

68
config/docker.exs Normal file
View file

@ -0,0 +1,68 @@
import Config
config :pleroma, Pleroma.Web.Endpoint,
url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443],
http: [ip: {0, 0, 0, 0}, port: 4000]
config :pleroma, :instance,
name: System.get_env("INSTANCE_NAME", "Pleroma"),
email: System.get_env("ADMIN_EMAIL"),
notify_email: System.get_env("NOTIFY_EMAIL"),
limit: 5000,
registrations_open: false,
dynamic_configuration: true
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: System.get_env("DB_USER", "pleroma"),
password: System.fetch_env!("DB_PASS"),
database: System.get_env("DB_NAME", "pleroma"),
hostname: System.get_env("DB_HOST", "db"),
pool_size: 10
# Configure web push notifications
config :web_push_encryption, :vapid_details, subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
config :pleroma, :database, rum_enabled: false
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
# We can't store the secrets in this file, since this is baked into the docker image
if not File.exists?("/var/lib/pleroma/secret.exs") do
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
secret_file =
EEx.eval_string(
"""
import Config
config :pleroma, Pleroma.Web.Endpoint,
secret_key_base: "<%= secret %>",
signing_salt: "<%= signing_salt %>"
config :web_push_encryption, :vapid_details,
public_key: "<%= web_push_public_key %>",
private_key: "<%= web_push_private_key %>"
""",
secret: secret,
signing_salt: signing_salt,
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
)
File.write("/var/lib/pleroma/secret.exs", secret_file)
end
import_config("/var/lib/pleroma/secret.exs")
# For additional user config
if File.exists?("/var/lib/pleroma/config.exs"),
do: import_config("/var/lib/pleroma/config.exs"),
else:
File.write("/var/lib/pleroma/config.exs", """
import Config
# For additional configuration outside of environmental variables
""")

View file

@ -1,43 +1,2 @@
firefox, /emoji/Firefox.gif, Gif,Fun firefox, /emoji/Firefox.gif, Gif,Fun
blank, /emoji/blank.png, Fun blank, /emoji/blank.png, Fun
f_00b, /emoji/f_00b.png
f_00b11b, /emoji/f_00b11b.png
f_00b33b, /emoji/f_00b33b.png
f_00h, /emoji/f_00h.png
f_00t, /emoji/f_00t.png
f_01b, /emoji/f_01b.png
f_03b, /emoji/f_03b.png
f_10b, /emoji/f_10b.png
f_11b, /emoji/f_11b.png
f_11b00b, /emoji/f_11b00b.png
f_11b22b, /emoji/f_11b22b.png
f_11h, /emoji/f_11h.png
f_11t, /emoji/f_11t.png
f_12b, /emoji/f_12b.png
f_21b, /emoji/f_21b.png
f_22b, /emoji/f_22b.png
f_22b11b, /emoji/f_22b11b.png
f_22b33b, /emoji/f_22b33b.png
f_22h, /emoji/f_22h.png
f_22t, /emoji/f_22t.png
f_23b, /emoji/f_23b.png
f_30b, /emoji/f_30b.png
f_32b, /emoji/f_32b.png
f_33b, /emoji/f_33b.png
f_33b00b, /emoji/f_33b00b.png
f_33b22b, /emoji/f_33b22b.png
f_33h, /emoji/f_33h.png
f_33t, /emoji/f_33t.png
mayushii, /emoji/mayushii.png
hahaendme, /emoji/death.png
stabby, /emoji/stabs.png
endme, /emoji/end_me.png
fingerguns, /emoji/fingergun.png
marismug, /emoji/marismug.png
laffeydrink, /emoji/laffeydrink.png
kagaangry, /emoji/kagaangry.png
hammbite, /emoji/hammbite.png
longislandcry, /emoji/longislandcry.png
thinktirpitz, /emoji/thinktirpitz.png
shiratsuyusleep, /emoji/shiratsuyusleep.png
enterprisequestion, /emoji/enterprisequestion.png

View file

@ -29,7 +29,8 @@ config :pleroma, :instance,
email: "admin@example.com", email: "admin@example.com",
notify_email: "noreply@example.com", notify_email: "noreply@example.com",
skip_thread_containment: false, skip_thread_containment: false,
federating: false federating: false,
external_user_synchronization: false
config :pleroma, :activitypub, sign_object_fetches: false config :pleroma, :activitypub, sign_object_fetches: false

14
docker-entrypoint.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/ash
set -e
echo "-- Waiting for database..."
while ! pg_isready -U ${DB_USER:-pleroma} -d postgres://${DB_HOST:-db}:5432/${DB_NAME:-pleroma} -t 1; do
sleep 1s
done
echo "-- Running migrations..."
$HOME/bin/pleroma_ctl migrate
echo "-- Starting!"
exec $HOME/bin/pleroma start

View file

@ -627,6 +627,9 @@ Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
Keywords can be passed as lists with 2 child tuples, e.g. Keywords can be passed as lists with 2 child tuples, e.g.
`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`. `[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
If value contains list of settings `[subkey: val1, subkey2: val2, subkey3: val3]`, it's possible to remove only subkeys instead of all settings passing `subkeys` parameter. E.g.:
{"group": "pleroma", "key": "some_key", "delete": "true", "subkeys": [":subkey", ":subkey3"]}.
Compile time settings (need instance reboot): Compile time settings (need instance reboot):
- all settings by this keys: - all settings by this keys:
- `:hackney_pools` - `:hackney_pools`
@ -645,6 +648,7 @@ Compile time settings (need instance reboot):
- `key` (string or string with leading `:` for atoms) - `key` (string or string with leading `:` for atoms)
- `value` (string, [], {} or {"tuple": []}) - `value` (string, [], {} or {"tuple": []})
- `delete` = true (optional, if parameter must be deleted) - `delete` = true (optional, if parameter must be deleted)
- `subkeys` [(string with leading `:` for atoms)] (optional, works only if `delete=true` parameter is passed, otherwise will be ignored)
] ]
- Request (example): - Request (example):

View file

@ -25,7 +25,7 @@ At this time, write CNAME to CDN in public_endpoint.
## Pleroma.Upload.Filter.Mogrify ## Pleroma.Upload.Filter.Mogrify
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"impode", "1"}]`. * `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.
## Pleroma.Upload.Filter.Dedupe ## Pleroma.Upload.Filter.Dedupe

View file

@ -1,8 +1,8 @@
# How to activate mediaproxy # How to activate mediaproxy
## Explanation ## Explanation
Without the `mediaproxy` function, Pleroma don't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server. Without the `mediaproxy` function, Pleroma doesn't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
With the `mediaproxy` function you can use the cache ability of nginx, to cache these content, so user can access it faster, cause it's loaded from your server. With the `mediaproxy` function you can use nginx to cache this content, so users can access it faster, because it's loaded from your server.
## Activate it ## Activate it

View file

@ -5,6 +5,7 @@
defmodule Mix.Tasks.Pleroma.Relay do defmodule Mix.Tasks.Pleroma.Relay do
use Mix.Task use Mix.Task
import Mix.Pleroma import Mix.Pleroma
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Relay
@shortdoc "Manages remote relays" @shortdoc "Manages remote relays"
@ -22,6 +23,10 @@ defmodule Mix.Tasks.Pleroma.Relay do
``mix pleroma.relay unfollow <relay_url>`` ``mix pleroma.relay unfollow <relay_url>``
Example: ``mix pleroma.relay unfollow https://example.org/relay`` Example: ``mix pleroma.relay unfollow https://example.org/relay``
## List relay subscriptions
``mix pleroma.relay list``
""" """
def run(["follow", target]) do def run(["follow", target]) do
start_pleroma() start_pleroma()
@ -44,4 +49,19 @@ defmodule Mix.Tasks.Pleroma.Relay do
{:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}") {:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}")
end end
end end
def run(["list"]) do
start_pleroma()
with %User{} = user <- Relay.get_actor() do
user.following
|> Enum.each(fn entry ->
URI.parse(entry)
|> Map.get(:host)
|> shell_info()
end)
else
e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
end
end
end end

View file

@ -31,8 +31,8 @@ defmodule Mix.Tasks.Pleroma.User do
mix pleroma.user invite [OPTION...] mix pleroma.user invite [OPTION...]
Options: Options:
- `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05") - `--expires-at DATE` - last day on which token is active (e.g. "2019-04-05")
- `--max_use NUMBER` - maximum numbers of token uses - `--max-use NUMBER` - maximum numbers of token uses
## List generated invites ## List generated invites

View file

@ -63,7 +63,6 @@ defmodule Pleroma.Emails.AdminEmail do
new() new()
|> to({to.name, to.email}) |> to({to.name, to.email})
|> from({instance_name(), instance_notify_email()}) |> from({instance_name(), instance_notify_email()})
|> reply_to({reporter.name, reporter.email})
|> subject("#{instance_name()} Report") |> subject("#{instance_name()} Report")
|> html_body(html_body) |> html_body(html_body)
end end

View file

@ -66,6 +66,16 @@ defmodule Pleroma.FlakeId do
@spec get :: binary @spec get :: binary
def get, do: to_string(:gen_server.call(:flake, :get)) def get, do: to_string(:gen_server.call(:flake, :get))
# checks that ID is is valid FlakeID
#
@spec is_flake_id?(String.t()) :: boolean
def is_flake_id?(id), do: is_flake_id?(String.to_charlist(id), true)
defp is_flake_id?([c | cs], true) when c >= ?0 and c <= ?9, do: is_flake_id?(cs, true)
defp is_flake_id?([c | cs], true) when c >= ?A and c <= ?Z, do: is_flake_id?(cs, true)
defp is_flake_id?([c | cs], true) when c >= ?a and c <= ?z, do: is_flake_id?(cs, true)
defp is_flake_id?([], true), do: true
defp is_flake_id?(_, _), do: false
# -- Ecto.Type API # -- Ecto.Type API
@impl Ecto.Type @impl Ecto.Type
def type, do: :uuid def type, do: :uuid

View file

@ -114,7 +114,7 @@ defmodule Pleroma.Object.Fetcher do
end end
end end
def fetch_and_contain_remote_object_from_id(id) do def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
Logger.info("Fetching object #{id} via AP") Logger.info("Fetching object #{id} via AP")
date = date =
@ -141,4 +141,9 @@ defmodule Pleroma.Object.Fetcher do
{:error, e} {:error, e}
end end
end end
def fetch_and_contain_remote_object_from_id(%{"id" => id}),
do: fetch_and_contain_remote_object_from_id(id)
def fetch_and_contain_remote_object_from_id(_id), do: {:error, "id must be a string"}
end end

View file

@ -114,7 +114,9 @@ defmodule Pleroma.User do
def user_info(%User{} = user, args \\ %{}) do def user_info(%User{} = user, args \\ %{}) do
following_count = following_count =
if args[:following_count], do: args[:following_count], else: following_count(user) if args[:following_count],
do: args[:following_count],
else: user.info.following_count || following_count(user)
follower_count = follower_count =
if args[:follower_count], do: args[:follower_count], else: user.info.follower_count if args[:follower_count], do: args[:follower_count], else: user.info.follower_count
@ -226,6 +228,7 @@ defmodule Pleroma.User do
|> put_password_hash |> put_password_hash
end end
@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def reset_password(%User{id: user_id} = user, data) do def reset_password(%User{id: user_id} = user, data) do
multi = multi =
Multi.new() Multi.new()
@ -330,6 +333,7 @@ defmodule Pleroma.User do
def needs_update?(_), do: true def needs_update?(_), do: true
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
{:ok, follower} {:ok, follower}
end end
@ -404,6 +408,8 @@ defmodule Pleroma.User do
{1, [follower]} = Repo.update_all(q, []) {1, [follower]} = Repo.update_all(q, [])
follower = maybe_update_following_count(follower)
{:ok, _} = update_follower_count(followed) {:ok, _} = update_follower_count(followed)
set_cache(follower) set_cache(follower)
@ -423,6 +429,8 @@ defmodule Pleroma.User do
{1, [follower]} = Repo.update_all(q, []) {1, [follower]} = Repo.update_all(q, [])
follower = maybe_update_following_count(follower)
{:ok, followed} = update_follower_count(followed) {:ok, followed} = update_follower_count(followed)
set_cache(follower) set_cache(follower)
@ -707,7 +715,35 @@ defmodule Pleroma.User do
|> update_and_set_cache() |> update_and_set_cache()
end end
def maybe_fetch_follow_information(user) do
with {:ok, user} <- fetch_follow_information(user) do
user
else
e ->
Logger.error("Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}")
user
end
end
def fetch_follow_information(user) do
with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
info_cng = User.Info.follow_information_update(user.info, info)
changeset =
user
|> change()
|> put_embed(:info, info_cng)
update_and_set_cache(changeset)
else
{:error, _} = e -> e
e -> {:error, e}
end
end
def update_follower_count(%User{} = user) do def update_follower_count(%User{} = user) do
if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
follower_count_query = follower_count_query =
User.Query.build(%{followers: user, deactivated: false}) User.Query.build(%{followers: user, deactivated: false})
|> select([u], %{count: count(u.id)}) |> select([u], %{count: count(u.id)})
@ -731,7 +767,20 @@ defmodule Pleroma.User do
{1, [user]} -> set_cache(user) {1, [user]} -> set_cache(user)
_ -> {:error, user} _ -> {:error, user}
end end
else
{:ok, maybe_fetch_follow_information(user)}
end end
end
def maybe_update_following_count(%User{local: false} = user) do
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
{:ok, maybe_fetch_follow_information(user)}
else
user
end
end
def maybe_update_following_count(user), do: user
def remove_duplicated_following(%User{following: following} = user) do def remove_duplicated_following(%User{following: following} = user) do
uniq_following = Enum.uniq(following) uniq_following = Enum.uniq(following)

View file

@ -16,6 +16,8 @@ defmodule Pleroma.User.Info do
field(:source_data, :map, default: %{}) field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0) field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0) field(:follower_count, :integer, default: 0)
# Should be filled in only for remote users
field(:following_count, :integer, default: nil)
field(:locked, :boolean, default: false) field(:locked, :boolean, default: false)
field(:confirmation_pending, :boolean, default: false) field(:confirmation_pending, :boolean, default: false)
field(:confirmation_token, :string, default: nil) field(:confirmation_token, :string, default: nil)
@ -223,7 +225,11 @@ defmodule Pleroma.User.Info do
:uri, :uri,
:hub, :hub,
:topic, :topic,
:salmon :salmon,
:hide_followers,
:hide_follows,
:follower_count,
:following_count
]) ])
end end
@ -234,7 +240,11 @@ defmodule Pleroma.User.Info do
:source_data, :source_data,
:banner, :banner,
:locked, :locked,
:magic_key :magic_key,
:follower_count,
:following_count,
:hide_follows,
:hide_followers
]) ])
end end
@ -348,4 +358,14 @@ defmodule Pleroma.User.Info do
cast(info, params, [:muted_reblogs]) cast(info, params, [:muted_reblogs])
end end
def follow_information_update(info, params) do
info
|> cast(params, [
:hide_followers,
:hide_follows,
:follower_count,
:following_count
])
end
end end

View file

@ -44,7 +44,7 @@ defmodule Pleroma.User.Search do
query_string = String.trim_leading(query_string, "@") query_string = String.trim_leading(query_string, "@")
with [name, domain] <- String.split(query_string, "@"), with [name, domain] <- String.split(query_string, "@"),
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do
name <> "@" <> to_string(:idna.encode(formatted_domain)) name <> "@" <> to_string(:idna.encode(formatted_domain))
else else
_ -> query_string _ -> query_string

View file

@ -267,6 +267,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
else else
{:fake, true, activity} -> {:fake, true, activity} ->
{:ok, activity} {:ok, activity}
{:error, message} ->
{:error, message}
end end
end end
@ -746,8 +749,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
from( from(
activity in query, [_activity, object] in query,
where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data) where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
) )
end end
@ -1009,10 +1012,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
user_data = %{ user_data = %{
ap_id: data["id"], ap_id: data["id"],
info: %{ info: %{
"ap_enabled" => true, ap_enabled: true,
"source_data" => data, source_data: data,
"banner" => banner, banner: banner,
"locked" => locked locked: locked
}, },
avatar: avatar, avatar: avatar,
name: data["name"], name: data["name"],
@ -1036,6 +1039,71 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, user_data} {:ok, user_data}
end end
def fetch_follow_information_for_user(user) do
with {:ok, following_data} <-
Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
following_count when is_integer(following_count) <- following_data["totalItems"],
{:ok, hide_follows} <- collection_private(following_data),
{:ok, followers_data} <-
Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
followers_count when is_integer(followers_count) <- followers_data["totalItems"],
{:ok, hide_followers} <- collection_private(followers_data) do
{:ok,
%{
hide_follows: hide_follows,
follower_count: followers_count,
following_count: following_count,
hide_followers: hide_followers
}}
else
{:error, _} = e ->
e
e ->
{:error, e}
end
end
defp maybe_update_follow_information(data) do
with {:enabled, true} <-
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
{:ok, info} <- fetch_follow_information_for_user(data) do
info = Map.merge(data.info, info)
Map.put(data, :info, info)
else
{:enabled, false} ->
data
e ->
Logger.error(
"Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
)
data
end
end
defp collection_private(data) do
if is_map(data["first"]) and
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
{:ok, false}
else
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
{:ok, false}
else
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
{:ok, true}
{:error, _} = e ->
e
e ->
{:error, e}
end
end
end
def user_data_from_user_object(data) do def user_data_from_user_object(data) do
with {:ok, data} <- MRF.filter(data), with {:ok, data} <- MRF.filter(data),
{:ok, data} <- object_to_user_data(data) do {:ok, data} <- object_to_user_data(data) do
@ -1047,7 +1115,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_and_prepare_user_from_ap_id(ap_id) do def fetch_and_prepare_user_from_ap_id(ap_id) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id), with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
{:ok, data} <- user_data_from_user_object(data) do {:ok, data} <- user_data_from_user_object(data),
data <- maybe_update_follow_information(data) do
{:ok, data} {:ok, data}
else else
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}") e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")

View file

@ -608,13 +608,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
banner = new_user_data[:info]["banner"] banner = new_user_data[:info][:banner]
locked = new_user_data[:info]["locked"] || false locked = new_user_data[:info][:locked] || false
update_data = update_data =
new_user_data new_user_data
|> Map.take([:name, :bio, :avatar]) |> Map.take([:name, :bio, :avatar])
|> Map.put(:info, %{"banner" => banner, "locked" => locked}) |> Map.put(:info, %{banner: banner, locked: locked})
actor actor
|> User.upgrade_changeset(update_data) |> User.upgrade_changeset(update_data)
@ -1076,10 +1076,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user]) PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
end end
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
update_following_followers_counters(user)
end
{:ok, user} {:ok, user}
else else
%User{} = user -> {:ok, user} %User{} = user -> {:ok, user}
@ -1112,27 +1108,4 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
data data
|> maybe_fix_user_url |> maybe_fix_user_url
end end
def update_following_followers_counters(user) do
info = %{}
following = fetch_counter(user.following_address)
info = if following, do: Map.put(info, :following_count, following), else: info
followers = fetch_counter(user.follower_address)
info = if followers, do: Map.put(info, :follower_count, followers), else: info
User.set_info_cache(user, info)
end
defp fetch_counter(url) do
with {:ok, %{body: body, status: code}} when code in 200..299 <-
Pleroma.HTTP.get(
url,
[{:Accept, "application/activity+json"}]
),
{:ok, data} <- Jason.decode(body) do
data["totalItems"]
end
end
end end

View file

@ -251,20 +251,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def insert_full_object(map), do: {:ok, map, nil} def insert_full_object(map), do: {:ok, map, nil}
def update_object_in_activities(%{data: %{"id" => id}} = object) do
# TODO
# Update activities that already had this. Could be done in a seperate process.
# Alternatively, just don't do this and fetch the current object each time. Most
# could probably be taken from cache.
relevant_activities = Activity.get_all_create_by_object_ap_id(id)
Enum.map(relevant_activities, fn activity ->
new_activity_data = activity.data |> Map.put("object", object.data)
changeset = Changeset.change(activity, data: new_activity_data)
Repo.update(changeset)
end)
end
#### Like-related helpers #### Like-related helpers
@doc """ @doc """
@ -347,8 +333,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Map.put("#{property}_count", length(element)) |> Map.put("#{property}_count", length(element))
|> Map.put("#{property}s", element), |> Map.put("#{property}s", element),
changeset <- Changeset.change(object, data: new_data), changeset <- Changeset.change(object, data: new_data),
{:ok, object} <- Object.update_and_set_cache(changeset), {:ok, object} <- Object.update_and_set_cache(changeset) do
_ <- update_object_in_activities(object) do
{:ok, object} {:ok, object}
end end
end end

View file

@ -66,8 +66,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
"orderedItems" => items "orderedItems" => items
} }
if offset < total do if offset + length(items) < total do
Map.put(map, "next", "#{iri}?page=#{page + 1}") Map.put(map, "next", "#{iri}?page=#{page + 1}")
else
map
end end
end end
end end

View file

@ -65,7 +65,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
do: render("service.json", %{user: user}) do: render("service.json", %{user: user})
def render("user.json", %{user: %User{nickname: "internal." <> _} = user}), def render("user.json", %{user: %User{nickname: "internal." <> _} = user}),
do: render("service.json", %{user: user}) do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
def render("user.json", %{user: user}) do def render("user.json", %{user: user}) do
{:ok, user} = User.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)

View file

@ -402,9 +402,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
if Pleroma.Config.get([:instance, :dynamic_configuration]) do if Pleroma.Config.get([:instance, :dynamic_configuration]) do
updated = updated =
Enum.map(configs, fn Enum.map(configs, fn
%{"group" => group, "key" => key, "delete" => "true"} -> %{"group" => group, "key" => key, "delete" => "true"} = params ->
{:ok, _} = Config.delete(%{group: group, key: key}) {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
nil config
%{"group" => group, "key" => key, "value" => value} -> %{"group" => group, "key" => key, "value" => value} ->
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value}) {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})

View file

@ -55,8 +55,19 @@ defmodule Pleroma.Web.AdminAPI.Config do
@spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
def delete(params) do def delete(params) do
with %Config{} = config <- Config.get_by_params(params) do with %Config{} = config <- Config.get_by_params(Map.delete(params, :subkeys)) do
if params[:subkeys] do
updated_value =
Keyword.drop(
:erlang.binary_to_term(config.value),
Enum.map(params[:subkeys], &do_transform_string(&1))
)
Config.update(config, %{value: updated_value})
else
Repo.delete(config) Repo.delete(config)
{:ok, nil}
end
else else
nil -> nil ->
err = err =

View file

@ -24,7 +24,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
# This is a hack for twidere. # This is a hack for twidere.
def get_by_id_or_ap_id(id) do def get_by_id_or_ap_id(id) do
activity = activity =
Activity.get_by_id_with_object(id) || Activity.get_create_by_object_ap_id_with_object(id) with true <- Pleroma.FlakeId.is_flake_id?(id),
%Activity{} = activity <- Activity.get_by_id_with_object(id) do
activity
else
_ -> Activity.get_create_by_object_ap_id_with_object(id)
end
activity && activity &&
if activity.data["type"] == "Create" do if activity.data["type"] == "Create" do
@ -42,26 +47,43 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def get_replied_to_activity(_), do: nil def get_replied_to_activity(_), do: nil
def attachments_from_ids(data) do def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do
if Map.has_key?(data, "descriptions") do attachments_from_ids_descs(ids, desc)
attachments_from_ids_descs(data["media_ids"], data["descriptions"])
else
attachments_from_ids_no_descs(data["media_ids"])
end
end end
def attachments_from_ids_no_descs(ids) do def attachments_from_ids(%{"media_ids" => ids} = _) do
Enum.map(ids || [], fn media_id -> attachments_from_ids_no_descs(ids)
Repo.get(Object, media_id).data
end)
end end
def attachments_from_ids(_), do: []
def attachments_from_ids_no_descs([]), do: []
def attachments_from_ids_no_descs(ids) do
Enum.map(ids, fn media_id ->
case Repo.get(Object, media_id) do
%Object{data: data} = _ -> data
_ -> nil
end
end)
|> Enum.filter(& &1)
end
def attachments_from_ids_descs([], _), do: []
def attachments_from_ids_descs(ids, descs_str) do def attachments_from_ids_descs(ids, descs_str) do
{_, descs} = Jason.decode(descs_str) {_, descs} = Jason.decode(descs_str)
Enum.map(ids || [], fn media_id -> Enum.map(ids, fn media_id ->
Map.put(Repo.get(Object, media_id).data, "name", descs[media_id]) case Repo.get(Object, media_id) do
%Object{data: data} = _ ->
Map.put(data, "name", descs[media_id])
_ ->
nil
end
end) end)
|> Enum.filter(& &1)
end end
@spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) :: @spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
@ -242,20 +264,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end end
def add_attachments(text, attachments) do def add_attachments(text, attachments) do
attachment_text = attachment_text = Enum.map(attachments, &build_attachment_link/1)
Enum.map(attachments, fn Enum.join([text | attachment_text], "<br>")
%{"url" => [%{"href" => href} | _]} = attachment -> end
defp build_attachment_link(%{"url" => [%{"href" => href} | _]} = attachment) do
name = attachment["name"] || URI.decode(Path.basename(href)) name = attachment["name"] || URI.decode(Path.basename(href))
href = MediaProxy.url(href) href = MediaProxy.url(href)
"<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>" "<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>"
_ ->
""
end)
Enum.join([text | attachment_text], "<br>")
end end
defp build_attachment_link(_), do: ""
def format_input(text, format, options \\ []) def format_input(text, format, options \\ [])
@doc """ @doc """
@ -315,7 +335,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
sensitive \\ false, sensitive \\ false,
merge \\ %{} merge \\ %{}
) do ) do
object = %{ %{
"type" => "Note", "type" => "Note",
"to" => to, "to" => to,
"cc" => cc, "cc" => cc,
@ -325,18 +345,20 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"context" => context, "context" => context,
"attachment" => attachments, "attachment" => attachments,
"actor" => actor, "actor" => actor,
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq() "tag" => Keyword.values(tags) |> Enum.uniq()
} }
|> add_in_reply_to(in_reply_to)
|> Map.merge(merge)
end
object = defp add_in_reply_to(object, nil), do: object
with false <- is_nil(in_reply_to),
%Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do defp add_in_reply_to(object, in_reply_to) do
with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
Map.put(object, "inReplyTo", in_reply_to_object.data["id"]) Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
else else
_ -> object _ -> object
end end
Map.merge(object, merge)
end end
def format_naive_asctime(date) do def format_naive_asctime(date) do
@ -368,17 +390,16 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
end end
def to_masto_date(date) do def to_masto_date(date) when is_binary(date) do
try do with {:ok, date} <- NaiveDateTime.from_iso8601(date) do
date to_masto_date(date)
|> NaiveDateTime.from_iso8601!() else
|> NaiveDateTime.to_iso8601() _ -> ""
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
rescue
_e -> ""
end end
end end
def to_masto_date(_), do: ""
defp shortname(name) do defp shortname(name) do
if String.length(name) < 30 do if String.length(name) < 30 do
name name
@ -423,7 +444,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
object_data = object_data =
cond do cond do
!is_nil(object) -> not is_nil(object) ->
object.data object.data
is_map(data["object"]) -> is_map(data["object"]) ->
@ -467,9 +488,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def maybe_extract_mentions(%{"tag" => tag}) do def maybe_extract_mentions(%{"tag" => tag}) do
tag tag
|> Enum.filter(fn x -> is_map(x) end) |> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|> Enum.filter(fn x -> x["type"] == "Mention" end)
|> Enum.map(fn x -> x["href"] end) |> Enum.map(fn x -> x["href"] end)
|> Enum.uniq()
end end
def maybe_extract_mentions(_), do: [] def maybe_extract_mentions(_), do: []

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.MediaProxy do defmodule Pleroma.Web.MediaProxy do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Upload
alias Pleroma.Web alias Pleroma.Web
@base64_opts [padding: false] @base64_opts [padding: false]
@ -26,7 +27,18 @@ defmodule Pleroma.Web.MediaProxy do
defp whitelisted?(url) do defp whitelisted?(url) do
%{host: domain} = URI.parse(url) %{host: domain} = URI.parse(url)
Enum.any?(Config.get([:media_proxy, :whitelist]), fn pattern -> mediaproxy_whitelist = Config.get([:media_proxy, :whitelist])
upload_base_url_domain =
if !is_nil(Config.get([Upload, :base_url])) do
[URI.parse(Config.get([Upload, :base_url])).host]
else
[]
end
whitelist = mediaproxy_whitelist ++ upload_base_url_domain
Enum.any?(whitelist, fn pattern ->
String.equivalent?(domain, pattern) String.equivalent?(domain, pattern)
end) end)
end end

View file

@ -183,6 +183,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
retweeted_object = Object.normalize(retweeted_activity)
retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"]) retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true) retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
@ -197,7 +198,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']},
{:id, h.(activity.data["id"])}, {:id, h.(activity.data["id"])},
{:title, ['#{user.nickname} repeated a notice']}, {:title, ['#{user.nickname} repeated a notice']},
{:content, [type: 'html'], ['RT #{retweeted_activity.data["object"]["content"]}']}, {:content, [type: 'html'], ['RT #{retweeted_object.data["content"]}']},
{:published, h.(inserted_at)}, {:published, h.(inserted_at)},
{:updated, h.(updated_at)}, {:updated, h.(updated_at)},
{:"ostatus:conversation", [ref: h.(activity.data["context"])], {:"ostatus:conversation", [ref: h.(activity.data["context"])],

View file

@ -9,14 +9,18 @@ defmodule Pleroma.Web.OStatus.FollowHandler do
alias Pleroma.Web.XML alias Pleroma.Web.XML
def handle(entry, doc) do def handle(entry, doc) do
with {:ok, actor} <- OStatus.find_make_or_update_user(doc), with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
followed_uri when not is_nil(followed_uri) <- followed_uri when not is_nil(followed_uri) <-
XML.string_from_xpath("/entry/activity:object/id", entry), XML.string_from_xpath("/entry/activity:object/id", entry),
{:ok, followed} <- OStatus.find_or_make_user(followed_uri), {:ok, followed} <- OStatus.find_or_make_user(followed_uri),
{:locked, false} <- {:locked, followed.info.locked},
{:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
User.follow(actor, followed) User.follow(actor, followed)
{:ok, activity} {:ok, activity}
else
{:locked, true} ->
{:error, "It's not possible to follow locked accounts over OStatus"}
end end
end end
end end

View file

@ -111,7 +111,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
with id <- XML.string_from_xpath("//id", entry), with id <- XML.string_from_xpath("//id", entry),
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id), activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
[author] <- :xmerl_xpath.string('//author[1]', doc), [author] <- :xmerl_xpath.string('//author[1]', doc),
{:ok, actor} <- OStatus.find_make_or_update_user(author), {:ok, actor} <- OStatus.find_make_or_update_actor(author),
content_html <- OStatus.get_content(entry), content_html <- OStatus.get_content(entry),
cw <- OStatus.get_cw(entry), cw <- OStatus.get_cw(entry),
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry), in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.OStatus.UnfollowHandler do
alias Pleroma.Web.XML alias Pleroma.Web.XML
def handle(entry, doc) do def handle(entry, doc) do
with {:ok, actor} <- OStatus.find_make_or_update_user(doc), with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
followed_uri when not is_nil(followed_uri) <- followed_uri when not is_nil(followed_uri) <-
XML.string_from_xpath("/entry/activity:object/id", entry), XML.string_from_xpath("/entry/activity:object/id", entry),

View file

@ -56,7 +56,7 @@ defmodule Pleroma.Web.OStatus do
def handle_incoming(xml_string, options \\ []) do def handle_incoming(xml_string, options \\ []) do
with doc when doc != :error <- parse_document(xml_string) do with doc when doc != :error <- parse_document(xml_string) do
with {:ok, actor_user} <- find_make_or_update_user(doc), with {:ok, actor_user} <- find_make_or_update_actor(doc),
do: Pleroma.Instances.set_reachable(actor_user.ap_id) do: Pleroma.Instances.set_reachable(actor_user.ap_id)
entries = :xmerl_xpath.string('//entry', doc) entries = :xmerl_xpath.string('//entry', doc)
@ -120,7 +120,7 @@ defmodule Pleroma.Web.OStatus do
end end
def make_share(entry, doc, retweeted_activity) do def make_share(entry, doc, retweeted_activity) do
with {:ok, actor} <- find_make_or_update_user(doc), with {:ok, actor} <- find_make_or_update_actor(doc),
%Object{} = object <- Object.normalize(retweeted_activity), %Object{} = object <- Object.normalize(retweeted_activity),
id when not is_nil(id) <- string_from_xpath("/entry/id", entry), id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
{:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do {:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
@ -138,7 +138,7 @@ defmodule Pleroma.Web.OStatus do
end end
def make_favorite(entry, doc, favorited_activity) do def make_favorite(entry, doc, favorited_activity) do
with {:ok, actor} <- find_make_or_update_user(doc), with {:ok, actor} <- find_make_or_update_actor(doc),
%Object{} = object <- Object.normalize(favorited_activity), %Object{} = object <- Object.normalize(favorited_activity),
id when not is_nil(id) <- string_from_xpath("/entry/id", entry), id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
{:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do {:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
@ -264,11 +264,18 @@ defmodule Pleroma.Web.OStatus do
end end
end end
def find_make_or_update_user(doc) do def find_make_or_update_actor(doc) do
uri = string_from_xpath("//author/uri[1]", doc) uri = string_from_xpath("//author/uri[1]", doc)
with {:ok, user} <- find_or_make_user(uri) do with {:ok, %User{} = user} <- find_or_make_user(uri),
{:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do
maybe_update(doc, user) maybe_update(doc, user)
else
{:ap_enabled, true} ->
{:error, :invalid_protocol}
_ ->
{:error, :unknown_user}
end end
end end

View file

@ -3,13 +3,20 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
alias Pleroma.Web.RichMedia.Parsers.MetaTagsParser
@spec parse(String.t(), map()) :: {:ok, map()} | {:error, String.t()}
def parse(html, data) do def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse( data
html, |> parse_name_attrs(html)
data, |> parse_property_attrs(html)
"twitter", end
"No twitter card metadata found",
"name" defp parse_name_attrs(data, html) do
) MetaTagsParser.parse(html, data, "twitter", %{}, "name")
end
defp parse_property_attrs({_, data}, html) do
MetaTagsParser.parse(html, data, "twitter", "No twitter card metadata found", "property")
end end
end end

View file

@ -36,6 +36,11 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
a {
color: color: #d8a070;
text-decoration: none;
}
form { form {
width: 100%; width: 100%;
} }

View file

@ -18,6 +18,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.WebFinger alias Pleroma.Web.WebFinger
plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
def help_test(conn, _params) do def help_test(conn, _params) do
json(conn, "ok") json(conn, "ok")
end end
@ -58,27 +60,25 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"]) %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
redirect(conn, to: "/notice/#{activity_id}") redirect(conn, to: "/notice/#{activity_id}")
else else
{err, followee} = User.get_or_fetch(acct) with {:ok, followee} <- User.get_or_fetch(acct) do
avatar = User.avatar_url(followee)
name = followee.nickname
id = followee.id
if !!user do
conn conn
|> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id}) |> render(follow_template(user), %{
else
conn
|> render("follow_login.html", %{
error: false, error: false,
acct: acct, acct: acct,
avatar: avatar, avatar: User.avatar_url(followee),
name: name, name: followee.nickname,
id: id id: followee.id
}) })
else
{:error, _reason} ->
render(conn, follow_template(user), %{error: :error})
end end
end end
end end
defp follow_template(%User{} = _user), do: "follow.html"
defp follow_template(_), do: "follow_login.html"
defp is_status?(acct) do defp is_status?(acct) do
case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
{:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] -> {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
@ -92,48 +92,53 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def do_remote_follow(conn, %{ def do_remote_follow(conn, %{
"authorization" => %{"name" => username, "password" => password, "id" => id} "authorization" => %{"name" => username, "password" => password, "id" => id}
}) do }) do
followee = User.get_cached_by_id(id) with %User{} = followee <- User.get_cached_by_id(id),
avatar = User.avatar_url(followee) {_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee},
name = followee.nickname {_, true, _} <- {
:auth,
with %User{} = user <- User.get_cached_by_nickname(username), AuthenticationPlug.checkpw(password, user.password_hash),
true <- AuthenticationPlug.checkpw(password, user.password_hash), followee
%User{} = _followed <- User.get_cached_by_id(id), },
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
conn conn
|> render("followed.html", %{error: false}) |> render("followed.html", %{error: false})
else else
# Was already following user # Was already following user
{:error, "Could not follow user:" <> _rest} -> {:error, "Could not follow user:" <> _rest} ->
render(conn, "followed.html", %{error: false}) render(conn, "followed.html", %{error: "Error following account"})
_e -> {:auth, _, followee} ->
conn conn
|> render("follow_login.html", %{ |> render("follow_login.html", %{
error: "Wrong username or password", error: "Wrong username or password",
id: id, id: id,
name: name, name: followee.nickname,
avatar: avatar avatar: User.avatar_url(followee)
}) })
e ->
Logger.debug("Remote follow failed with error #{inspect(e)}")
render(conn, "followed.html", %{error: "Something went wrong."})
end end
end end
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
with %User{} = followee <- User.get_cached_by_id(id), with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
conn conn
|> render("followed.html", %{error: false}) |> render("followed.html", %{error: false})
else else
# Was already following user # Was already following user
{:error, "Could not follow user:" <> _rest} -> {:error, "Could not follow user:" <> _rest} ->
conn render(conn, "followed.html", %{error: "Error following account"})
|> render("followed.html", %{error: false})
{:fetch_user, error} ->
Logger.debug("Remote follow failed with error #{inspect(error)}")
render(conn, "followed.html", %{error: "Could not find user"})
e -> e ->
Logger.debug("Remote follow failed with error #{inspect(e)}") Logger.debug("Remote follow failed with error #{inspect(e)}")
render(conn, "followed.html", %{error: "Something went wrong."})
conn
|> render("followed.html", %{error: inspect(e)})
end end
end end
@ -148,11 +153,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end end
end end
def config(conn, _params) do def config(%{assigns: %{format: "xml"}} = conn, _params) do
instance = Pleroma.Config.get(:instance) instance = Pleroma.Config.get(:instance)
case get_format(conn) do
"xml" ->
response = """ response = """
<config> <config>
<site> <site>
@ -167,8 +170,11 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
conn conn
|> put_resp_content_type("application/xml") |> put_resp_content_type("application/xml")
|> send_resp(200, response) |> send_resp(200, response)
end
def config(conn, _params) do
instance = Pleroma.Config.get(:instance)
_ ->
vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
uploadlimit = %{ uploadlimit = %{
@ -184,30 +190,32 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
server: Web.base_url(), server: Web.base_url(),
textlimit: to_string(Keyword.get(instance, :limit)), textlimit: to_string(Keyword.get(instance, :limit)),
uploadlimit: uploadlimit, uploadlimit: uploadlimit,
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"), closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
private: if(Keyword.get(instance, :public, true), do: "0", else: "1"), private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
vapidPublicKey: vapid_public_key, vapidPublicKey: vapid_public_key,
accountActivationRequired: accountActivationRequired:
if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"), bool_to_val(Keyword.get(instance, :account_activation_required, false)),
invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"), invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
safeDMMentionsEnabled: safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
} }
pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
managed_config = Keyword.get(instance, :managed_config) managed_config = Keyword.get(instance, :managed_config)
data = data =
if managed_config do if managed_config do
data |> Map.put("pleromafe", pleroma_fe) pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
Map.put(data, "pleromafe", pleroma_fe)
else else
data data
end end
json(conn, %{site: data}) json(conn, %{site: data})
end end
end
defp bool_to_val(true), do: "1"
defp bool_to_val(_), do: "0"
defp bool_to_val(true, val, _), do: val
defp bool_to_val(_, _, val), do: val
def frontend_configurations(conn, _params) do def frontend_configurations(conn, _params) do
config = config =
@ -217,20 +225,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
json(conn, config) json(conn, config)
end end
def version(conn, _params) do def version(%{assigns: %{format: "xml"}} = conn, _params) do
version = Pleroma.Application.named_version() version = Pleroma.Application.named_version()
case get_format(conn) do
"xml" ->
response = "<version>#{version}</version>"
conn conn
|> put_resp_content_type("application/xml") |> put_resp_content_type("application/xml")
|> send_resp(200, response) |> send_resp(200, "<version>#{version}</version>")
_ ->
json(conn, version)
end end
def version(conn, _params) do
json(conn, Pleroma.Application.named_version())
end end
def emoji(conn, _params) do def emoji(conn, _params) do

23
mix.exs
View file

@ -190,12 +190,13 @@ defmodule Pleroma.Mixfile do
tag = String.trim(tag), tag = String.trim(tag),
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]), {describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]),
describe = String.trim(describe), describe = String.trim(describe),
ahead <- String.replace(describe, tag, "") do ahead <- String.replace(describe, tag, ""),
ahead <- String.trim_leading(ahead, "-") do
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))} {String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
else else
_ -> _ ->
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"]) {commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
{nil, "-0-g" <> String.trim(commit_hash)} {nil, "0-g" <> String.trim(commit_hash)}
end end
if git_tag && version != git_tag do if git_tag && version != git_tag do
@ -207,14 +208,15 @@ defmodule Pleroma.Mixfile do
# Branch name as pre-release version component, denoted with a dot # Branch name as pre-release version component, denoted with a dot
branch_name = branch_name =
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]), with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
branch_name <- String.trim(branch_name),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name, branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
true <- branch_name != "master" do true <- branch_name not in ["master", "HEAD"] do
branch_name = branch_name =
branch_name branch_name
|> String.trim() |> String.trim()
|> String.replace(identifier_filter, "-") |> String.replace(identifier_filter, "-")
"." <> branch_name branch_name
end end
build_name = build_name =
@ -234,6 +236,17 @@ defmodule Pleroma.Mixfile do
env_override -> env_override env_override -> env_override
end end
# Pre-release version, denoted by appending a hyphen
# and a series of dot separated identifiers
pre_release =
[git_pre_release, branch_name]
|> Enum.filter(fn string -> string && string != "" end)
|> Enum.join(".")
|> (fn
"" -> nil
string -> "-" <> String.replace(string, identifier_filter, "-")
end).()
# Build metadata, denoted with a plus sign # Build metadata, denoted with a plus sign
build_metadata = build_metadata =
[build_name, env_name] [build_name, env_name]
@ -244,7 +257,7 @@ defmodule Pleroma.Mixfile do
string -> "+" <> String.replace(string, identifier_filter, "-") string -> "+" <> String.replace(string, identifier_filter, "-")
end).() end).()
[version, git_pre_release, branch_name, build_metadata] [version, pre_release, build_metadata]
|> Enum.filter(fn string -> string && string != "" end) |> Enum.filter(fn string -> string && string != "" end)
|> Enum.join() |> Enum.join()
end end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 839 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View file

@ -24,7 +24,6 @@ defmodule Pleroma.Emails.AdminEmailTest do
assert res.to == [{to_user.name, to_user.email}] assert res.to == [{to_user.name, to_user.email}]
assert res.from == {config[:name], config[:notify_email]} assert res.from == {config[:name], config[:notify_email]}
assert res.reply_to == {reporter.name, reporter.email}
assert res.subject == "#{config[:name]} Report" assert res.subject == "#{config[:name]} Report"
assert res.html_body == assert res.html_body ==
@ -34,4 +33,17 @@ defmodule Pleroma.Emails.AdminEmailTest do
status_url status_url
}\">#{status_url}</li>\n </ul>\n</p>\n\n" }\">#{status_url}</li>\n </ul>\n</p>\n\n"
end end
test "it works when the reporter is a remote user without email" do
config = Pleroma.Config.get(:instance)
to_user = insert(:user)
reporter = insert(:user, email: nil, local: false)
account = insert(:user)
res =
AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
assert res.to == [{to_user.name, to_user.email}]
assert res.from == {config[:name], config[:notify_email]}
end
end end

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:4001/users/masto_closed/followers?page=1","type":"OrderedCollectionPage","totalItems":437,"next":"http://localhost:4001/users/masto_closed/followers?page=2","partOf":"http://localhost:4001/users/masto_closed/followers","orderedItems":["https://testing.uguu.ltd/users/rin","https://patch.cx/users/rin","https://letsalllovela.in/users/xoxo","https://pleroma.site/users/crushv","https://aria.company/users/boris","https://kawen.space/users/crushv","https://freespeech.host/users/cvcvcv","https://pleroma.site/users/picpub","https://pixelfed.social/users/nosleep","https://boopsnoot.gq/users/5c1896d162f7d337f90492a3","https://pikachu.rocks/users/waifu","https://royal.crablettesare.life/users/crablettes"]}

View file

@ -0,0 +1 @@
{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:4001/users/masto_closed/following?page=1","type":"OrderedCollectionPage","totalItems":152,"next":"http://localhost:4001/users/masto_closed/following?page=2","partOf":"http://localhost:4001/users/masto_closed/following","orderedItems":["https://testing.uguu.ltd/users/rin","https://patch.cx/users/rin","https://letsalllovela.in/users/xoxo","https://pleroma.site/users/crushv","https://aria.company/users/boris","https://kawen.space/users/crushv","https://freespeech.host/users/cvcvcv","https://pleroma.site/users/picpub","https://pixelfed.social/users/nosleep","https://boopsnoot.gq/users/5c1896d162f7d337f90492a3","https://pikachu.rocks/users/waifu","https://royal.crablettesare.life/users/crablettes"]}

View file

@ -39,4 +39,9 @@ defmodule Pleroma.FlakeIdTest do
assert dump(flake_s) == {:ok, flake} assert dump(flake_s) == {:ok, flake}
assert dump(flake) == {:ok, flake} assert dump(flake) == {:ok, flake}
end end
test "is_flake_id?/1" do
assert is_flake_id?("9eoozpwTul5mjSEDRI")
refute is_flake_id?("http://example.com/activities/3ebbadd1-eb14-4e20-8118-b6f79c0c7b0b")
end
end end

View file

@ -182,8 +182,8 @@ defmodule Pleroma.Factory do
} }
end end
def like_activity_factory do def like_activity_factory(attrs \\ %{}) do
note_activity = insert(:note_activity) note_activity = attrs[:note_activity] || insert(:note_activity)
object = Object.normalize(note_activity) object = Object.normalize(note_activity)
user = insert(:user) user = insert(:user)

View file

@ -51,6 +51,10 @@ defmodule HttpRequestMock do
}} }}
end end
def get("https://mastodon.social/users/not_found", _, _, _) do
{:ok, %Tesla.Env{status: 404}}
end
def get("https://mastodon.sdf.org/users/rinpatch", _, _, _) do def get("https://mastodon.sdf.org/users/rinpatch", _, _, _) do
{:ok, {:ok,
%Tesla.Env{ %Tesla.Env{
@ -792,6 +796,14 @@ defmodule HttpRequestMock do
}} }}
end end
def get("http://localhost:4001/users/masto_closed/followers?page=1", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json")
}}
end
def get("http://localhost:4001/users/masto_closed/following", _, _, _) do def get("http://localhost:4001/users/masto_closed/following", _, _, _) do
{:ok, {:ok,
%Tesla.Env{ %Tesla.Env{
@ -800,6 +812,14 @@ defmodule HttpRequestMock do
}} }}
end end
def get("http://localhost:4001/users/masto_closed/following?page=1", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json")
}}
end
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
{:ok, {:ok,
%Tesla.Env{ %Tesla.Env{

View file

@ -1393,4 +1393,78 @@ defmodule Pleroma.UserTest do
assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id) assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
end end
end end
describe "following/followers synchronization" do
setup do
sync = Pleroma.Config.get([:instance, :external_user_synchronization])
on_exit(fn -> Pleroma.Config.put([:instance, :external_user_synchronization], sync) end)
end
test "updates the counters normally on following/getting a follow when disabled" do
Pleroma.Config.put([:instance, :external_user_synchronization], false)
user = insert(:user)
other_user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
info: %{ap_enabled: true}
)
assert User.user_info(other_user).following_count == 0
assert User.user_info(other_user).follower_count == 0
{:ok, user} = Pleroma.User.follow(user, other_user)
other_user = Pleroma.User.get_by_id(other_user.id)
assert User.user_info(user).following_count == 1
assert User.user_info(other_user).follower_count == 1
end
test "syncronizes the counters with the remote instance for the followed when enabled" do
Pleroma.Config.put([:instance, :external_user_synchronization], false)
user = insert(:user)
other_user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
info: %{ap_enabled: true}
)
assert User.user_info(other_user).following_count == 0
assert User.user_info(other_user).follower_count == 0
Pleroma.Config.put([:instance, :external_user_synchronization], true)
{:ok, _user} = User.follow(user, other_user)
other_user = User.get_by_id(other_user.id)
assert User.user_info(other_user).follower_count == 437
end
test "syncronizes the counters with the remote instance for the follower when enabled" do
Pleroma.Config.put([:instance, :external_user_synchronization], false)
user = insert(:user)
other_user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
info: %{ap_enabled: true}
)
assert User.user_info(other_user).following_count == 0
assert User.user_info(other_user).follower_count == 0
Pleroma.Config.put([:instance, :external_user_synchronization], true)
{:ok, other_user} = User.follow(other_user, user)
assert User.user_info(other_user).following_count == 152
end
end
end end

View file

@ -180,18 +180,65 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
end end
describe "/object/:uuid/likes" do describe "/object/:uuid/likes" do
test "it returns the like activities in a collection", %{conn: conn} do setup do
like = insert(:like_activity) like = insert(:like_activity)
like_object_ap_id = Object.normalize(like).data["id"] like_object_ap_id = Object.normalize(like).data["id"]
uuid = String.split(like_object_ap_id, "/") |> List.last()
uuid =
like_object_ap_id
|> String.split("/")
|> List.last()
[id: like.data["id"], uuid: uuid]
end
test "it returns the like activities in a collection", %{conn: conn, id: id, uuid: uuid} do
result = result =
conn conn
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}/likes") |> get("/objects/#{uuid}/likes")
|> json_response(200) |> json_response(200)
assert List.first(result["first"]["orderedItems"])["id"] == like.data["id"] assert List.first(result["first"]["orderedItems"])["id"] == id
assert result["type"] == "OrderedCollection"
assert result["totalItems"] == 1
refute result["first"]["next"]
end
test "it does not crash when page number is exceeded total pages", %{conn: conn, uuid: uuid} do
result =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}/likes?page=2")
|> json_response(200)
assert result["type"] == "OrderedCollectionPage"
assert result["totalItems"] == 1
refute result["next"]
assert Enum.empty?(result["orderedItems"])
end
test "it contains the next key when likes count is more than 10", %{conn: conn} do
note = insert(:note_activity)
insert_list(11, :like_activity, note_activity: note)
uuid =
note
|> Object.normalize()
|> Map.get(:data)
|> Map.get("id")
|> String.split("/")
|> List.last()
result =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}/likes?page=1")
|> json_response(200)
assert result["totalItems"] == 11
assert length(result["orderedItems"]) == 10
assert result["next"]
end end
end end

View file

@ -677,14 +677,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert object.data["likes"] == [user.ap_id] assert object.data["likes"] == [user.ap_id]
assert object.data["like_count"] == 1 assert object.data["like_count"] == 1
[note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
assert note_activity.data["object"]["like_count"] == 1
{:ok, _like_activity, object} = ActivityPub.like(user_two, object) {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
assert object.data["like_count"] == 2 assert object.data["like_count"] == 2
[note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
assert note_activity.data["object"]["like_count"] == 2
end end
end end
@ -1128,4 +1122,65 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert result.id == activity.id assert result.id == activity.id
end end
end end
describe "fetch_follow_information_for_user" do
test "syncronizes following/followers counters" do
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/fuser2/followers",
following_address: "http://localhost:4001/users/fuser2/following"
)
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
assert info.follower_count == 527
assert info.following_count == 267
end
test "detects hidden followers" do
mock(fn env ->
case env.url do
"http://localhost:4001/users/masto_closed/followers?page=1" ->
%Tesla.Env{status: 403, body: ""}
_ ->
apply(HttpRequestMock, :request, [env])
end
end)
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following"
)
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
assert info.hide_followers == true
assert info.hide_follows == false
end
test "detects hidden follows" do
mock(fn env ->
case env.url do
"http://localhost:4001/users/masto_closed/following?page=1" ->
%Tesla.Env{status: 403, body: ""}
_ ->
apply(HttpRequestMock, :request, [env])
end
end)
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following"
)
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
assert info.hide_followers == false
assert info.hide_follows == true
end
end
end end

View file

@ -1373,32 +1373,4 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
refute recipient.follower_address in fixed_object["to"] refute recipient.follower_address in fixed_object["to"]
end end
end end
test "update_following_followers_counters/1" do
user1 =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following"
)
user2 =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/fuser2/followers",
following_address: "http://localhost:4001/users/fuser2/following"
)
Transmogrifier.update_following_followers_counters(user1)
Transmogrifier.update_following_followers_counters(user2)
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
assert followers == 437
assert following == 152
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
assert followers == 527
assert following == 267
end
end end

View file

@ -1914,6 +1914,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
] ]
} }
end end
test "delete part of settings by atom subkeys", %{conn: conn} do
config =
insert(:config,
key: "keyaa1",
value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
)
conn =
post(conn, "/api/pleroma/admin/config", %{
configs: [
%{
group: config.group,
key: config.key,
subkeys: [":subkey1", ":subkey3"],
delete: "true"
}
]
})
assert(
json_response(conn, 200) == %{
"configs" => [
%{
"group" => "pleroma",
"key" => "keyaa1",
"value" => [%{"tuple" => [":subkey2", "val2"]}]
}
]
}
)
end
end end
describe "config mix tasks run" do describe "config mix tasks run" do
@ -1922,7 +1954,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
temp_file = "config/test.exported_from_db.secret.exs" temp_file = "config/test.exported_from_db.secret.exs"
Mix.shell(Mix.Shell.Quiet)
on_exit(fn -> on_exit(fn ->
Mix.shell(Mix.Shell.IO)
:ok = File.rm(temp_file) :ok = File.rm(temp_file)
end) end)

View file

@ -306,7 +306,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
mentions = [mentioned_user.ap_id] mentions = [mentioned_user.ap_id]
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private") {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private")
assert length(to) == 2 assert length(to) == 2
assert length(cc) == 0 assert length(cc) == 0
@ -360,4 +359,242 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
assert third_user.ap_id in to assert third_user.ap_id in to
end end
end end
describe "get_by_id_or_ap_id/1" do
test "get activity by id" do
activity = insert(:note_activity)
%Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id)
assert note.id == activity.id
end
test "get activity by ap_id" do
activity = insert(:note_activity)
%Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"])
assert note.id == activity.id
end
test "get activity by object when type isn't `Create` " do
activity = insert(:like_activity)
%Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id)
assert like.data["object"] == activity.data["object"]
end
end
describe "to_master_date/1" do
test "removes microseconds from date (NaiveDateTime)" do
assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z"
end
test "removes microseconds from date (String)" do
assert Utils.to_masto_date("2015-01-23T23:50:07.123Z") == "2015-01-23T23:50:07.000Z"
end
test "returns empty string when date invalid" do
assert Utils.to_masto_date("2015-01?23T23:50:07.123Z") == ""
end
end
describe "conversation_id_to_context/1" do
test "returns id" do
object = insert(:note)
assert Utils.conversation_id_to_context(object.id) == object.data["id"]
end
test "returns error if object not found" do
assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"}
end
end
describe "maybe_notify_mentioned_recipients/2" do
test "returns recipients when activity is not `Create`" do
activity = insert(:like_activity)
assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == ["test"]
end
test "returns recipients from tag" do
user = insert(:user)
object =
insert(:note,
user: user,
data: %{
"tag" => [
%{"type" => "Hashtag"},
"",
%{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"},
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"},
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}
]
}
)
activity = insert(:note_activity, user: user, note: object)
assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [
"test",
"https://testing.pleroma.lol/users/lain",
"https://shitposter.club/user/5381"
]
end
test "returns recipients when object is map" do
user = insert(:user)
object = insert(:note, user: user)
activity =
insert(:note_activity,
user: user,
note: object,
data_attrs: %{
"object" => %{
"tag" => [
%{"type" => "Hashtag"},
"",
%{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"},
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"},
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}
]
}
}
)
Pleroma.Repo.delete(object)
assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [
"test",
"https://testing.pleroma.lol/users/lain",
"https://shitposter.club/user/5381"
]
end
test "returns recipients when object not found" do
user = insert(:user)
object = insert(:note, user: user)
activity = insert(:note_activity, user: user, note: object)
Pleroma.Repo.delete(object)
assert Utils.maybe_notify_mentioned_recipients(["test-test"], activity) == [
"test-test"
]
end
end
describe "attachments_from_ids_descs/2" do
test "returns [] when attachment ids is empty" do
assert Utils.attachments_from_ids_descs([], "{}") == []
end
test "returns list attachments with desc" do
object = insert(:note)
desc = Jason.encode!(%{object.id => "test-desc"})
assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [
Map.merge(object.data, %{"name" => "test-desc"})
]
end
end
describe "attachments_from_ids/1" do
test "returns attachments with descs" do
object = insert(:note)
desc = Jason.encode!(%{object.id => "test-desc"})
assert Utils.attachments_from_ids(%{
"media_ids" => ["#{object.id}"],
"descriptions" => desc
}) == [
Map.merge(object.data, %{"name" => "test-desc"})
]
end
test "returns attachments without descs" do
object = insert(:note)
assert Utils.attachments_from_ids(%{"media_ids" => ["#{object.id}"]}) == [object.data]
end
test "returns [] when not pass media_ids" do
assert Utils.attachments_from_ids(%{}) == []
end
end
describe "maybe_add_list_data/3" do
test "adds list params when found user list" do
user = insert(:user)
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
%{
additional: %{"bcc" => [list.ap_id], "listMessage" => list.ap_id},
object: %{"listMessage" => list.ap_id}
}
end
test "returns original params when list not found" do
user = insert(:user)
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user))
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
%{additional: %{}, object: %{}}
end
end
describe "make_note_data/11" do
test "returns note data" do
user = insert(:user)
note = insert(:note)
user2 = insert(:user)
user3 = insert(:user)
assert Utils.make_note_data(
user.ap_id,
[user2.ap_id],
"2hu",
"<h1>This is :moominmamma: note</h1>",
[],
note.id,
[name: "jimm"],
"test summary",
[user3.ap_id],
false,
%{"custom_tag" => "test"}
) == %{
"actor" => user.ap_id,
"attachment" => [],
"cc" => [user3.ap_id],
"content" => "<h1>This is :moominmamma: note</h1>",
"context" => "2hu",
"sensitive" => false,
"summary" => "test summary",
"tag" => ["jimm"],
"to" => [user2.ap_id],
"type" => "Note",
"custom_tag" => "test"
}
end
end
describe "maybe_add_attachments/3" do
test "returns parsed results when no_links is true" do
assert Utils.maybe_add_attachments(
{"test", [], ["tags"]},
[],
true
) == {"test", [], ["tags"]}
end
test "adds attachments to parsed results" do
attachment = %{"url" => [%{"href" => "SakuraPM.png"}]}
assert Utils.maybe_add_attachments(
{"test", [], ["tags"]},
[attachment],
false
) == {
"test<br><a href=\"SakuraPM.png\" class='attachment'>SakuraPM.png</a>",
[],
["tags"]
}
end
end
end end

View file

@ -229,5 +229,21 @@ defmodule Pleroma.Web.FederatorTest do
:error = Federator.incoming_ap_doc(params) :error = Federator.incoming_ap_doc(params)
end end
test "it does not crash if MRF rejects the post" do
policies = Pleroma.Config.get([:instance, :rewrite_policy])
mrf_keyword_policy = Pleroma.Config.get(:mrf_keyword)
Pleroma.Config.put([:mrf_keyword, :reject], ["lain"])
Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.KeywordPolicy)
params =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
assert Federator.incoming_ap_doc(params) == :error
Pleroma.Config.put([:instance, :rewrite_policy], policies)
Pleroma.Config.put(:mrf_keyword, mrf_keyword_policy)
end
end end
end end

View file

@ -1671,40 +1671,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
object = Repo.get(Object, media["id"]) object = Repo.get(Object, media["id"])
assert object.data["actor"] == User.ap_id(conn.assigns[:user]) assert object.data["actor"] == User.ap_id(conn.assigns[:user])
end end
test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
proxy_url = "https://cache.pleroma.social"
Pleroma.Config.put([:media_proxy, :enabled], true)
Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
media =
conn
|> post("/api/v1/media", %{"file" => image})
|> json_response(:ok)
assert String.starts_with?(media["url"], proxy_url)
end
test "returns media url when proxy is enabled but media url is whitelisted", %{
conn: conn,
image: image
} do
media_url = "https://media.pleroma.social"
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
Pleroma.Config.put([:media_proxy, :enabled], true)
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
media =
conn
|> post("/api/v1/media", %{"file" => image})
|> json_response(:ok)
assert String.starts_with?(media["url"], media_url)
end
end end
describe "locked accounts" do describe "locked accounts" do

View file

@ -95,6 +95,18 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
assert user_three.nickname in result_ids assert user_three.nickname in result_ids
end end
test "returns account if query contains a space", %{conn: conn} do
user = insert(:user, %{nickname: "shp@shitposter.club"})
results =
conn
|> assign(:user, user)
|> get("/api/v1/accounts/search", %{"q" => "shp@shitposter.club xxx "})
|> json_response(200)
assert length(results) == 1
end
end end
describe ".search" do describe ".search" do

View file

@ -171,21 +171,6 @@ defmodule Pleroma.Web.MediaProxyTest do
encoded = url(url) encoded = url(url)
assert decode_result(encoded) == url assert decode_result(encoded) == url
end end
test "does not change whitelisted urls" do
upload_config = Pleroma.Config.get([Pleroma.Upload])
media_url = "https://media.pleroma.social"
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
url = "#{media_url}/static/logo.png"
encoded = url(url)
assert String.starts_with?(encoded, media_url)
Pleroma.Config.put([Pleroma.Upload], upload_config)
end
end end
describe "when disabled" do describe "when disabled" do
@ -215,12 +200,43 @@ defmodule Pleroma.Web.MediaProxyTest do
decoded decoded
end end
test "mediaproxy whitelist" do describe "whitelist" do
setup do
Pleroma.Config.put([:media_proxy, :enabled], true) Pleroma.Config.put([:media_proxy, :enabled], true)
:ok
end
test "mediaproxy whitelist" do
Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"]) Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
url = "https://feld.me/foo.png" url = "https://feld.me/foo.png"
unencoded = url(url) unencoded = url(url)
assert unencoded == url assert unencoded == url
end end
test "does not change whitelisted urls" do
Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"])
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
media_url = "https://mycdn.akamai.com"
url = "#{media_url}/static/logo.png"
encoded = url(url)
assert String.starts_with?(encoded, media_url)
end
test "ensure Pleroma.Upload base_url is always whitelisted" do
upload_config = Pleroma.Config.get([Pleroma.Upload])
media_url = "https://media.pleroma.social"
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
url = "#{media_url}/static/logo.png"
encoded = url(url)
assert String.starts_with?(encoded, media_url)
Pleroma.Config.put([Pleroma.Upload], upload_config)
end
end
end end

View file

@ -199,7 +199,7 @@ defmodule Pleroma.Web.OStatusTest do
assert retweeted_activity.data["type"] == "Create" assert retweeted_activity.data["type"] == "Create"
assert retweeted_activity.data["actor"] == user.ap_id assert retweeted_activity.data["actor"] == user.ap_id
assert retweeted_activity.local assert retweeted_activity.local
assert retweeted_activity.data["object"]["announcement_count"] == 1 assert Object.normalize(retweeted_activity).data["announcement_count"] == 1
end end
test "handle incoming retweets - Mastodon, salmon" do test "handle incoming retweets - Mastodon, salmon" do
@ -326,6 +326,14 @@ defmodule Pleroma.Web.OStatusTest do
assert User.following?(follower, followed) assert User.following?(follower, followed)
end end
test "refuse following over OStatus if the followed's account is locked" do
incoming = File.read!("test/fixtures/follow.xml")
_user = insert(:user, info: %{locked: true}, ap_id: "https://pawoo.net/users/pekorino")
{:ok, [{:error, "It's not possible to follow locked accounts over OStatus"}]} =
OStatus.handle_incoming(incoming)
end
test "handle incoming unfollows with existing follow" do test "handle incoming unfollows with existing follow" do
incoming_follow = File.read!("test/fixtures/follow.xml") incoming_follow = File.read!("test/fixtures/follow.xml")
{:ok, [_activity]} = OStatus.handle_incoming(incoming_follow) {:ok, [_activity]} = OStatus.handle_incoming(incoming_follow)
@ -426,7 +434,7 @@ defmodule Pleroma.Web.OStatusTest do
} }
end end
test "find_make_or_update_user takes an author element and returns an updated user" do test "find_make_or_update_actor takes an author element and returns an updated user" do
uri = "https://social.heldscal.la/user/23211" uri = "https://social.heldscal.la/user/23211"
{:ok, user} = OStatus.find_or_make_user(uri) {:ok, user} = OStatus.find_or_make_user(uri)
@ -439,14 +447,56 @@ defmodule Pleroma.Web.OStatusTest do
doc = XML.parse_document(File.read!("test/fixtures/23211.atom")) doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
[author] = :xmerl_xpath.string('//author[1]', doc) [author] = :xmerl_xpath.string('//author[1]', doc)
{:ok, user} = OStatus.find_make_or_update_user(author) {:ok, user} = OStatus.find_make_or_update_actor(author)
assert user.avatar["type"] == "Image" assert user.avatar["type"] == "Image"
assert user.name == old_name assert user.name == old_name
assert user.bio == old_bio assert user.bio == old_bio
{:ok, user_again} = OStatus.find_make_or_update_user(author) {:ok, user_again} = OStatus.find_make_or_update_actor(author)
assert user_again == user assert user_again == user
end end
test "find_or_make_user disallows protocol downgrade" do
user = insert(:user, %{local: true})
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
assert User.ap_enabled?(user)
user =
insert(:user, %{
ap_id: "https://social.heldscal.la/user/23211",
info: %{ap_enabled: true},
local: false
})
assert User.ap_enabled?(user)
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
assert User.ap_enabled?(user)
end
test "find_make_or_update_actor disallows protocol downgrade" do
user = insert(:user, %{local: true})
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
assert User.ap_enabled?(user)
user =
insert(:user, %{
ap_id: "https://social.heldscal.la/user/23211",
info: %{ap_enabled: true},
local: false
})
assert User.ap_enabled?(user)
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
assert User.ap_enabled?(user)
doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
[author] = :xmerl_xpath.string('//author[1]', doc)
{:error, :invalid_protocol} = OStatus.find_make_or_update_actor(author)
end
end end
describe "gathering user info from a user id" do describe "gathering user info from a user id" do

View file

@ -0,0 +1,69 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do
use ExUnit.Case, async: true
alias Pleroma.Web.RichMedia.Parsers.TwitterCard
test "returns error when html not contains twitter card" do
assert TwitterCard.parse("", %{}) == {:error, "No twitter card metadata found"}
end
test "parses twitter card with only name attributes" do
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers3.html")
assert TwitterCard.parse(html, %{}) ==
{:ok,
%{
"app:id:googleplay": "com.nytimes.android",
"app:name:googleplay": "NYTimes",
"app:url:googleplay": "nytimes://reader/id/100000006583622",
site: nil,
title:
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database. - The New York Times"
}}
end
test "parses twitter card with only property attributes" do
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers2.html")
assert TwitterCard.parse(html, %{}) ==
{:ok,
%{
card: "summary_large_image",
description:
"With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
image:
"https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
"image:alt": "",
title:
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
url:
"https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html"
}}
end
test "parses twitter card with name & property attributes" do
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers.html")
assert TwitterCard.parse(html, %{}) ==
{:ok,
%{
"app:id:googleplay": "com.nytimes.android",
"app:name:googleplay": "NYTimes",
"app:url:googleplay": "nytimes://reader/id/100000006583622",
card: "summary_large_image",
description:
"With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
image:
"https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
"image:alt": "",
site: nil,
title:
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
url:
"https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html"
}}
end
end

Some files were not shown because too many files have changed in this diff Show more