Merge branch 'develop' into feature/configurable-blocks
This commit is contained in:
commit
2e294ee44a
51 changed files with 1066 additions and 167 deletions
72
CONFIGURATION.md
Normal file
72
CONFIGURATION.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# Configuring Pleroma
|
||||
|
||||
In the `config/` directory, you will find the following relevant files:
|
||||
|
||||
* `config.exs`: default base configuration
|
||||
* `dev.exs`: default additional configuration for `MIX_ENV=dev`
|
||||
* `prod.exs`: default additional configuration for `MIX_ENV=prod`
|
||||
|
||||
|
||||
Do not modify files in the list above.
|
||||
Instead, overload the settings by editing the following files:
|
||||
|
||||
* `dev.secret.exs`: custom additional configuration for `MIX_ENV=dev`
|
||||
* `prod.secret.exs`: custom additional configuration for `MIX_ENV=prod`
|
||||
|
||||
## Message Rewrite Filters (MRFs)
|
||||
|
||||
Modify incoming and outgoing posts.
|
||||
|
||||
config :pleroma, :instance,
|
||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy
|
||||
|
||||
`rewrite_policy` specifies which MRF policies to apply.
|
||||
It can either be a single policy or a list of policies.
|
||||
Currently, MRFs availible by default are:
|
||||
|
||||
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`
|
||||
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`
|
||||
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`
|
||||
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`
|
||||
|
||||
Some policies, such as SimplePolicy and RejectNonPublic,
|
||||
can be additionally configured in their respective sections.
|
||||
|
||||
### NoOpPolicy
|
||||
|
||||
Does not modify posts (this is the default `rewrite_policy`)
|
||||
|
||||
### DropPolicy
|
||||
|
||||
Drops all posts.
|
||||
It generally does not make sense to use this in production.
|
||||
|
||||
### SimplePolicy
|
||||
|
||||
Restricts the visibility of posts from certain instances.
|
||||
|
||||
config :pleroma, :mrf_simple,
|
||||
media_removal: [],
|
||||
media_nsfw: [],
|
||||
federated_timeline_removal: [],
|
||||
reject: []
|
||||
|
||||
* `media_removal`: posts from these instances will have attachments
|
||||
removed
|
||||
* `media_nsfw`: posts from these instances will have attachments marked
|
||||
as nsfw
|
||||
* `federated_timeline_removal`: posts from these instances will be
|
||||
marked as unlisted
|
||||
* `reject`: posts from these instances will be dropped
|
||||
|
||||
### RejectNonPublic
|
||||
|
||||
Drops posts with non-public visibility settings.
|
||||
|
||||
config :pleroma :mrf_rejectnonpublic
|
||||
allow_followersonly: false,
|
||||
allow_direct: false,
|
||||
|
||||
* `allow_followersonly`: whether to allow follower-only posts through
|
||||
the filter
|
||||
* `allow_direct`: whether to allow direct messages through the filter
|
|
@ -64,6 +64,10 @@ config :pleroma, :activitypub,
|
|||
|
||||
config :pleroma, :user, deny_follow_blocked: true
|
||||
|
||||
config :pleroma, :mrf_rejectnonpublic,
|
||||
allow_followersonly: false,
|
||||
allow_direct: false
|
||||
|
||||
config :pleroma, :mrf_simple,
|
||||
media_removal: [],
|
||||
media_nsfw: [],
|
||||
|
|
|
@ -24,18 +24,27 @@ server {
|
|||
# }
|
||||
}
|
||||
|
||||
# Enable SSL session caching for improved performance
|
||||
ssl_session_cache shared:ssl_session_cache:10m;
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
ssl on;
|
||||
ssl_session_timeout 5m;
|
||||
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
|
||||
# Add TLSv1.0 to support older devices
|
||||
ssl_protocols TLSv1.2;
|
||||
# Uncomment line below if you want to support older devices (Before Android 4.4.2, IE 8, etc.)
|
||||
# ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
|
||||
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
server_name example.tld;
|
||||
|
||||
gzip_vary on;
|
||||
|
|
|
@ -5,7 +5,7 @@ defmodule Mix.Tasks.SetModerator do
|
|||
|
||||
@shortdoc "Set moderator status"
|
||||
def run([nickname | rest]) do
|
||||
ensure_started(Repo, [])
|
||||
Application.ensure_all_started(:pleroma)
|
||||
|
||||
moderator =
|
||||
case rest do
|
||||
|
@ -19,7 +19,7 @@ defmodule Mix.Tasks.SetModerator do
|
|||
|> Map.put("is_moderator", !!moderator)
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
user = Repo.update!(cng)
|
||||
{:ok, user} = User.update_and_set_cache(cng)
|
||||
|
||||
IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}")
|
||||
else
|
||||
|
|
|
@ -8,7 +8,8 @@ config :pleroma, :instance,
|
|||
name: "<%= name %>",
|
||||
email: "<%= email %>",
|
||||
limit: 5000,
|
||||
registrations_open: true
|
||||
registrations_open: true,
|
||||
dedupe_media: false
|
||||
|
||||
config :pleroma, :media_proxy,
|
||||
enabled: false,
|
||||
|
|
30
lib/mix/tasks/set_locked.ex
Normal file
30
lib/mix/tasks/set_locked.ex
Normal file
|
@ -0,0 +1,30 @@
|
|||
defmodule Mix.Tasks.SetLocked do
|
||||
use Mix.Task
|
||||
import Mix.Ecto
|
||||
alias Pleroma.{Repo, User}
|
||||
|
||||
@shortdoc "Set locked status"
|
||||
def run([nickname | rest]) do
|
||||
ensure_started(Repo, [])
|
||||
|
||||
locked =
|
||||
case rest do
|
||||
[locked] -> locked == "true"
|
||||
_ -> true
|
||||
end
|
||||
|
||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||
info =
|
||||
user.info
|
||||
|> Map.put("locked", !!locked)
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
user = Repo.update!(cng)
|
||||
|
||||
IO.puts("locked status of #{nickname}: #{user.info["locked"]}")
|
||||
else
|
||||
_ ->
|
||||
IO.puts("No local user #{nickname}")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
defmodule Pleroma.List do
|
||||
use Ecto.Schema
|
||||
import Ecto.{Changeset, Query}
|
||||
alias Pleroma.{User, Repo}
|
||||
alias Pleroma.{User, Repo, Activity}
|
||||
|
||||
schema "lists" do
|
||||
belongs_to(:user, Pleroma.User)
|
||||
|
@ -56,6 +56,19 @@ defmodule Pleroma.List do
|
|||
{:ok, Repo.all(q)}
|
||||
end
|
||||
|
||||
# Get lists the activity should be streamed to.
|
||||
def get_lists_from_activity(%Activity{actor: ap_id}) do
|
||||
actor = User.get_cached_by_ap_id(ap_id)
|
||||
|
||||
query =
|
||||
from(
|
||||
l in Pleroma.List,
|
||||
where: fragment("? && ?", l.following, ^[actor.follower_address])
|
||||
)
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
def rename(%Pleroma.List{} = list, title) do
|
||||
list
|
||||
|> title_changeset(%{title: title})
|
||||
|
|
|
@ -2,20 +2,21 @@ defmodule Pleroma.Upload do
|
|||
alias Ecto.UUID
|
||||
alias Pleroma.Web
|
||||
|
||||
def store(%Plug.Upload{} = file) do
|
||||
uuid = UUID.generate()
|
||||
upload_folder = Path.join(upload_path(), uuid)
|
||||
File.mkdir_p!(upload_folder)
|
||||
result_file = Path.join(upload_folder, file.filename)
|
||||
File.cp!(file.path, result_file)
|
||||
def store(%Plug.Upload{} = file, should_dedupe) do
|
||||
content_type = get_content_type(file.path)
|
||||
uuid = get_uuid(file, should_dedupe)
|
||||
name = get_name(file, uuid, content_type, should_dedupe)
|
||||
upload_folder = get_upload_path(uuid, should_dedupe)
|
||||
url_path = get_url(name, uuid, should_dedupe)
|
||||
|
||||
# fix content type on some image uploads
|
||||
content_type =
|
||||
if file.content_type in [nil, "application/octet-stream"] do
|
||||
get_content_type(file.path)
|
||||
else
|
||||
file.content_type
|
||||
end
|
||||
File.mkdir_p!(upload_folder)
|
||||
result_file = Path.join(upload_folder, name)
|
||||
|
||||
if File.exists?(result_file) do
|
||||
File.rm!(file.path)
|
||||
else
|
||||
File.cp!(file.path, result_file)
|
||||
end
|
||||
|
||||
%{
|
||||
"type" => "Image",
|
||||
|
@ -23,26 +24,48 @@ defmodule Pleroma.Upload do
|
|||
%{
|
||||
"type" => "Link",
|
||||
"mediaType" => content_type,
|
||||
"href" => url_for(Path.join(uuid, :cow_uri.urlencode(file.filename)))
|
||||
"href" => url_path
|
||||
}
|
||||
],
|
||||
"name" => file.filename,
|
||||
"uuid" => uuid
|
||||
"name" => name
|
||||
}
|
||||
end
|
||||
|
||||
def store(%{"img" => "data:image/" <> image_data}) do
|
||||
def store(%{"img" => "data:image/" <> image_data}, should_dedupe) do
|
||||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
||||
data = Base.decode64!(parsed["data"])
|
||||
data = Base.decode64!(parsed["data"], ignore: :whitespace)
|
||||
uuid = UUID.generate()
|
||||
upload_folder = Path.join(upload_path(), uuid)
|
||||
uuidpath = Path.join(upload_path(), uuid)
|
||||
uuid = UUID.generate()
|
||||
|
||||
File.mkdir_p!(upload_path())
|
||||
|
||||
File.write!(uuidpath, data)
|
||||
|
||||
content_type = get_content_type(uuidpath)
|
||||
|
||||
name =
|
||||
create_name(
|
||||
String.downcase(Base.encode16(:crypto.hash(:sha256, data))),
|
||||
parsed["filetype"],
|
||||
content_type
|
||||
)
|
||||
|
||||
upload_folder = get_upload_path(uuid, should_dedupe)
|
||||
url_path = get_url(name, uuid, should_dedupe)
|
||||
|
||||
File.mkdir_p!(upload_folder)
|
||||
filename = Base.encode16(:crypto.hash(:sha256, data)) <> ".#{parsed["filetype"]}"
|
||||
result_file = Path.join(upload_folder, filename)
|
||||
result_file = Path.join(upload_folder, name)
|
||||
|
||||
File.write!(result_file, data)
|
||||
|
||||
content_type = "image/#{parsed["filetype"]}"
|
||||
if should_dedupe do
|
||||
if !File.exists?(result_file) do
|
||||
File.rename(uuidpath, result_file)
|
||||
else
|
||||
File.rm!(uuidpath)
|
||||
end
|
||||
else
|
||||
File.rename(uuidpath, result_file)
|
||||
end
|
||||
|
||||
%{
|
||||
"type" => "Image",
|
||||
|
@ -50,11 +73,10 @@ defmodule Pleroma.Upload do
|
|||
%{
|
||||
"type" => "Link",
|
||||
"mediaType" => content_type,
|
||||
"href" => url_for(Path.join(uuid, :cow_uri.urlencode(filename)))
|
||||
"href" => url_path
|
||||
}
|
||||
],
|
||||
"name" => filename,
|
||||
"uuid" => uuid
|
||||
"name" => name
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -63,6 +85,65 @@ defmodule Pleroma.Upload do
|
|||
Keyword.fetch!(settings, :uploads)
|
||||
end
|
||||
|
||||
defp create_name(uuid, ext, type) do
|
||||
case type do
|
||||
"application/octet-stream" ->
|
||||
String.downcase(Enum.join([uuid, ext], "."))
|
||||
|
||||
"audio/mpeg" ->
|
||||
String.downcase(Enum.join([uuid, "mp3"], "."))
|
||||
|
||||
_ ->
|
||||
String.downcase(Enum.join([uuid, List.last(String.split(type, "/"))], "."))
|
||||
end
|
||||
end
|
||||
|
||||
defp get_uuid(file, should_dedupe) do
|
||||
if should_dedupe do
|
||||
Base.encode16(:crypto.hash(:sha256, File.read!(file.path)))
|
||||
else
|
||||
UUID.generate()
|
||||
end
|
||||
end
|
||||
|
||||
defp get_name(file, uuid, type, should_dedupe) do
|
||||
if should_dedupe do
|
||||
create_name(uuid, List.last(String.split(file.filename, ".")), type)
|
||||
else
|
||||
unless String.contains?(file.filename, ".") do
|
||||
case type do
|
||||
"image/png" -> file.filename <> ".png"
|
||||
"image/jpeg" -> file.filename <> ".jpg"
|
||||
"image/gif" -> file.filename <> ".gif"
|
||||
"video/webm" -> file.filename <> ".webm"
|
||||
"video/mp4" -> file.filename <> ".mp4"
|
||||
"audio/mpeg" -> file.filename <> ".mp3"
|
||||
"audio/ogg" -> file.filename <> ".ogg"
|
||||
"audio/wav" -> file.filename <> ".wav"
|
||||
_ -> file.filename
|
||||
end
|
||||
else
|
||||
file.filename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get_upload_path(uuid, should_dedupe) do
|
||||
if should_dedupe do
|
||||
upload_path()
|
||||
else
|
||||
Path.join(upload_path(), uuid)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_url(name, uuid, should_dedupe) do
|
||||
if should_dedupe do
|
||||
url_for(:cow_uri.urlencode(name))
|
||||
else
|
||||
url_for(Path.join(uuid, :cow_uri.urlencode(name)))
|
||||
end
|
||||
end
|
||||
|
||||
defp url_for(file) do
|
||||
"#{Web.base_url()}/media/#{file}"
|
||||
end
|
||||
|
@ -89,6 +170,9 @@ defmodule Pleroma.Upload do
|
|||
<<0x49, 0x44, 0x33, _, _, _, _, _>> ->
|
||||
"audio/mpeg"
|
||||
|
||||
<<255, 251, _, 68, 0, 0, 0, 0>> ->
|
||||
"audio/mpeg"
|
||||
|
||||
<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00>> ->
|
||||
"audio/ogg"
|
||||
|
||||
|
|
|
@ -201,6 +201,14 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def maybe_follow(%User{} = follower, %User{info: info} = followed) do
|
||||
if not following?(follower, followed) do
|
||||
follow(follower, followed)
|
||||
else
|
||||
{:ok, follower}
|
||||
end
|
||||
end
|
||||
|
||||
@user_config Application.get_env(:pleroma, :user)
|
||||
@deny_follow_blocked Keyword.get(@user_config, :deny_follow_blocked)
|
||||
|
||||
|
@ -259,6 +267,10 @@ defmodule Pleroma.User do
|
|||
Enum.member?(follower.following, followed.follower_address)
|
||||
end
|
||||
|
||||
def locked?(%User{} = user) do
|
||||
user.info["locked"] || false
|
||||
end
|
||||
|
||||
def get_by_ap_id(ap_id) do
|
||||
Repo.get_by(User, ap_id: ap_id)
|
||||
end
|
||||
|
@ -356,6 +368,40 @@ defmodule Pleroma.User do
|
|||
{:ok, Repo.all(q)}
|
||||
end
|
||||
|
||||
def get_follow_requests_query(%User{} = user) do
|
||||
from(
|
||||
a in Activity,
|
||||
where:
|
||||
fragment(
|
||||
"? ->> 'type' = 'Follow'",
|
||||
a.data
|
||||
),
|
||||
where:
|
||||
fragment(
|
||||
"? ->> 'state' = 'pending'",
|
||||
a.data
|
||||
),
|
||||
where:
|
||||
fragment(
|
||||
"? @> ?",
|
||||
a.data,
|
||||
^%{"object" => user.ap_id}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def get_follow_requests(%User{} = user) do
|
||||
q = get_follow_requests_query(user)
|
||||
reqs = Repo.all(q)
|
||||
|
||||
users =
|
||||
Enum.map(reqs, fn req -> req.actor end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.map(fn ap_id -> get_by_ap_id(ap_id) end)
|
||||
|
||||
{:ok, users}
|
||||
end
|
||||
|
||||
def increase_note_count(%User{} = user) do
|
||||
note_count = (user.info["note_count"] || 0) + 1
|
||||
new_info = Map.put(user.info, "note_count", note_count)
|
||||
|
@ -486,7 +532,31 @@ defmodule Pleroma.User do
|
|||
|
||||
def blocks?(user, %{ap_id: ap_id}) do
|
||||
blocks = user.info["blocks"] || []
|
||||
Enum.member?(blocks, ap_id)
|
||||
domain_blocks = user.info["domain_blocks"] || []
|
||||
%{host: host} = URI.parse(ap_id)
|
||||
|
||||
Enum.member?(blocks, ap_id) ||
|
||||
Enum.any?(domain_blocks, fn domain ->
|
||||
host == domain
|
||||
end)
|
||||
end
|
||||
|
||||
def block_domain(user, domain) do
|
||||
domain_blocks = user.info["domain_blocks"] || []
|
||||
new_blocks = Enum.uniq([domain | domain_blocks])
|
||||
new_info = Map.put(user.info, "domain_blocks", new_blocks)
|
||||
|
||||
cs = User.info_changeset(user, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
end
|
||||
|
||||
def unblock_domain(user, domain) do
|
||||
blocks = user.info["domain_blocks"] || []
|
||||
new_blocks = List.delete(blocks, domain)
|
||||
new_info = Map.put(user.info, "domain_blocks", new_blocks)
|
||||
|
||||
cs = User.info_changeset(user, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
end
|
||||
|
||||
def local_user_query() do
|
||||
|
|
|
@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
if activity.data["type"] in ["Create", "Announce"] do
|
||||
Pleroma.Web.Streamer.stream("user", activity)
|
||||
Pleroma.Web.Streamer.stream("list", activity)
|
||||
|
||||
if Enum.member?(activity.data["to"], public) do
|
||||
Pleroma.Web.Streamer.stream("public", activity)
|
||||
|
@ -198,7 +199,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
:ok <- maybe_federate(unannounce_activity),
|
||||
{:ok, _activity} <- Repo.delete(announce_activity),
|
||||
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
||||
{:ok, unannounce_activity, announce_activity, object}
|
||||
{:ok, unannounce_activity, object}
|
||||
else
|
||||
_e -> {:ok, object}
|
||||
end
|
||||
|
@ -214,6 +215,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
|
||||
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
|
||||
{:ok, activity} <- insert(unfollow_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
|
@ -449,11 +451,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
|
||||
blocks = info["blocks"] || []
|
||||
domain_blocks = info["domain_blocks"] || []
|
||||
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
|
||||
where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks)
|
||||
where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks),
|
||||
where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -502,7 +506,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
|
||||
def upload(file) do
|
||||
data = Upload.store(file)
|
||||
data = Upload.store(file, Application.get_env(:pleroma, :instance)[:dedupe_media])
|
||||
Repo.insert(%Object{data: data})
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
|||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@mrf_rejectnonpublic Application.get_env(:pleroma, :mrf_rejectnonpublic)
|
||||
@allow_followersonly Keyword.get(@mrf_rejectnonpublic, :allow_followersonly)
|
||||
@allow_direct Keyword.get(@mrf_rejectnonpublic, :allow_direct)
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
if object["type"] == "Create" do
|
||||
|
@ -18,9 +22,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
|||
end
|
||||
|
||||
case visibility do
|
||||
"public" -> {:ok, object}
|
||||
"unlisted" -> {:ok, object}
|
||||
_ -> {:reject, nil}
|
||||
"public" ->
|
||||
{:ok, object}
|
||||
|
||||
"unlisted" ->
|
||||
{:ok, object}
|
||||
|
||||
"followers" ->
|
||||
with true <- @allow_followersonly do
|
||||
{:ok, object}
|
||||
else
|
||||
_e -> {:reject, nil}
|
||||
end
|
||||
|
||||
"direct" ->
|
||||
with true <- @allow_direct do
|
||||
{:ok, object}
|
||||
else
|
||||
_e -> {:reject, nil}
|
||||
end
|
||||
end
|
||||
else
|
||||
{:ok, object}
|
||||
|
|
|
@ -30,14 +30,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
when not is_nil(in_reply_to_id) do
|
||||
case ActivityPub.fetch_object_from_id(in_reply_to_id) do
|
||||
{:ok, replied_object} ->
|
||||
activity = Activity.get_create_activity_by_object_ap_id(replied_object.data["id"])
|
||||
|
||||
object
|
||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
||||
|> Map.put("inReplyToStatusId", activity.id)
|
||||
|> Map.put("conversation", replied_object.data["context"] || object["conversation"])
|
||||
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) do
|
||||
object
|
||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
||||
|> Map.put("inReplyToStatusId", activity.id)
|
||||
|> Map.put("conversation", replied_object.data["context"] || object["conversation"])
|
||||
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|
||||
else
|
||||
e ->
|
||||
Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}")
|
||||
object
|
||||
end
|
||||
|
||||
e ->
|
||||
Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}")
|
||||
|
@ -137,9 +142,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
|
||||
%User{} = follower <- User.get_or_fetch_by_ap_id(follower),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||
ActivityPub.accept(%{to: [follower.ap_id], actor: followed.ap_id, object: data, local: true})
|
||||
if not User.locked?(followed) do
|
||||
ActivityPub.accept(%{
|
||||
to: [follower.ap_id],
|
||||
actor: followed.ap_id,
|
||||
object: data,
|
||||
local: true
|
||||
})
|
||||
|
||||
User.follow(follower, followed)
|
||||
end
|
||||
|
||||
User.follow(follower, followed)
|
||||
{:ok, activity}
|
||||
else
|
||||
_e -> :error
|
||||
|
@ -252,7 +265,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||
|
||||
banner = new_user_data[:info]["banner"]
|
||||
locked = new_user_data[:info]["locked"]
|
||||
locked = new_user_data[:info]["locked"] || false
|
||||
|
||||
update_data =
|
||||
new_user_data
|
||||
|
@ -304,7 +317,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, object} <-
|
||||
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
|
||||
{:ok, activity, _, _} <- ActivityPub.unannounce(actor, object, id, false) do
|
||||
{:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do
|
||||
{:ok, activity}
|
||||
else
|
||||
_e -> :error
|
||||
|
@ -432,6 +445,58 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
{:ok, data}
|
||||
end
|
||||
|
||||
# Mastodon Accept/Reject requires a non-normalized object containing the actor URIs,
|
||||
# because of course it does.
|
||||
def prepare_outgoing(%{"type" => "Accept"} = data) do
|
||||
follow_activity_id =
|
||||
if is_binary(data["object"]) do
|
||||
data["object"]
|
||||
else
|
||||
data["object"]["id"]
|
||||
end
|
||||
|
||||
with follow_activity <- Activity.get_by_ap_id(follow_activity_id) do
|
||||
object = %{
|
||||
"actor" => follow_activity.actor,
|
||||
"object" => follow_activity.data["object"],
|
||||
"id" => follow_activity.data["id"],
|
||||
"type" => "Follow"
|
||||
}
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|> Map.put("@context", "https://www.w3.org/ns/activitystreams")
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_outgoing(%{"type" => "Reject"} = data) do
|
||||
follow_activity_id =
|
||||
if is_binary(data["object"]) do
|
||||
data["object"]
|
||||
else
|
||||
data["object"]["id"]
|
||||
end
|
||||
|
||||
with follow_activity <- Activity.get_by_ap_id(follow_activity_id) do
|
||||
object = %{
|
||||
"actor" => follow_activity.actor,
|
||||
"object" => follow_activity.data["object"],
|
||||
"id" => follow_activity.data["id"],
|
||||
"type" => "Follow"
|
||||
}
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|> Map.put("@context", "https://www.w3.org/ns/activitystreams")
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_outgoing(%{"type" => _type} = data) do
|
||||
data =
|
||||
data
|
||||
|
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
alias Pleroma.Web.Endpoint
|
||||
alias Ecto.{Changeset, UUID}
|
||||
import Ecto.Query
|
||||
require Logger
|
||||
|
||||
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
||||
# so figure out what the actor's URI is based on what we have.
|
||||
|
@ -216,10 +217,27 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|
||||
#### Follow-related helpers
|
||||
|
||||
@doc """
|
||||
Updates a follow activity's state (for locked accounts).
|
||||
"""
|
||||
def update_follow_state(%Activity{} = activity, state) do
|
||||
with new_data <-
|
||||
activity.data
|
||||
|> Map.put("state", state),
|
||||
changeset <- Changeset.change(activity, data: new_data),
|
||||
{:ok, activity} <- Repo.update(changeset) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Makes a follow activity data for the given follower and followed
|
||||
"""
|
||||
def make_follow_data(%User{ap_id: follower_id}, %User{ap_id: followed_id}, activity_id) do
|
||||
def make_follow_data(
|
||||
%User{ap_id: follower_id},
|
||||
%User{ap_id: followed_id} = followed,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Follow",
|
||||
"actor" => follower_id,
|
||||
|
@ -228,7 +246,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"object" => followed_id
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
data = if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
data = if User.locked?(followed), do: Map.put(data, "state", "pending"), else: data
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||
|
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.Web
|
||||
alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.{CommonAPI, OStatus}
|
||||
alias Pleroma.Web.OAuth.{Authorization, Token, App}
|
||||
alias Comeonin.Pbkdf2
|
||||
|
@ -71,6 +72,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
user
|
||||
end
|
||||
|
||||
user =
|
||||
if locked = params["locked"] do
|
||||
with locked <- locked == "true",
|
||||
new_info <- Map.put(user.info, "locked", locked),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
|
||||
with changeset <- User.update_changeset(user, params),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
if original_user != user do
|
||||
|
@ -345,7 +360,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
|
@ -476,6 +491,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
|
||||
with {:ok, follow_requests} <- User.get_follow_requests(followed) do
|
||||
render(conn, AccountView, "accounts.json", %{users: follow_requests, as: :user})
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||
with %User{} = follower <- Repo.get(User, id),
|
||||
{:ok, follower} <- User.maybe_follow(follower, followed),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
||||
{:ok, _activity} <-
|
||||
ActivityPub.accept(%{
|
||||
to: [follower.ap_id],
|
||||
actor: followed.ap_id,
|
||||
object: follow_activity.data["id"],
|
||||
type: "Accept"
|
||||
}) do
|
||||
render(conn, AccountView, "relationship.json", %{user: followed, target: follower})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(403, Jason.encode!(%{"error" => message}))
|
||||
end
|
||||
end
|
||||
|
||||
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||
with %User{} = follower <- Repo.get(User, id),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||
{:ok, _activity} <-
|
||||
ActivityPub.reject(%{
|
||||
to: [follower.ap_id],
|
||||
actor: followed.ap_id,
|
||||
object: follow_activity.data["id"],
|
||||
type: "Reject"
|
||||
}) do
|
||||
render(conn, AccountView, "relationship.json", %{user: followed, target: follower})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(403, Jason.encode!(%{"error" => message}))
|
||||
end
|
||||
end
|
||||
|
||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||
with %User{} = followed <- Repo.get(User, id),
|
||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
|
@ -545,6 +607,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do
|
||||
json(conn, info["domain_blocks"] || [])
|
||||
end
|
||||
|
||||
def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
User.block_domain(blocker, domain)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def unblock_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
User.unblock_domain(blocker, domain)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true")
|
||||
|
||||
|
|
|
@ -15,10 +15,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
|
|||
with token when not is_nil(token) <- params["access_token"],
|
||||
%Token{user_id: user_id} <- Repo.get_by(Token, token: token),
|
||||
%User{} = user <- Repo.get(User, user_id),
|
||||
stream when stream in ["public", "public:local", "user", "direct"] <- params["stream"] do
|
||||
stream when stream in ["public", "public:local", "user", "direct", "list"] <-
|
||||
params["stream"] do
|
||||
topic = if stream == "list", do: "list:#{params["list"]}", else: stream
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:topic, params["stream"])
|
||||
|> assign(:topic, topic)
|
||||
|> assign(:user, user)
|
||||
|
||||
Pleroma.Web.Streamer.add_socket(params["stream"], socket)
|
||||
|
|
|
@ -125,8 +125,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
uri: object["id"],
|
||||
url: object["external_url"] || object["id"],
|
||||
account: AccountView.render("account.json", %{user: user}),
|
||||
in_reply_to_id: reply_to && reply_to.id,
|
||||
in_reply_to_account_id: reply_to_user && reply_to_user.id,
|
||||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
||||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||
reblog: nil,
|
||||
content: HtmlSanitizeEx.basic_html(object["content"]),
|
||||
created_at: created_at,
|
||||
|
|
|
@ -81,10 +81,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
# - investigate a way to verify the user wants to grant read/write/follow once scope handling is done
|
||||
def token_exchange(
|
||||
conn,
|
||||
%{"grant_type" => "password", "name" => name, "password" => password} = params
|
||||
%{"grant_type" => "password", "username" => name, "password" => password} = params
|
||||
) do
|
||||
with %App{} = app <- get_app_from_request(conn, params),
|
||||
%User{} = user <- User.get_cached_by_nickname(name),
|
||||
%User{} = user <- User.get_by_nickname_or_email(name),
|
||||
true <- Pbkdf2.checkpw(password, user.password_hash),
|
||||
{:ok, auth} <- Authorization.create_authorization(app, user),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
|
@ -104,6 +104,18 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
end
|
||||
end
|
||||
|
||||
def token_exchange(
|
||||
conn,
|
||||
%{"grant_type" => "password", "name" => name, "password" => password} = params
|
||||
) do
|
||||
params =
|
||||
params
|
||||
|> Map.delete("name")
|
||||
|> Map.put("username", name)
|
||||
|
||||
token_exchange(conn, params)
|
||||
end
|
||||
|
||||
defp fix_padding(token) do
|
||||
token
|
||||
|> Base.url_decode64!(padding: false)
|
||||
|
|
|
@ -41,7 +41,7 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
pipeline :well_known do
|
||||
plug(:accepts, ["xml", "xrd+xml", "json", "jrd+json"])
|
||||
plug(:accepts, ["json", "jrd+json", "xml", "xrd+xml"])
|
||||
end
|
||||
|
||||
pipeline :config do
|
||||
|
@ -97,12 +97,14 @@ defmodule Pleroma.Web.Router do
|
|||
post("/accounts/:id/mute", MastodonAPIController, :relationship_noop)
|
||||
post("/accounts/:id/unmute", MastodonAPIController, :relationship_noop)
|
||||
|
||||
get("/follow_requests", MastodonAPIController, :follow_requests)
|
||||
post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request)
|
||||
post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request)
|
||||
|
||||
post("/follows", MastodonAPIController, :follow)
|
||||
|
||||
get("/blocks", MastodonAPIController, :blocks)
|
||||
|
||||
get("/domain_blocks", MastodonAPIController, :empty_array)
|
||||
get("/follow_requests", MastodonAPIController, :empty_array)
|
||||
get("/mutes", MastodonAPIController, :empty_array)
|
||||
|
||||
get("/timelines/home", MastodonAPIController, :home_timeline)
|
||||
|
@ -134,6 +136,10 @@ defmodule Pleroma.Web.Router do
|
|||
get("/lists/:id/accounts", MastodonAPIController, :list_accounts)
|
||||
post("/lists/:id/accounts", MastodonAPIController, :add_to_list)
|
||||
delete("/lists/:id/accounts", MastodonAPIController, :remove_from_list)
|
||||
|
||||
get("/domain_blocks", MastodonAPIController, :domain_blocks)
|
||||
post("/domain_blocks", MastodonAPIController, :block_domain)
|
||||
delete("/domain_blocks", MastodonAPIController, :unblock_domain)
|
||||
end
|
||||
|
||||
scope "/api/web", Pleroma.Web.MastodonAPI do
|
||||
|
@ -238,8 +244,13 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/statuses/update", TwitterAPI.Controller, :status_update)
|
||||
post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet)
|
||||
post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet)
|
||||
post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post)
|
||||
|
||||
get("/pleroma/friend_requests", TwitterAPI.Controller, :friend_requests)
|
||||
post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request)
|
||||
post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request)
|
||||
|
||||
post("/friendships/create", TwitterAPI.Controller, :follow)
|
||||
post("/friendships/destroy", TwitterAPI.Controller, :unfollow)
|
||||
post("/blocks/create", TwitterAPI.Controller, :block)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule Pleroma.Web.Streamer do
|
||||
use GenServer
|
||||
require Logger
|
||||
alias Pleroma.{User, Notification}
|
||||
alias Pleroma.{User, Notification, Activity, Object}
|
||||
|
||||
def init(args) do
|
||||
{:ok, args}
|
||||
|
@ -59,6 +59,19 @@ defmodule Pleroma.Web.Streamer do
|
|||
{:noreply, topics}
|
||||
end
|
||||
|
||||
def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
|
||||
recipient_topics =
|
||||
Pleroma.List.get_lists_from_activity(item)
|
||||
|> Enum.map(fn %{id: id} -> "list:#{id}" end)
|
||||
|
||||
Enum.each(recipient_topics || [], fn list_topic ->
|
||||
Logger.debug("Trying to push message to #{list_topic}\n\n")
|
||||
push_to_socket(topics, list_topic, item)
|
||||
end)
|
||||
|
||||
{:noreply, topics}
|
||||
end
|
||||
|
||||
def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do
|
||||
topic = "user:#{item.user_id}"
|
||||
|
||||
|
@ -125,6 +138,34 @@ defmodule Pleroma.Web.Streamer do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp represent_update(%Activity{} = activity, %User{} = user) do
|
||||
%{
|
||||
event: "update",
|
||||
payload:
|
||||
Pleroma.Web.MastodonAPI.StatusView.render(
|
||||
"status.json",
|
||||
activity: activity,
|
||||
for: user
|
||||
)
|
||||
|> Jason.encode!()
|
||||
}
|
||||
|> Jason.encode!()
|
||||
end
|
||||
|
||||
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
|
||||
Enum.each(topics[topic] || [], fn socket ->
|
||||
# Get the current user so we have up-to-date blocks etc.
|
||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||
blocks = user.info["blocks"] || []
|
||||
|
||||
parent = Object.get_by_ap_id(item.data["object"])
|
||||
|
||||
unless is_nil(parent) or item.actor in blocks or parent.data["actor"] in blocks do
|
||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def push_to_socket(topics, topic, item) do
|
||||
Enum.each(topics[topic] || [], fn socket ->
|
||||
# Get the current user so we have up-to-date blocks etc.
|
||||
|
@ -132,20 +173,7 @@ defmodule Pleroma.Web.Streamer do
|
|||
blocks = user.info["blocks"] || []
|
||||
|
||||
unless item.actor in blocks do
|
||||
json =
|
||||
%{
|
||||
event: "update",
|
||||
payload:
|
||||
Pleroma.Web.MastodonAPI.StatusView.render(
|
||||
"status.json",
|
||||
activity: item,
|
||||
for: user
|
||||
)
|
||||
|> Jason.encode!()
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
||||
send(socket.transport_pid, {:text, json})
|
||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -12,14 +12,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
|
||||
def delete(%User{} = user, id) do
|
||||
# TwitterAPI does not have an "unretweet" endpoint; instead this is done
|
||||
# via the "destroy" endpoint. Therefore, we need to handle
|
||||
# when the status to "delete" is actually an Announce (repeat) object.
|
||||
with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id) do
|
||||
case type do
|
||||
"Announce" -> unrepeat(user, id)
|
||||
_ -> CommonAPI.delete(id, user)
|
||||
end
|
||||
with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id),
|
||||
{:ok, activity} <- CommonAPI.delete(id, user) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,8 +65,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
end
|
||||
|
||||
defp unrepeat(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _unannounce, activity, _object} <- CommonAPI.unrepeat(ap_id_or_id, user) do
|
||||
def unrepeat(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.{Repo, Activity, User, Notification}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Ecto.Changeset
|
||||
|
||||
require Logger
|
||||
|
@ -240,6 +241,13 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
end
|
||||
|
||||
def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.unrepeat(user, id) do
|
||||
render(conn, ActivityView, "activity.json", %{activity: activity, for: user})
|
||||
end
|
||||
end
|
||||
|
||||
def register(conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.register_user(params) do
|
||||
render(conn, UserView, "show.json", %{user: user})
|
||||
|
@ -331,6 +339,54 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
end
|
||||
|
||||
def friend_requests(conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
||||
{:ok, friend_requests} <- User.get_follow_requests(user) do
|
||||
render(conn, UserView, "index.json", %{users: friend_requests, for: conn.assigns[:user]})
|
||||
else
|
||||
_e -> bad_request_reply(conn, "Can't get friend requests")
|
||||
end
|
||||
end
|
||||
|
||||
def approve_friend_request(conn, %{"user_id" => uid} = params) do
|
||||
with followed <- conn.assigns[:user],
|
||||
uid when is_number(uid) <- String.to_integer(uid),
|
||||
%User{} = follower <- Repo.get(User, uid),
|
||||
{:ok, follower} <- User.maybe_follow(follower, followed),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
||||
{:ok, _activity} <-
|
||||
ActivityPub.accept(%{
|
||||
to: [follower.ap_id],
|
||||
actor: followed.ap_id,
|
||||
object: follow_activity.data["id"],
|
||||
type: "Accept"
|
||||
}) do
|
||||
render(conn, UserView, "show.json", %{user: follower, for: followed})
|
||||
else
|
||||
e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}")
|
||||
end
|
||||
end
|
||||
|
||||
def deny_friend_request(conn, %{"user_id" => uid} = params) do
|
||||
with followed <- conn.assigns[:user],
|
||||
uid when is_number(uid) <- String.to_integer(uid),
|
||||
%User{} = follower <- Repo.get(User, uid),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||
{:ok, _activity} <-
|
||||
ActivityPub.reject(%{
|
||||
to: [follower.ap_id],
|
||||
actor: followed.ap_id,
|
||||
object: follow_activity.data["id"],
|
||||
type: "Reject"
|
||||
}) do
|
||||
render(conn, UserView, "show.json", %{user: follower, for: followed})
|
||||
else
|
||||
e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}")
|
||||
end
|
||||
end
|
||||
|
||||
def friends_ids(%{assigns: %{user: user}} = conn, _params) do
|
||||
with {:ok, friends} <- User.get_friends(user) do
|
||||
ids =
|
||||
|
@ -357,6 +413,20 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
params
|
||||
end
|
||||
|
||||
user =
|
||||
if locked = params["locked"] do
|
||||
with locked <- locked == "true",
|
||||
new_info <- Map.put(user.info, "locked", locked),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
|
||||
with changeset <- User.update_changeset(user, params),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
CommonAPI.update(user)
|
||||
|
|
|
@ -51,7 +51,8 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
"statusnet_profile_url" => user.ap_id,
|
||||
"cover_photo" => User.banner_url(user) |> MediaProxy.url(),
|
||||
"background_image" => image_url(user.info["background"]) |> MediaProxy.url(),
|
||||
"is_local" => user.local
|
||||
"is_local" => user.local,
|
||||
"locked" => !!user.info["locked"]
|
||||
}
|
||||
|
||||
if assigns[:token] do
|
||||
|
|
|
@ -25,35 +25,17 @@ defmodule Pleroma.Web.WebFinger do
|
|||
|> XmlBuilder.to_doc()
|
||||
end
|
||||
|
||||
def webfinger(resource, "JSON") do
|
||||
def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do
|
||||
host = Pleroma.Web.Endpoint.host()
|
||||
regex = ~r/(acct:)?(?<username>\w+)@#{host}/
|
||||
|
||||
with %{"username" => username} <- Regex.named_captures(regex, resource) do
|
||||
user = User.get_by_nickname(username)
|
||||
{:ok, represent_user(user, "JSON")}
|
||||
with %{"username" => username} <- Regex.named_captures(regex, resource),
|
||||
%User{} = user <- User.get_by_nickname(username) do
|
||||
{:ok, represent_user(user, fmt)}
|
||||
else
|
||||
_e ->
|
||||
with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do
|
||||
{:ok, represent_user(user, "JSON")}
|
||||
else
|
||||
_e ->
|
||||
{:error, "Couldn't find user"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def webfinger(resource, "XML") do
|
||||
host = Pleroma.Web.Endpoint.host()
|
||||
regex = ~r/(acct:)?(?<username>\w+)@#{host}/
|
||||
|
||||
with %{"username" => username} <- Regex.named_captures(regex, resource) do
|
||||
user = User.get_by_nickname(username)
|
||||
{:ok, represent_user(user, "XML")}
|
||||
else
|
||||
_e ->
|
||||
with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do
|
||||
{:ok, represent_user(user, "XML")}
|
||||
with %User{} = user <- User.get_cached_by_ap_id(resource) do
|
||||
{:ok, represent_user(user, fmt)}
|
||||
else
|
||||
_e ->
|
||||
{:error, "Couldn't find user"}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddListFollowIndex do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create index(:lists, [:following])
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
defmodule Pleroma.Repo.Migrations.CreateApidHostExtractionIndex do
|
||||
use Ecto.Migration
|
||||
@disable_ddl_transaction true
|
||||
|
||||
def change do
|
||||
create index(:activities, ["(split_part(actor, '/', 3))"], concurrently: true, name: :activities_hosts)
|
||||
end
|
||||
end
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 997 B |
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.c0e1e1e1fcff94fd1e14fc44bfee9a1e.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.16ab7851cdbf730f9cbc.js></script><script type=text/javascript src=/static/js/vendor.56aa9f8c34786f6af6b7.js></script><script type=text/javascript src=/static/js/app.13c0bda10eb515cdf8ed.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.5d0189b6f119febde070b703869bbd06.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.f2341edd686e54ee9b4a.js></script><script type=text/javascript src=/static/js/vendor.a93310d51acbd9480094.js></script><script type=text/javascript src=/static/js/app.de965bb2a0a8bffbeafa.js></script></body></html>
|
|
@ -10,5 +10,6 @@
|
|||
"whoToFollowProviderDummy2": "https://followlink.osa-p.net/api/get_recommend.json?acct=@{{user}}@{{host}}",
|
||||
"whoToFollowLink": "https://vinayaka.distsn.org/?{{host}}+{{user}}",
|
||||
"whoToFollowLinkDummy2": "https://followlink.osa-p.net/recommend.html",
|
||||
"showInstanceSpecificPanel": false
|
||||
"showInstanceSpecificPanel": false,
|
||||
"scopeOptionsEnabled": false
|
||||
}
|
||||
|
|
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
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
8
priv/static/static/js/app.de965bb2a0a8bffbeafa.js
Normal file
8
priv/static/static/js/app.de965bb2a0a8bffbeafa.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.de965bb2a0a8bffbeafa.js.map
Normal file
1
priv/static/static/js/app.de965bb2a0a8bffbeafa.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,2 +0,0 @@
|
|||
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(a&&a(c,o);i.length;)i.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"56aa9f8c34786f6af6b7",2:"13c0bda10eb515cdf8ed"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.16ab7851cdbf730f9cbc.js.map
|
2
priv/static/static/js/manifest.f2341edd686e54ee9b4a.js
Normal file
2
priv/static/static/js/manifest.f2341edd686e54ee9b4a.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(o,p){for(var c,l,s=0,i=[];s<o.length;s++)l=o[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(c in p)Object.prototype.hasOwnProperty.call(p,c)&&(e[c]=p[c]);for(a&&a(o,p);i.length;)i.shift().call(null,t);if(p[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"a93310d51acbd9480094",2:"de965bb2a0a8bffbeafa"}[e]+".js",r.appendChild(o)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.f2341edd686e54ee9b4a.js.map
|
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
1
priv/static/static/js/vendor.a93310d51acbd9480094.js.map
Normal file
1
priv/static/static/js/vendor.a93310d51acbd9480094.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -74,4 +74,20 @@ defmodule Pleroma.ListTest do
|
|||
assert list_two in lists
|
||||
refute list_three in lists
|
||||
end
|
||||
|
||||
test "getting all lists the user is a member of" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
||||
{:ok, list_one} = Pleroma.List.follow(list_one, other_user)
|
||||
{:ok, list_two} = Pleroma.List.follow(list_two, other_user)
|
||||
{:ok, list_three} = Pleroma.List.follow(list_three, user)
|
||||
|
||||
lists = Pleroma.List.get_lists_from_activity(%Pleroma.Activity{actor: other_user.ap_id})
|
||||
assert list_one in lists
|
||||
assert list_two in lists
|
||||
refute list_three in lists
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,40 +3,58 @@ defmodule Pleroma.UploadTest do
|
|||
use Pleroma.DataCase
|
||||
|
||||
describe "Storing a file" do
|
||||
test "copies the file to the configured folder" do
|
||||
test "copies the file to the configured folder with deduping" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
data = Upload.store(file)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
data = Upload.store(file, true)
|
||||
|
||||
assert List.first(data["url"])["href"] ==
|
||||
"http://localhost:4001/media/#{data["uuid"]}/an%20%5Bimage.jpg"
|
||||
assert data["name"] ==
|
||||
"e7a6d0cf595bff76f14c9a98b6c199539559e8b844e02e51e5efcfd1f614a2df.jpeg"
|
||||
end
|
||||
|
||||
test "fixes an incorrect content type" do
|
||||
test "copies the file to the configured folder without deduping" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "application/octet-stream",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
data = Upload.store(file)
|
||||
data = Upload.store(file, false)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
end
|
||||
|
||||
test "fixes incorrect content type" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "application/octet-stream",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
data = Upload.store(file, true)
|
||||
assert hd(data["url"])["mediaType"] == "image/jpeg"
|
||||
end
|
||||
|
||||
test "does not modify a valid content type" do
|
||||
test "adds missing extension" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/png",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
filename: "an [image.jpg"
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "an [image"
|
||||
}
|
||||
|
||||
data = Upload.store(file)
|
||||
assert hd(data["url"])["mediaType"] == "image/png"
|
||||
data = Upload.store(file, false)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -361,6 +361,27 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "domain blocking" do
|
||||
test "blocks domains" do
|
||||
user = insert(:user)
|
||||
collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
|
||||
|
||||
{:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
|
||||
|
||||
assert User.blocks?(user, collateral_user)
|
||||
end
|
||||
|
||||
test "unblocks domains" do
|
||||
user = insert(:user)
|
||||
collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
|
||||
|
||||
{:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
|
||||
{:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
|
||||
|
||||
refute User.blocks?(user, collateral_user)
|
||||
end
|
||||
end
|
||||
|
||||
test "get recipients from activity" do
|
||||
actor = insert(:user)
|
||||
user = insert(:user, local: true)
|
||||
|
|
|
@ -318,11 +318,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
{:ok, announce_activity, object} = ActivityPub.announce(user, object)
|
||||
assert object.data["announcement_count"] == 1
|
||||
|
||||
{:ok, unannounce_activity, activity, object} = ActivityPub.unannounce(user, object)
|
||||
{:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
|
||||
assert object.data["announcement_count"] == 0
|
||||
|
||||
assert activity == announce_activity
|
||||
|
||||
assert unannounce_activity.data["to"] == [
|
||||
User.ap_followers(user),
|
||||
announce_activity.data["actor"]
|
||||
|
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.{Repo, User, Activity, Notification}
|
||||
alias Pleroma.Web.{OStatus, CommonAPI}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
||||
import Pleroma.Factory
|
||||
import ExUnit.CaptureLog
|
||||
|
@ -644,6 +645,73 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "locked accounts" do
|
||||
test "/api/v1/follow_requests works" do
|
||||
user = insert(:user, %{info: %{"locked" => true}})
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/follow_requests")
|
||||
|
||||
assert [relationship] = json_response(conn, 200)
|
||||
assert to_string(other_user.id) == relationship["id"]
|
||||
end
|
||||
|
||||
test "/api/v1/follow_requests/:id/authorize works" do
|
||||
user = insert(:user, %{info: %{"locked" => true}})
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> post("/api/v1/follow_requests/#{other_user.id}/authorize")
|
||||
|
||||
assert relationship = json_response(conn, 200)
|
||||
assert to_string(other_user.id) == relationship["id"]
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == true
|
||||
end
|
||||
|
||||
test "/api/v1/follow_requests/:id/reject works" do
|
||||
user = insert(:user, %{info: %{"locked" => true}})
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> post("/api/v1/follow_requests/#{other_user.id}/reject")
|
||||
|
||||
assert relationship = json_response(conn, 200)
|
||||
assert to_string(other_user.id) == relationship["id"]
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
end
|
||||
end
|
||||
|
||||
test "account fetching", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
@ -792,6 +860,46 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "blocking / unblocking a domain", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
|
||||
|
||||
assert %{} = json_response(conn, 200)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
assert User.blocks?(user, other_user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
|
||||
|
||||
assert %{} = json_response(conn, 200)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
refute User.blocks?(user, other_user)
|
||||
end
|
||||
|
||||
test "getting a list of domain blocks" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, user} = User.block_domain(user, "bad.site")
|
||||
{:ok, user} = User.block_domain(user, "even.worse.site")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/domain_blocks")
|
||||
|
||||
domain_blocks = json_response(conn, 200)
|
||||
|
||||
assert "bad.site" in domain_blocks
|
||||
assert "even.worse.site" in domain_blocks
|
||||
end
|
||||
|
||||
test "unimplemented mute endpoints" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
|
|
@ -64,11 +64,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
|
||||
status = StatusView.render("status.json", %{activity: activity})
|
||||
|
||||
assert status.in_reply_to_id == note.id
|
||||
assert status.in_reply_to_id == to_string(note.id)
|
||||
|
||||
[status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
|
||||
|
||||
assert status.in_reply_to_id == note.id
|
||||
assert status.in_reply_to_id == to_string(note.id)
|
||||
end
|
||||
|
||||
test "contains mentions" do
|
||||
|
|
|
@ -580,6 +580,40 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "POST /api/statuses/unretweet/:id" do
|
||||
setup [:valid_user]
|
||||
|
||||
test "without valid credentials", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
conn = post(conn, "/api/statuses/unretweet/#{note_activity.id}.json")
|
||||
assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
|
||||
end
|
||||
|
||||
test "with credentials", %{conn: conn, user: current_user} do
|
||||
note_activity = insert(:note_activity)
|
||||
|
||||
request_path = "/api/statuses/retweet/#{note_activity.id}.json"
|
||||
|
||||
_response =
|
||||
conn
|
||||
|> with_credentials(current_user.nickname, "test")
|
||||
|> post(request_path)
|
||||
|
||||
request_path = String.replace(request_path, "retweet", "unretweet")
|
||||
|
||||
response =
|
||||
conn
|
||||
|> with_credentials(current_user.nickname, "test")
|
||||
|> post(request_path)
|
||||
|
||||
activity = Repo.get(Activity, note_activity.id)
|
||||
activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"])
|
||||
|
||||
assert json_response(response, 200) ==
|
||||
ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user})
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/account/register" do
|
||||
test "it creates a new user", %{conn: conn} do
|
||||
data = %{
|
||||
|
@ -762,6 +796,38 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
|
||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
||||
end
|
||||
|
||||
test "it locks an account", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/account/update_profile.json", %{
|
||||
"locked" => "true"
|
||||
})
|
||||
|
||||
user = Repo.get!(User, user.id)
|
||||
assert user.info["locked"] == true
|
||||
|
||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
||||
end
|
||||
|
||||
test "it unlocks an account", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/account/update_profile.json", %{
|
||||
"locked" => "false"
|
||||
})
|
||||
|
||||
user = Repo.get!(User, user.id)
|
||||
assert user.info["locked"] == false
|
||||
|
||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_user(_context) do
|
||||
|
@ -926,4 +992,72 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
:timer.sleep(1000)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/pleroma/friend_requests" do
|
||||
test "it lists friend requests" do
|
||||
user = insert(:user, %{info: %{"locked" => true}})
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> get("/api/pleroma/friend_requests")
|
||||
|
||||
assert [relationship] = json_response(conn, 200)
|
||||
assert other_user.id == relationship["id"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/friendships/approve" do
|
||||
test "it approves a friend request" do
|
||||
user = insert(:user, %{info: %{"locked" => true}})
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> post("/api/pleroma/friendships/approve", %{"user_id" => to_string(other_user.id)})
|
||||
|
||||
assert relationship = json_response(conn, 200)
|
||||
assert other_user.id == relationship["id"]
|
||||
assert relationship["follows_you"] == true
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/friendships/deny" do
|
||||
test "it denies a friend request" do
|
||||
user = insert(:user, %{info: %{"locked" => true}})
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> post("/api/pleroma/friendships/deny", %{"user_id" => to_string(other_user.id)})
|
||||
|
||||
assert relationship = json_response(conn, 200)
|
||||
assert other_user.id == relationship["id"]
|
||||
assert relationship["follows_you"] == false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -228,6 +228,17 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
assert status == updated_activity
|
||||
end
|
||||
|
||||
test "it unretweets an already retweeted status" do
|
||||
user = insert(:user)
|
||||
note_activity = insert(:note_activity)
|
||||
|
||||
{:ok, _status} = TwitterAPI.repeat(user, note_activity.id)
|
||||
{:ok, status} = TwitterAPI.unrepeat(user, note_activity.id)
|
||||
updated_activity = Activity.get_by_ap_id(note_activity.data["id"])
|
||||
|
||||
assert status == updated_activity
|
||||
end
|
||||
|
||||
test "it registers a new user and returns the user." do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
|
|
|
@ -59,7 +59,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||
"statusnet_profile_url" => user.ap_id,
|
||||
"cover_photo" => banner,
|
||||
"background_image" => nil,
|
||||
"is_local" => true
|
||||
"is_local" => true,
|
||||
"locked" => false
|
||||
}
|
||||
|
||||
assert represented == UserView.render("show.json", %{user: user})
|
||||
|
@ -94,7 +95,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||
"statusnet_profile_url" => user.ap_id,
|
||||
"cover_photo" => banner,
|
||||
"background_image" => nil,
|
||||
"is_local" => true
|
||||
"is_local" => true,
|
||||
"locked" => false
|
||||
}
|
||||
|
||||
assert represented == UserView.render("show.json", %{user: user, for: follower})
|
||||
|
@ -130,7 +132,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||
"statusnet_profile_url" => follower.ap_id,
|
||||
"cover_photo" => banner,
|
||||
"background_image" => nil,
|
||||
"is_local" => true
|
||||
"is_local" => true,
|
||||
"locked" => false
|
||||
}
|
||||
|
||||
assert represented == UserView.render("show.json", %{user: follower, for: user})
|
||||
|
@ -173,7 +176,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||
"statusnet_profile_url" => user.ap_id,
|
||||
"cover_photo" => banner,
|
||||
"background_image" => nil,
|
||||
"is_local" => true
|
||||
"is_local" => true,
|
||||
"locked" => false
|
||||
}
|
||||
|
||||
blocker = Repo.get(User, blocker.id)
|
||||
|
|
Loading…
Reference in a new issue