forked from AkkomaGang/akkoma
Resolve merge conflict
This commit is contained in:
commit
dec23500d8
56 changed files with 761 additions and 123 deletions
|
@ -8,6 +8,7 @@ variables:
|
|||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
DB_HOST: postgres
|
||||
MIX_ENV: test
|
||||
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
|
@ -23,15 +24,15 @@ before_script:
|
|||
- mix local.rebar --force
|
||||
- mix deps.get
|
||||
- mix compile --force
|
||||
- MIX_ENV=test mix ecto.create
|
||||
- MIX_ENV=test mix ecto.migrate
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
|
||||
lint:
|
||||
stage: lint
|
||||
script:
|
||||
- MIX_ENV=test mix format --check-formatted
|
||||
- mix format --check-formatted
|
||||
|
||||
unit-testing:
|
||||
stage: test
|
||||
script:
|
||||
- MIX_ENV=test mix test --trace
|
||||
- mix test --trace
|
||||
|
|
|
@ -30,7 +30,7 @@ While we don't provide docker files, other people have written very good ones. T
|
|||
### Dependencies
|
||||
|
||||
* Postgresql version 9.6 or newer
|
||||
* Elixir version 1.5 or newer. If your distribution only has an old version available, check [Elixir's install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
||||
* Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir's install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
||||
* Build-essential tools
|
||||
|
||||
### Configuration
|
||||
|
|
|
@ -98,7 +98,8 @@
|
|||
name: "Pleroma",
|
||||
email: "example@example.com",
|
||||
description: "A Pleroma instance, an alternative fediverse server",
|
||||
limit: 5000,
|
||||
limit: 5_000,
|
||||
remote_limit: 100_000,
|
||||
upload_limit: 16_000_000,
|
||||
avatar_upload_limit: 2_000_000,
|
||||
background_upload_limit: 4_000_000,
|
||||
|
@ -137,8 +138,8 @@
|
|||
logo_mask: true,
|
||||
logo_margin: "0.1em",
|
||||
background: "/static/aurora_borealis.jpg",
|
||||
redirect_root_no_login: "/~/main/all",
|
||||
redirect_root_login: "/~/main/friends",
|
||||
redirect_root_no_login: "/main/all",
|
||||
redirect_root_login: "/main/friends",
|
||||
show_instance_panel: true,
|
||||
scope_options_enabled: false,
|
||||
formatting_options_enabled: false,
|
||||
|
@ -220,6 +221,37 @@
|
|||
credentials: true,
|
||||
headers: ["Authorization", "Content-Type", "Idempotency-Key"]
|
||||
|
||||
config :pleroma, Pleroma.User,
|
||||
restricted_nicknames: [
|
||||
"about",
|
||||
"~",
|
||||
"main",
|
||||
"users",
|
||||
"settings",
|
||||
"objects",
|
||||
"activities",
|
||||
"web",
|
||||
"registration",
|
||||
"friend-requests",
|
||||
"pleroma",
|
||||
"api",
|
||||
"tag",
|
||||
"notice",
|
||||
"status",
|
||||
"user-search",
|
||||
"ostatus_subscribe",
|
||||
"oauth",
|
||||
"push",
|
||||
"relay",
|
||||
"inbox",
|
||||
".well-known",
|
||||
"nodeinfo",
|
||||
"auth",
|
||||
"proxy",
|
||||
"dev",
|
||||
"internal"
|
||||
]
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -63,6 +63,7 @@ config :pleroma, Pleroma.Mailer,
|
|||
* `email`: Email used to reach an Administrator/Moderator of the instance
|
||||
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``
|
||||
* `limit`: Posts character limit (CW/Subject included in the counter)
|
||||
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
||||
* `upload_limit`: File size limit of uploads (except for avatar, background, banner)
|
||||
* `avatar_upload_limit`: File size limit of user’s profile avatars
|
||||
* `background_upload_limit`: File size limit of user’s profile backgrounds
|
||||
|
|
|
@ -21,6 +21,8 @@ ProtectSystem=full
|
|||
PrivateDevices=false
|
||||
; Ensures that the service process and all its children can never gain new privileges through execve().
|
||||
NoNewPrivileges=true
|
||||
; Drops the sysadmin capability from the daemon.
|
||||
CapabilityBoundingSet=~CAP_SYS_ADMIN
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -75,7 +75,7 @@ def run(["gen" | rest]) do
|
|||
name =
|
||||
Common.get_option(
|
||||
options,
|
||||
:name,
|
||||
:instance_name,
|
||||
"What is the name of your instance? (e.g. Pleroma/Soykaf)"
|
||||
)
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.{Repo, Activity, Notification}
|
||||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||
@mastodon_notification_types %{
|
||||
"Create" => "mention",
|
||||
|
|
|
@ -10,6 +10,8 @@ defmodule Pleroma.HTTP do
|
|||
alias Pleroma.HTTP.Connection
|
||||
alias Pleroma.HTTP.RequestBuilder, as: Builder
|
||||
|
||||
@type t :: __MODULE__
|
||||
|
||||
@doc """
|
||||
Builds and perform http request.
|
||||
|
||||
|
|
|
@ -80,9 +80,8 @@ def get(%{id: user_id} = _user, id) do
|
|||
end
|
||||
|
||||
def clear(user) do
|
||||
query = from(n in Notification, where: n.user_id == ^user.id)
|
||||
|
||||
Repo.delete_all(query)
|
||||
from(n in Notification, where: n.user_id == ^user.id)
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def dismiss(%{id: user_id} = _user, id) do
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
defmodule Pleroma.Object do
|
||||
use Ecto.Schema
|
||||
alias Pleroma.{Repo, Object, User, Activity, HTML}
|
||||
alias Pleroma.{Repo, Object, User, Activity, HTML, ObjectTombstone}
|
||||
alias Pleroma.{Repo, Object, User, Activity, ObjectTombstone}
|
||||
import Ecto.{Query, Changeset}
|
||||
|
||||
schema "objects" do
|
||||
|
@ -66,8 +67,25 @@ def context_mapping(context) do
|
|||
Object.change(%Object{}, %{data: %{"id" => context}})
|
||||
end
|
||||
|
||||
def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
|
||||
%ObjectTombstone{
|
||||
id: id,
|
||||
formerType: type,
|
||||
deleted: deleted
|
||||
}
|
||||
|> Map.from_struct()
|
||||
end
|
||||
|
||||
def swap_object_with_tombstone(object) do
|
||||
tombstone = make_tombstone(object)
|
||||
|
||||
object
|
||||
|> Object.change(%{data: tombstone})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete(%Object{data: %{"id" => id}} = object) do
|
||||
with Repo.delete(object),
|
||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
|
||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||
{:ok, object}
|
||||
|
|
4
lib/pleroma/object_tombstone.ex
Normal file
4
lib/pleroma/object_tombstone.ex
Normal file
|
@ -0,0 +1,4 @@
|
|||
defmodule Pleroma.ObjectTombstone do
|
||||
@enforce_keys [:id, :formerType, :deleted]
|
||||
defstruct [:id, :formerType, :deleted, type: "Tombstone"]
|
||||
end
|
|
@ -13,6 +13,8 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Web.{OStatus, Websub, OAuth}
|
||||
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
|
||||
|
||||
require Logger
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||
|
@ -47,6 +49,14 @@ def auth_active?(%User{} = user) do
|
|||
!Pleroma.Config.get([:instance, :account_activation_required])
|
||||
end
|
||||
|
||||
def remote_or_auth_active?(%User{} = user), do: !user.local || auth_active?(user)
|
||||
|
||||
def visible_for?(%User{} = user, for_user \\ nil) do
|
||||
User.remote_or_auth_active?(user) || (for_user && for_user.id == user.id) ||
|
||||
User.superuser?(for_user)
|
||||
end
|
||||
|
||||
def superuser?(nil), do: false
|
||||
def superuser?(%User{} = user), do: user.info && User.Info.superuser?(user.info)
|
||||
|
||||
def avatar_url(user) do
|
||||
|
@ -197,6 +207,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
|||
|> validate_confirmation(:password)
|
||||
|> unique_constraint(:email)
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
|
||||
|> validate_format(:nickname, local_nickname_regex())
|
||||
|> validate_format(:email, @email_regex)
|
||||
|> validate_length(:bio, max: 1000)
|
||||
|
@ -330,6 +341,24 @@ def following?(%User{} = follower, %User{} = followed) do
|
|||
Enum.member?(follower.following, followed.follower_address)
|
||||
end
|
||||
|
||||
def follow_import(%User{} = follower, followed_identifiers)
|
||||
when is_list(followed_identifiers) do
|
||||
Enum.map(
|
||||
followed_identifiers,
|
||||
fn followed_identifier ->
|
||||
with %User{} = followed <- get_or_fetch(followed_identifier),
|
||||
{:ok, follower} <- maybe_direct_follow(follower, followed),
|
||||
{:ok, _} <- ActivityPub.follow(follower, followed) do
|
||||
followed
|
||||
else
|
||||
err ->
|
||||
Logger.debug("follow_import failed for #{followed_identifier} with: #{inspect(err)}")
|
||||
err
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def locked?(%User{} = user) do
|
||||
user.info.locked || false
|
||||
end
|
||||
|
@ -366,7 +395,11 @@ def get_cached_by_nickname(nickname) do
|
|||
end
|
||||
|
||||
def get_by_nickname(nickname) do
|
||||
Repo.get_by(User, nickname: nickname)
|
||||
Repo.get_by(User, nickname: nickname) ||
|
||||
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
|
||||
[local_nickname, _] = String.split(nickname, "@")
|
||||
Repo.get_by(User, nickname: local_nickname)
|
||||
end
|
||||
end
|
||||
|
||||
def get_by_nickname_or_email(nickname_or_email) do
|
||||
|
@ -595,6 +628,23 @@ def search(query, resolve \\ false) do
|
|||
Repo.all(q)
|
||||
end
|
||||
|
||||
def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
|
||||
Enum.map(
|
||||
blocked_identifiers,
|
||||
fn blocked_identifier ->
|
||||
with %User{} = blocked <- get_or_fetch(blocked_identifier),
|
||||
{:ok, blocker} <- block(blocker, blocked),
|
||||
{:ok, _} <- ActivityPub.block(blocker, blocked) do
|
||||
blocked
|
||||
else
|
||||
err ->
|
||||
Logger.debug("blocks_import failed for #{blocked_identifier} with: #{inspect(err)}")
|
||||
err
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def block(blocker, %User{ap_id: ap_id} = blocked) do
|
||||
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
||||
blocker =
|
||||
|
@ -648,6 +698,9 @@ def blocks?(user, %{ap_id: ap_id}) do
|
|||
end)
|
||||
end
|
||||
|
||||
def blocked_users(user),
|
||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
|
||||
|
||||
def block_domain(user, domain) do
|
||||
info_cng =
|
||||
user.info
|
||||
|
|
|
@ -56,10 +56,18 @@ defp check_actor_is_active(actor) do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_remote_limit(%{"object" => %{"content" => content}}) do
|
||||
limit = Pleroma.Config.get([:instance, :remote_limit])
|
||||
String.length(content) <= limit
|
||||
end
|
||||
|
||||
defp check_remote_limit(_), do: true
|
||||
|
||||
def insert(map, local \\ true) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map),
|
||||
:ok <- check_actor_is_active(map["actor"]),
|
||||
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
:ok <- insert_full_object(map) do
|
||||
{recipients, _, _} = get_recipients(map)
|
||||
|
@ -503,6 +511,12 @@ defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or
|
|||
|
||||
defp restrict_replies(query, _), do: query
|
||||
|
||||
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
|
||||
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
|
||||
end
|
||||
|
||||
defp restrict_reblogs(query, _), do: query
|
||||
|
||||
# Only search through last 100_000 activities by default
|
||||
defp restrict_recent(query, %{"whole_db" => true}), do: query
|
||||
|
||||
|
@ -561,6 +575,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
|||
|> restrict_media(opts)
|
||||
|> restrict_visibility(opts)
|
||||
|> restrict_replies(opts)
|
||||
|> restrict_reblogs(opts)
|
||||
end
|
||||
|
||||
def fetch_activities(recipients, opts \\ %{}) do
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Pleroma.{User, Object}
|
||||
alias Pleroma.{Activity, User, Object}
|
||||
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.Federator
|
||||
|
||||
require Logger
|
||||
|
@ -93,19 +94,15 @@ def followers(conn, %{"nickname" => nickname}) do
|
|||
end
|
||||
end
|
||||
|
||||
def outbox(conn, %{"nickname" => nickname, "max_id" => max_id}) do
|
||||
def outbox(conn, %{"nickname" => nickname} = params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("outbox.json", %{user: user, max_id: max_id}))
|
||||
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
|
||||
end
|
||||
end
|
||||
|
||||
def outbox(conn, %{"nickname" => nickname}) do
|
||||
outbox(conn, %{"nickname" => nickname, "max_id" => nil})
|
||||
end
|
||||
|
||||
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
true <- Utils.recipient_in_message(user.ap_id, params),
|
||||
|
@ -156,6 +153,57 @@ def relay(conn, _params) do
|
|||
end
|
||||
end
|
||||
|
||||
def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do
|
||||
if nickname == user.nickname do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
|
||||
else
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json("can't read inbox of #{nickname} as #{user.nickname}")
|
||||
end
|
||||
end
|
||||
|
||||
def update_outbox(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"nickname" => nickname, "type" => "Create"} = params
|
||||
) do
|
||||
if nickname == user.nickname do
|
||||
actor = user.ap_id()
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.drop(["id"])
|
||||
|> Map.put("actor", actor)
|
||||
|> Transmogrifier.fix_addressing()
|
||||
|
||||
object =
|
||||
params["object"]
|
||||
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||
|> Map.put("attributedTo", actor)
|
||||
|> Transmogrifier.fix_object()
|
||||
|
||||
with {:ok, %Activity{} = activity} <-
|
||||
ActivityPub.create(%{
|
||||
to: params["to"],
|
||||
actor: user,
|
||||
context: object["context"],
|
||||
object: object,
|
||||
additional: Map.take(params, ["cc"])
|
||||
}) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", activity.data["id"])
|
||||
|> json(activity.data)
|
||||
end
|
||||
else
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json("can't update outbox of #{nickname} as #{user.nickname}")
|
||||
end
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(404)
|
||||
|
|
|
@ -176,6 +176,53 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
|
|||
end
|
||||
end
|
||||
|
||||
def render("inbox.json", %{user: user, max_id: max_qid}) do
|
||||
params = %{
|
||||
"limit" => "10"
|
||||
}
|
||||
|
||||
params =
|
||||
if max_qid != nil do
|
||||
Map.put(params, "max_id", max_qid)
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
|
||||
|
||||
min_id = Enum.at(Enum.reverse(activities), 0).id
|
||||
max_id = Enum.at(activities, 0).id
|
||||
|
||||
collection =
|
||||
Enum.map(activities, fn act ->
|
||||
{:ok, data} = Transmogrifier.prepare_outgoing(act.data)
|
||||
data
|
||||
end)
|
||||
|
||||
iri = "#{user.ap_id}/inbox"
|
||||
|
||||
page = %{
|
||||
"id" => "#{iri}?max_id=#{max_id}",
|
||||
"type" => "OrderedCollectionPage",
|
||||
"partOf" => iri,
|
||||
"totalItems" => -1,
|
||||
"orderedItems" => collection,
|
||||
"next" => "#{iri}?max_id=#{min_id - 1}"
|
||||
}
|
||||
|
||||
if max_qid == nil do
|
||||
%{
|
||||
"id" => iri,
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => -1,
|
||||
"first" => page
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
else
|
||||
page |> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
end
|
||||
|
||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||
offset = (page - 1) * 10
|
||||
items = Enum.slice(collection, offset, 10)
|
||||
|
|
|
@ -102,7 +102,7 @@ def post(user, %{"status" => status} = data) do
|
|||
attachments,
|
||||
tags,
|
||||
get_content_type(data["content_type"]),
|
||||
data["no_attachment_links"]
|
||||
Enum.member?([true, "true"], data["no_attachment_links"])
|
||||
),
|
||||
context <- make_context(inReplyTo),
|
||||
cw <- data["spoiler_text"],
|
||||
|
|
|
@ -704,11 +704,9 @@ def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
|||
end
|
||||
end
|
||||
|
||||
# TODO: Use proper query
|
||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||
with blocked_users <- user.info.blocks || [],
|
||||
accounts <- Enum.map(blocked_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do
|
||||
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
||||
with blocked_accounts <- User.blocked_users(user) do
|
||||
res = AccountView.render("accounts.json", users: blocked_accounts, for: user, as: :user)
|
||||
json(conn, res)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,10 +11,55 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
alias Pleroma.HTML
|
||||
|
||||
def render("accounts.json", %{users: users} = opts) do
|
||||
render_many(users, AccountView, "account.json", opts)
|
||||
users
|
||||
|> render_many(AccountView, "account.json", opts)
|
||||
|> Enum.filter(&Enum.any?/1)
|
||||
end
|
||||
|
||||
def render("account.json", %{user: user} = opts) do
|
||||
if User.visible_for?(user, opts[:for]),
|
||||
do: do_render("account.json", opts),
|
||||
else: %{}
|
||||
end
|
||||
|
||||
def render("mention.json", %{user: user}) do
|
||||
%{
|
||||
id: to_string(user.id),
|
||||
acct: user.nickname,
|
||||
username: username_from_nickname(user.nickname),
|
||||
url: user.ap_id
|
||||
}
|
||||
end
|
||||
|
||||
def render("relationship.json", %{user: user, target: target}) do
|
||||
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
|
||||
|
||||
requested =
|
||||
if follow_activity do
|
||||
follow_activity.data["state"] == "pending"
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
%{
|
||||
id: to_string(target.id),
|
||||
following: User.following?(user, target),
|
||||
followed_by: User.following?(target, user),
|
||||
blocking: User.blocks?(user, target),
|
||||
muting: false,
|
||||
muting_notifications: false,
|
||||
requested: requested,
|
||||
domain_blocking: false,
|
||||
showing_reblogs: false,
|
||||
endorsed: false
|
||||
}
|
||||
end
|
||||
|
||||
def render("relationships.json", %{user: user, targets: targets}) do
|
||||
render_many(targets, AccountView, "relationship.json", user: user, as: :target)
|
||||
end
|
||||
|
||||
defp do_render("account.json", %{user: user} = opts) do
|
||||
image = User.avatar_url(user) |> MediaProxy.url()
|
||||
header = User.banner_url(user) |> MediaProxy.url()
|
||||
user_info = User.user_info(user)
|
||||
|
@ -72,43 +117,6 @@ def render("account.json", %{user: user} = opts) do
|
|||
}
|
||||
end
|
||||
|
||||
def render("mention.json", %{user: user}) do
|
||||
%{
|
||||
id: to_string(user.id),
|
||||
acct: user.nickname,
|
||||
username: username_from_nickname(user.nickname),
|
||||
url: user.ap_id
|
||||
}
|
||||
end
|
||||
|
||||
def render("relationship.json", %{user: user, target: target}) do
|
||||
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
|
||||
|
||||
requested =
|
||||
if follow_activity do
|
||||
follow_activity.data["state"] == "pending"
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
%{
|
||||
id: to_string(target.id),
|
||||
following: User.following?(user, target),
|
||||
followed_by: User.following?(target, user),
|
||||
blocking: User.blocks?(user, target),
|
||||
muting: false,
|
||||
muting_notifications: false,
|
||||
requested: requested,
|
||||
domain_blocking: false,
|
||||
showing_reblogs: false,
|
||||
endorsed: false
|
||||
}
|
||||
end
|
||||
|
||||
def render("relationships.json", %{user: user, targets: targets}) do
|
||||
render_many(targets, AccountView, "relationship.json", user: user, as: :target)
|
||||
end
|
||||
|
||||
defp username_from_nickname(string) when is_binary(string) do
|
||||
hd(String.split(string, "@"))
|
||||
end
|
||||
|
|
|
@ -138,7 +138,8 @@ def nodeinfo(conn, %{"version" => "2.0"}) do
|
|||
},
|
||||
accountActivationRequired: Keyword.get(instance, :account_activation_required, false),
|
||||
invitesEnabled: Keyword.get(instance, :invites_enabled, false),
|
||||
features: features
|
||||
features: features,
|
||||
restrictedNicknames: Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||
pipe_through(:authenticated_api)
|
||||
post("/blocks_import", UtilController, :blocks_import)
|
||||
post("/follow_import", UtilController, :follow_import)
|
||||
post("/change_password", UtilController, :change_password)
|
||||
post("/delete_account", UtilController, :delete_account)
|
||||
|
@ -281,6 +282,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/statuses/followers", TwitterAPI.Controller, :followers)
|
||||
get("/statuses/friends", TwitterAPI.Controller, :friends)
|
||||
get("/statuses/blocks", TwitterAPI.Controller, :blocks)
|
||||
get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status)
|
||||
get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation)
|
||||
|
||||
|
@ -410,6 +412,27 @@ defmodule Pleroma.Web.Router do
|
|||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||
end
|
||||
|
||||
pipeline :activitypub_client do
|
||||
plug(:accepts, ["activity+json"])
|
||||
plug(:fetch_session)
|
||||
plug(Pleroma.Plugs.OAuthPlug)
|
||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web.ActivityPub do
|
||||
pipe_through([:activitypub_client])
|
||||
|
||||
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
||||
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
||||
end
|
||||
|
||||
scope "/relay", Pleroma.Web.ActivityPub do
|
||||
pipe_through(:ap_relay)
|
||||
get("/", ActivityPubController, :relay)
|
||||
|
|
|
@ -161,16 +161,21 @@ def remote_users(%{data: %{"to" => to} = data}) do
|
|||
|> Enum.filter(fn user -> user && !user.local end)
|
||||
end
|
||||
|
||||
defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do
|
||||
# push an activity to remote accounts
|
||||
#
|
||||
defp send_to_user(%{info: %{salmon: salmon}}, feed, poster),
|
||||
do: send_to_user(salmon, feed, poster)
|
||||
|
||||
defp send_to_user(url, feed, poster) when is_binary(url) do
|
||||
with {:ok, %{status: code}} <-
|
||||
poster.(
|
||||
salmon,
|
||||
url,
|
||||
feed,
|
||||
[{"Content-Type", "application/magic-envelope+xml"}]
|
||||
) do
|
||||
Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end)
|
||||
Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
|
||||
else
|
||||
e -> Logger.debug(fn -> "Pushing Salmon to #{salmon} failed, #{inspect(e)}" end)
|
||||
e -> Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -184,6 +189,11 @@ defp send_to_user(_, _, _), do: nil
|
|||
"Undo",
|
||||
"Delete"
|
||||
]
|
||||
|
||||
@doc """
|
||||
Publishes an activity to remote accounts
|
||||
"""
|
||||
@spec publish(User.t(), Pleroma.Activity.t(), Pleroma.HTTP.t()) :: none
|
||||
def publish(user, activity, poster \\ &@httpoison.post/3)
|
||||
|
||||
def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||
|
|
|
@ -240,21 +240,22 @@ def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
|||
follow_import(conn, %{"list" => File.read!(listfile.path)})
|
||||
end
|
||||
|
||||
def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do
|
||||
Task.start(fn ->
|
||||
String.split(list)
|
||||
|> Enum.map(fn account ->
|
||||
with %User{} = follower <- User.get_cached_by_ap_id(user.ap_id),
|
||||
%User{} = followed <- User.get_or_fetch(account),
|
||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed) do
|
||||
ActivityPub.follow(follower, followed)
|
||||
else
|
||||
err -> Logger.debug("follow_import: following #{account} failed with #{inspect(err)}")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
|
||||
with followed_identifiers <- String.split(list),
|
||||
{:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
|
||||
json(conn, "job started")
|
||||
end
|
||||
end
|
||||
|
||||
json(conn, "job started")
|
||||
def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
||||
blocks_import(conn, %{"list" => File.read!(listfile.path)})
|
||||
end
|
||||
|
||||
def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
|
||||
with blocked_identifiers <- String.split(list),
|
||||
{:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
|
||||
json(conn, "job started")
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(%{assigns: %{user: user}} = conn, params) do
|
||||
|
|
|
@ -130,6 +130,15 @@ def show_user(conn, params) do
|
|||
def user_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
case TwitterAPI.get_user(user, params) do
|
||||
{:ok, target_user} ->
|
||||
# Twitter and ActivityPub use a different name and sense for this parameter.
|
||||
{include_rts, params} = Map.pop(params, "include_rts")
|
||||
|
||||
params =
|
||||
case include_rts do
|
||||
x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true")
|
||||
_ -> params
|
||||
end
|
||||
|
||||
activities = ActivityPub.fetch_user_activities(target_user, user, params)
|
||||
|
||||
conn
|
||||
|
@ -498,6 +507,14 @@ def friends(%{assigns: %{user: for_user}} = conn, params) do
|
|||
end
|
||||
end
|
||||
|
||||
def blocks(%{assigns: %{user: user}} = conn, _params) do
|
||||
with blocked_users <- User.blocked_users(user) do
|
||||
conn
|
||||
|> put_view(UserView)
|
||||
|> render("index.json", %{users: blocked_users, for: user})
|
||||
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
|
||||
|
@ -653,7 +670,7 @@ defp forbidden_json_reply(conn, error_message) do
|
|||
json_reply(conn, 403, json)
|
||||
end
|
||||
|
||||
def only_if_public_instance(conn = %{conn: %{assigns: %{user: _user}}}, _), do: conn
|
||||
def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def only_if_public_instance(conn, _) do
|
||||
if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
|
||||
|
|
|
@ -15,18 +15,44 @@ def render("show.json", %{user: user = %User{}} = assigns) do
|
|||
end
|
||||
|
||||
def render("index.json", %{users: users, for: user}) do
|
||||
render_many(users, Pleroma.Web.TwitterAPI.UserView, "user.json", for: user)
|
||||
users
|
||||
|> render_many(Pleroma.Web.TwitterAPI.UserView, "user.json", for: user)
|
||||
|> Enum.filter(&Enum.any?/1)
|
||||
end
|
||||
|
||||
def render("user.json", %{user: user = %User{}} = assigns) do
|
||||
if User.visible_for?(user, assigns[:for]),
|
||||
do: do_render("user.json", assigns),
|
||||
else: %{}
|
||||
end
|
||||
|
||||
def render("short.json", %{
|
||||
user: %User{
|
||||
nickname: nickname,
|
||||
id: id,
|
||||
ap_id: ap_id,
|
||||
name: name
|
||||
}
|
||||
}) do
|
||||
%{
|
||||
"fullname" => name,
|
||||
"id" => id,
|
||||
"ostatus_uri" => ap_id,
|
||||
"profile_url" => ap_id,
|
||||
"screen_name" => nickname
|
||||
}
|
||||
end
|
||||
|
||||
defp do_render("user.json", %{user: user = %User{}} = assigns) do
|
||||
for_user = assigns[:for]
|
||||
image = User.avatar_url(user) |> MediaProxy.url()
|
||||
|
||||
{following, follows_you, statusnet_blocking} =
|
||||
if assigns[:for] do
|
||||
if for_user do
|
||||
{
|
||||
User.following?(assigns[:for], user),
|
||||
User.following?(user, assigns[:for]),
|
||||
User.blocks?(assigns[:for], user)
|
||||
User.following?(for_user, user),
|
||||
User.following?(user, for_user),
|
||||
User.blocks?(for_user, user)
|
||||
}
|
||||
else
|
||||
{false, false, false}
|
||||
|
@ -51,7 +77,7 @@ def render("user.json", %{user: user = %User{}} = assigns) do
|
|||
data = %{
|
||||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
||||
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(assigns[:for])),
|
||||
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)),
|
||||
"favourites_count" => 0,
|
||||
"followers_count" => user_info[:follower_count],
|
||||
"following" => following,
|
||||
|
@ -97,23 +123,6 @@ def render("user.json", %{user: user = %User{}} = assigns) do
|
|||
end
|
||||
end
|
||||
|
||||
def render("short.json", %{
|
||||
user: %User{
|
||||
nickname: nickname,
|
||||
id: id,
|
||||
ap_id: ap_id,
|
||||
name: name
|
||||
}
|
||||
}) do
|
||||
%{
|
||||
"fullname" => name,
|
||||
"id" => id,
|
||||
"ostatus_uri" => ap_id,
|
||||
"profile_url" => ap_id,
|
||||
"screen_name" => nickname
|
||||
}
|
||||
end
|
||||
|
||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||
defp image_url(_), do: nil
|
||||
|
||||
|
|
4
mix.exs
4
mix.exs
|
@ -5,7 +5,7 @@ def project do
|
|||
[
|
||||
app: :pleroma,
|
||||
version: version("0.9.0"),
|
||||
elixir: "~> 1.4",
|
||||
elixir: "~> 1.7",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
elixirc_options: [warnings_as_errors: true],
|
||||
|
@ -71,7 +71,7 @@ defp deps do
|
|||
{:crypt,
|
||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
||||
{:cors_plug, "~> 1.5"},
|
||||
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false},
|
||||
{:ex_doc, "~> 0.19", only: :dev, runtime: false},
|
||||
{:web_push_encryption, "~> 0.2.1"},
|
||||
{:swoosh, "~> 0.20"},
|
||||
{:gen_smtp, "~> 0.13"},
|
||||
|
|
|
@ -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><!--server-generated-meta--><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.285fa56c62b811bbd37880f7e2656b13.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.60e190da7cc4cc4711dc.js></script><script type=text/javascript src=/static/js/vendor.48d4753220bd83360796.js></script><script type=text/javascript src=/static/js/app.6cb7378f44092df9536a.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><!--server-generated-meta--><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.44bcebbab7b3203648fdb538eb16129b.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.18ee0a4963e1e9ec7ea6.js></script><script type=text/javascript src=/static/js/vendor.21f9327c919db89265c3.js></script><script type=text/javascript src=/static/js/app.f5ecd4e55f996aad6b8a.js></script></body></html>
|
|
@ -4,8 +4,8 @@
|
|||
"logo": "/static/logo.png",
|
||||
"logoMask": true,
|
||||
"logoMargin": ".1em",
|
||||
"redirectRootNoLogin": "/~/main/all",
|
||||
"redirectRootLogin": "/~/main/friends",
|
||||
"redirectRootNoLogin": "/main/all",
|
||||
"redirectRootLogin": "/main/friends",
|
||||
"chatDisabled": false,
|
||||
"showInstanceSpecificPanel": false,
|
||||
"scopeOptionsEnabled": false,
|
||||
|
@ -16,5 +16,7 @@
|
|||
"alwaysShowSubjectInput": true,
|
||||
"hidePostStats": false,
|
||||
"hideUserStats": false,
|
||||
"loginMethod": "password"
|
||||
"loginMethod": "password",
|
||||
"webPushNotifications": false,
|
||||
"noAttachmentLinks": false
|
||||
}
|
||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
priv/static/static/css/app.44bcebbab7b3203648fdb538eb16129b.css
Normal file
BIN
priv/static/static/css/app.44bcebbab7b3203648fdb538eb16129b.css
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js
Normal file
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js.map
Normal file
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/manifest.18ee0a4963e1e9ec7ea6.js
Normal file
BIN
priv/static/static/js/manifest.18ee0a4963e1e9ec7ea6.js
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js
Normal file
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js.map
Normal file
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,18 +4,19 @@
|
|||
|
||||
defmodule Pleroma.ActivityTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.Activity
|
||||
import Pleroma.Factory
|
||||
|
||||
test "returns an activity by it's AP id" do
|
||||
activity = insert(:note_activity)
|
||||
found_activity = Pleroma.Activity.get_by_ap_id(activity.data["id"])
|
||||
found_activity = Activity.get_by_ap_id(activity.data["id"])
|
||||
|
||||
assert activity == found_activity
|
||||
end
|
||||
|
||||
test "returns activities by it's objects AP ids" do
|
||||
activity = insert(:note_activity)
|
||||
[found_activity] = Pleroma.Activity.all_by_object_ap_id(activity.data["object"]["id"])
|
||||
[found_activity] = Activity.all_by_object_ap_id(activity.data["object"]["id"])
|
||||
|
||||
assert activity == found_activity
|
||||
end
|
||||
|
@ -23,8 +24,7 @@ test "returns activities by it's objects AP ids" do
|
|||
test "returns the activity that created an object" do
|
||||
activity = insert(:note_activity)
|
||||
|
||||
found_activity =
|
||||
Pleroma.Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"])
|
||||
found_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"])
|
||||
|
||||
assert activity == found_activity
|
||||
end
|
||||
|
|
9
test/fixtures/activitypub-client-post-activity.json
vendored
Normal file
9
test/fixtures/activitypub-client-post-activity.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"@context": ["https://www.w3.org/ns/activitystreams", {"@language": "en-GB"}],
|
||||
"type": "Create",
|
||||
"object": {
|
||||
"type": "Note",
|
||||
"content": "It's a note"
|
||||
},
|
||||
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
|
@ -36,6 +36,8 @@ test "deletes an object" do
|
|||
found_object = Object.get_by_ap_id(object.data["id"])
|
||||
|
||||
refute object == found_object
|
||||
|
||||
assert found_object.data["type"] == "Tombstone"
|
||||
end
|
||||
|
||||
test "ensures cache is cleared for the object" do
|
||||
|
@ -51,6 +53,8 @@ test "ensures cache is cleared for the object" do
|
|||
cached_object = Object.get_cached_by_ap_id(object.data["id"])
|
||||
|
||||
refute object == cached_object
|
||||
|
||||
assert cached_object.data["type"] == "Tombstone"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -153,6 +153,20 @@ test "it requires an email, name, nickname and password, bio is optional" do
|
|||
end)
|
||||
end
|
||||
|
||||
test "it restricts certain nicknames" do
|
||||
[restricted_name | _] = Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
|
||||
|
||||
assert is_bitstring(restricted_name)
|
||||
|
||||
params =
|
||||
@full_user_data
|
||||
|> Map.put(:nickname, restricted_name)
|
||||
|
||||
changeset = User.register_changeset(%User{}, params)
|
||||
|
||||
refute changeset.valid?
|
||||
end
|
||||
|
||||
test "it sets the password_hash, ap_id and following fields" do
|
||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||
|
||||
|
@ -264,6 +278,24 @@ test "gets an existing user, case insensitive" do
|
|||
assert user == fetched_user
|
||||
end
|
||||
|
||||
test "gets an existing user by fully qualified nickname" do
|
||||
user = insert(:user)
|
||||
|
||||
fetched_user =
|
||||
User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
|
||||
|
||||
assert user == fetched_user
|
||||
end
|
||||
|
||||
test "gets an existing user by fully qualified nickname, case insensitive" do
|
||||
user = insert(:user, nickname: "nick")
|
||||
casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
|
||||
|
||||
fetched_user = User.get_or_fetch_by_nickname(casing_altered_fqn)
|
||||
|
||||
assert user == fetched_user
|
||||
end
|
||||
|
||||
test "fetches an external user via ostatus if no user exists" do
|
||||
fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
|
||||
assert fetched_user.nickname == "shp@social.heldscal.la"
|
||||
|
@ -471,6 +503,21 @@ test "it sets the info->follower_count property" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "follow_import" do
|
||||
test "it imports user followings from list" do
|
||||
[user1, user2, user3] = insert_list(3, :user)
|
||||
|
||||
identifiers = [
|
||||
user2.ap_id,
|
||||
user3.nickname
|
||||
]
|
||||
|
||||
result = User.follow_import(user1, identifiers)
|
||||
assert is_list(result)
|
||||
assert result == [user2, user3]
|
||||
end
|
||||
end
|
||||
|
||||
describe "blocks" do
|
||||
test "it blocks people" do
|
||||
user = insert(:user)
|
||||
|
@ -570,6 +617,21 @@ test "unblocks domains" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "blocks_import" do
|
||||
test "it imports user blocks from list" do
|
||||
[user1, user2, user3] = insert_list(3, :user)
|
||||
|
||||
identifiers = [
|
||||
user2.ap_id,
|
||||
user3.nickname
|
||||
]
|
||||
|
||||
result = User.blocks_import(user1, identifiers)
|
||||
assert is_list(result)
|
||||
assert result == [user2, user3]
|
||||
end
|
||||
end
|
||||
|
||||
test "get recipients from activity" do
|
||||
actor = insert(:user)
|
||||
user = insert(:user, local: true)
|
||||
|
|
|
@ -112,6 +112,32 @@ test "it inserts an incoming activity into the database", %{conn: conn} do
|
|||
:timer.sleep(500)
|
||||
assert Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "it rejects reads from other users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
otheruser = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, otheruser)
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/users/#{user.nickname}/inbox")
|
||||
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it returns a note activity in a collection", %{conn: conn} do
|
||||
note_activity = insert(:direct_note_activity)
|
||||
user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/users/#{user.nickname}/inbox")
|
||||
|
||||
assert response(conn, 200) =~ note_activity.data["object"]["content"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "/users/:nickname/outbox" do
|
||||
|
@ -138,6 +164,34 @@ test "it returns an announce activity in a collection", %{conn: conn} do
|
|||
|
||||
assert response(conn, 200) =~ announce_activity.data["object"]
|
||||
end
|
||||
|
||||
test "it rejects posts from other users", %{conn: conn} do
|
||||
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||
user = insert(:user)
|
||||
otheruser = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, otheruser)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it inserts an incoming activity into the database", %{conn: conn} do
|
||||
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
result = json_response(conn, 201)
|
||||
assert Activity.get_by_ap_id(result["id"])
|
||||
end
|
||||
end
|
||||
|
||||
describe "/users/:nickname/followers" do
|
||||
|
|
|
@ -31,6 +31,24 @@ test "it returns a user" do
|
|||
end
|
||||
|
||||
describe "insertion" do
|
||||
test "drops activities beyond a certain limit" do
|
||||
limit = Pleroma.Config.get([:instance, :remote_limit])
|
||||
|
||||
random_text =
|
||||
:crypto.strong_rand_bytes(limit + 1)
|
||||
|> Base.encode64()
|
||||
|> binary_part(0, limit + 1)
|
||||
|
||||
data = %{
|
||||
"ok" => true,
|
||||
"object" => %{
|
||||
"content" => random_text
|
||||
}
|
||||
}
|
||||
|
||||
assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
|
||||
end
|
||||
|
||||
test "returns the activity if one with the same id is already in" do
|
||||
activity = insert(:note_activity)
|
||||
{:ok, new_activity} = ActivityPub.insert(activity.data)
|
||||
|
@ -180,6 +198,16 @@ test "doesn't return blocked activities" do
|
|||
assert Enum.member?(activities, activity_one)
|
||||
end
|
||||
|
||||
test "excludes reblogs on request" do
|
||||
user = insert(:user)
|
||||
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
|
||||
{:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
|
||||
|
||||
[activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
|
||||
|
||||
assert activity == expected_activity
|
||||
end
|
||||
|
||||
describe "public fetch activities" do
|
||||
test "doesn't retrieve unlisted activities" do
|
||||
user = insert(:user)
|
||||
|
@ -482,7 +510,7 @@ test "it creates a delete activity and deletes the original object" do
|
|||
|
||||
assert Repo.get(Activity, delete.id) != nil
|
||||
|
||||
assert Repo.get(Object, object.id) == nil
|
||||
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -296,7 +296,7 @@ test "when you created it", %{conn: conn} do
|
|||
|
||||
assert %{} = json_response(conn, 200)
|
||||
|
||||
assert Repo.get(Activity, activity.id) == nil
|
||||
refute Repo.get(Activity, activity.id)
|
||||
end
|
||||
|
||||
test "when you didn't create it", %{conn: conn} do
|
||||
|
@ -840,6 +840,26 @@ test "gets an users media", %{conn: conn} do
|
|||
assert [%{"id" => id}] = json_response(conn, 200)
|
||||
assert id == to_string(image_post.id)
|
||||
end
|
||||
|
||||
test "gets a user's statuses without reblogs", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||
{:ok, _, _} = CommonAPI.repeat(post.id, user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
|
||||
|
||||
assert [%{"id" => id}] = json_response(conn, 200)
|
||||
assert id == to_string(post.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
|
||||
|
||||
assert [%{"id" => id}] = json_response(conn, 200)
|
||||
assert id == to_string(post.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "user relationships" do
|
||||
|
|
|
@ -19,6 +19,17 @@ test "nodeinfo shows staff accounts", %{conn: conn} do
|
|||
assert user.ap_id in result["metadata"]["staffAccounts"]
|
||||
end
|
||||
|
||||
test "nodeinfo shows restricted nicknames", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> get("/nodeinfo/2.0.json")
|
||||
|
||||
assert result = json_response(conn, 200)
|
||||
|
||||
assert Pleroma.Config.get([Pleroma.User, :restricted_nicknames]) ==
|
||||
result["metadata"]["restrictedNicknames"]
|
||||
end
|
||||
|
||||
test "returns 404 when federation is disabled", %{conn: conn} do
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|
|
|
@ -25,7 +25,7 @@ test "it removes the mentioned activity" do
|
|||
|
||||
refute Repo.get(Activity, note.id)
|
||||
refute Repo.get(Activity, like.id)
|
||||
refute Object.get_by_ap_id(note.data["object"]["id"])
|
||||
assert Object.get_by_ap_id(note.data["object"]["id"]).data["type"] == "Tombstone"
|
||||
assert Repo.get(Activity, second_note.id)
|
||||
assert Object.get_by_ap_id(second_note.data["object"]["id"])
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.{User, Repo}
|
||||
alias Pleroma.{User, Repo, Object}
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||
|
||||
|
@ -114,6 +114,22 @@ test "404s on nonexisting objects", %{conn: conn} do
|
|||
|> response(404)
|
||||
end
|
||||
|
||||
test "404s on deleted objects", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
|
||||
object = Object.get_by_ap_id(note_activity.data["object"]["id"])
|
||||
|
||||
conn
|
||||
|> get("/objects/#{uuid}")
|
||||
|> response(200)
|
||||
|
||||
Object.delete(object)
|
||||
|
||||
conn
|
||||
|> get("/objects/#{uuid}")
|
||||
|> response(404)
|
||||
end
|
||||
|
||||
test "gets an activity", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
||||
|
|
|
@ -112,6 +112,8 @@ test "with credentials", %{conn: conn, user: user} do
|
|||
end
|
||||
|
||||
describe "GET /statuses/public_timeline.json" do
|
||||
setup [:valid_user]
|
||||
|
||||
test "returns statuses", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
activities = ActivityBuilder.insert_list(30, %{}, %{user: user})
|
||||
|
@ -145,14 +147,44 @@ test "returns 403 to unauthenticated request when the instance is not public", %
|
|||
Application.put_env(:pleroma, :instance, instance)
|
||||
end
|
||||
|
||||
test "returns 200 to authenticated request when the instance is not public",
|
||||
%{conn: conn, user: user} do
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:public, false)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
|
||||
conn
|
||||
|> with_credentials(user.nickname, "test")
|
||||
|> get("/api/statuses/public_timeline.json")
|
||||
|> json_response(200)
|
||||
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:public, true)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
end
|
||||
|
||||
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
|
||||
conn
|
||||
|> get("/api/statuses/public_timeline.json")
|
||||
|> json_response(200)
|
||||
end
|
||||
|
||||
test "returns 200 to authenticated request when the instance is public",
|
||||
%{conn: conn, user: user} do
|
||||
conn
|
||||
|> with_credentials(user.nickname, "test")
|
||||
|> get("/api/statuses/public_timeline.json")
|
||||
|> json_response(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /statuses/public_and_external_timeline.json" do
|
||||
setup [:valid_user]
|
||||
|
||||
test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|
@ -171,11 +203,39 @@ test "returns 403 to unauthenticated request when the instance is not public", %
|
|||
Application.put_env(:pleroma, :instance, instance)
|
||||
end
|
||||
|
||||
test "returns 200 to authenticated request when the instance is not public",
|
||||
%{conn: conn, user: user} do
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:public, false)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
|
||||
conn
|
||||
|> with_credentials(user.nickname, "test")
|
||||
|> get("/api/statuses/public_and_external_timeline.json")
|
||||
|> json_response(200)
|
||||
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:public, true)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
end
|
||||
|
||||
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
|
||||
conn
|
||||
|> get("/api/statuses/public_and_external_timeline.json")
|
||||
|> json_response(200)
|
||||
end
|
||||
|
||||
test "returns 200 to authenticated request when the instance is public",
|
||||
%{conn: conn, user: user} do
|
||||
conn
|
||||
|> with_credentials(user.nickname, "test")
|
||||
|> get("/api/statuses/public_and_external_timeline.json")
|
||||
|> json_response(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /statuses/show/:id.json" do
|
||||
|
@ -519,6 +579,34 @@ test "with credentials screen_name", %{conn: conn, user: current_user} do
|
|||
assert length(response) == 1
|
||||
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||
end
|
||||
|
||||
test "with credentials with user_id, excluding RTs", %{conn: conn, user: current_user} do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = ActivityBuilder.insert(%{"id" => 1, "type" => "Create"}, %{user: user})
|
||||
{:ok, _} = ActivityBuilder.insert(%{"id" => 2, "type" => "Announce"}, %{user: user})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> with_credentials(current_user.nickname, "test")
|
||||
|> get("/api/statuses/user_timeline.json", %{
|
||||
"user_id" => user.id,
|
||||
"include_rts" => "false"
|
||||
})
|
||||
|
||||
response = json_response(conn, 200)
|
||||
|
||||
assert length(response) == 1
|
||||
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/statuses/user_timeline.json", %{"user_id" => user.id, "include_rts" => "0"})
|
||||
|
||||
response = json_response(conn, 200)
|
||||
|
||||
assert length(response) == 1
|
||||
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /friendships/create.json" do
|
||||
|
@ -1057,6 +1145,24 @@ test "it returns the followers for a hidden network if requested by the user the
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /api/statuses/blocks" do
|
||||
test "it returns the list of users blocked by requester", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, user} = User.block(user, other_user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/statuses/blocks")
|
||||
|
||||
expected = UserView.render("index.json", %{users: [other_user], for: user})
|
||||
result = json_response(conn, 200)
|
||||
assert Enum.sort(expected) == Enum.sort(result)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/statuses/friends" do
|
||||
test "it returns the logged in user's friends", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
|
35
test/web/twitter_api/util_controller_test.exs
Normal file
35
test/web/twitter_api/util_controller_test.exs
Normal file
|
@ -0,0 +1,35 @@
|
|||
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "POST /api/pleroma/follow_import" do
|
||||
test "it returns HTTP 200", %{conn: conn} do
|
||||
user1 = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> assign(:user, user1)
|
||||
|> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"})
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response == "job started"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/blocks_import" do
|
||||
test "it returns HTTP 200", %{conn: conn} do
|
||||
user1 = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> assign(:user, user1)
|
||||
|> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"})
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response == "job started"
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue