diff options
| author | Mark Felder <feld@FreeBSD.org> | 2019-02-19 16:58:28 +0000 | 
|---|---|---|
| committer | Mark Felder <feld@FreeBSD.org> | 2019-02-19 16:58:28 +0000 | 
| commit | 2d21ea1a0e1be2e737dc4d283e72207580856214 (patch) | |
| tree | f377cf25c36de97d664f13cebacb6ff2e0d43b91 | |
| parent | 10a11f083ca543022c61e24b1ec0cc83811d6d06 (diff) | |
| parent | 1dd718e83c76db218f12a98344b568fe10ecbefe (diff) | |
| download | pleroma-2d21ea1a0e1be2e737dc4d283e72207580856214.tar.gz pleroma-2d21ea1a0e1be2e737dc4d283e72207580856214.zip | |
Merge branch 'develop' into fix/twittercards
82 files changed, 1316 insertions, 310 deletions
| diff --git a/.credo.exs b/.credo.exs index 94e19c4b5..580cb2705 100644 --- a/.credo.exs +++ b/.credo.exs @@ -19,7 +19,7 @@          #          # You can give explicit globs or simply directories.          # In the latter case `**/*.{ex,exs}` will be used. -        included: ["lib/", "src/", "web/", "apps/"], +        included: ["lib/", "src/", "web/", "apps/", "test/"],          excluded: [~r"/_build/", ~r"/deps/"]        },        # diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b59445895..6deb0a1de 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,8 @@  image: elixir:1.7.2  services: -  - postgres:9.6.2 +  - name: postgres:9.6.2 +    command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]  variables:    POSTGRES_DB: pleroma_test @@ -35,4 +36,4 @@ lint:  unit-testing:    stage: test    script: -    - mix test --trace +    - mix test --trace --preload-modules diff --git a/config/config.exs b/config/config.exs index 5db0ea9aa..317299bf1 100644 --- a/config/config.exs +++ b/config/config.exs @@ -162,7 +162,9 @@ config :pleroma, :instance,    mrf_transparency: true,    autofollowed_nicknames: [],    max_pinned_statuses: 1, -  no_attachment_links: false +  no_attachment_links: false, +  welcome_user_nickname: nil, +  welcome_message: nil  config :pleroma, :markup,    # XXX - unfortunately, inline images must be enabled by default right now, because @@ -228,8 +230,8 @@ config :pleroma, :mrf_rejectnonpublic,    allow_direct: false  config :pleroma, :mrf_hellthread, -  delist_threshold: 5, -  reject_threshold: 10 +  delist_threshold: 10, +  reject_threshold: 20  config :pleroma, :mrf_simple,    media_removal: [], @@ -330,14 +332,16 @@ config :pleroma, Pleroma.User,      "web"    ] -config :pleroma, Pleroma.Web.Federator, max_jobs: 50 -  config :pleroma, Pleroma.Web.Federator.RetryQueue,    enabled: false,    max_jobs: 20,    initial_timeout: 30,    max_retries: 5 +config :pleroma, Pleroma.Jobs, +  federator_incoming: [max_jobs: 50], +  federator_outgoing: [max_jobs: 50] +  # 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" diff --git a/config/dev.exs b/config/dev.exs index 8f89aa03c..f77bb9976 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -16,7 +16,8 @@ config :pleroma, Pleroma.Web.Endpoint,    debug_errors: true,    code_reloader: true,    check_origin: false, -  watchers: [] +  watchers: [], +  secure_cookie_flag: false  config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local diff --git a/config/test.exs b/config/test.exs index 412970d93..fbeba0919 100644 --- a/config/test.exs +++ b/config/test.exs @@ -44,6 +44,8 @@ config :web_push_encryption, :vapid_details,      "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",    private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA" +config :pleroma, Pleroma.Jobs, testing: [max_jobs: 2] +  try do    import_config "test.secret.exs"  rescue diff --git a/docs/Clients.md b/docs/Clients.md index 057f12392..c3d776488 100644 --- a/docs/Clients.md +++ b/docs/Clients.md @@ -7,6 +7,7 @@ Feel free to contact us to be added to this list!  - Homepage: <http://www.pleroma.com/desktop-app/>  - Source Code: ???  - Platforms: Windows, Mac, (Linux?) +- Features: Streaming Ready  ### Social  - Source Code: <https://gitlab.gnome.org/BrainBlasted/Social> @@ -19,6 +20,7 @@ Feel free to contact us to be added to this list!  - Source Code: <https://github.com/h3poteto/whalebird-desktop>  - Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)  - Platforms: Windows, Mac, Linux +- Features: Streaming Ready  ## Handheld  ### Amaroq @@ -26,60 +28,71 @@ Feel free to contact us to be added to this list!  - Source Code: <https://github.com/ReticentJohn/Amaroq>  - Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)  - Platforms: iOS +- Features: No Streaming  ### Nekonium  - Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)  - Source: <https://git.gdgd.jp.net/lin/nekonium/>  - Contact: [@lin@pleroma.gdgd.jp.net](https://pleroma.gdgd.jp.net/users/lin)  - Platforms: Android +- Features: Streaming Ready  ### Mastalab  - Source Code: <https://gitlab.com/tom79/mastalab/>  - Contact: [@tom79@mastodon.social](https://mastodon.social/users/tom79)  - Platforms: Android +- Features: Streaming Ready  ### Roma  - Homepage: <http://www.pleroma.com/>  - Source Code: ???  - Platforms: iOS, Android +- Features: No Streaming  ### Tootdon  - Homepage: <http://tootdon.club/>, <http://blog.mastodon-tootdon.com/>  - Source Code: ???  - Contact: [@tootdon@mstdn.jp](https://mstdn.jp/users/tootdon)  - Platforms: Android, iOS +- Features: No Streaming  ### Tusky  - Homepage: <https://tuskyapp.github.io/>  - Source Code: <https://github.com/tuskyapp/Tusky>  - Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)  - Platforms: Android +- Features: No Streaming  ### Twidere  - Homepage: <https://twidere.mariotaku.org/>  - Source Code: <https://github.com/TwidereProject/Twidere-Android/>, <https://github.com/TwidereProject/Twidere-iOS/>  - Contact: <me@mariotaku.org>  - Platform: Android, iOS +- Features: No Streaming  ## Alternative Web Interfaces  ### Brutaldon  - Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>  - Source Code: <https://github.com/jfmcbrayer/brutaldon>  - Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc) +- Features: No Streaming  ### Feather  - Source Code: <https://github.com/kaniini/feather>  - Contact: [@kaniini@pleroma.site](https://pleroma.site/kaniini) +- Features: No Streaming  ### Halcyon  - Source Code: <https://notabug.org/halcyon-suite/halcyon>  - Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon) +- Features: Streaming Ready  ### Pinafore  - Homepage: <https://pinafore.social/>  - Source Code: <https://github.com/nolanlawson/pinafore>  - Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)  - Note: Pleroma support is a secondary goal +- Features: No Streaming  ### Sengi  - Source Code: <https://github.com/NicolasConstant/sengi> diff --git a/docs/Differences-in-MastodonAPI-Responses.md b/docs/Differences-in-MastodonAPI-Responses.md new file mode 100644 index 000000000..f6a5b6461 --- /dev/null +++ b/docs/Differences-in-MastodonAPI-Responses.md @@ -0,0 +1,11 @@ +# Differences in Mastodon API responses from vanilla Mastodon + +A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma <version>)" present in `version` field in response from `/api/v1/instance`  + +## Flake IDs + +Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are sortable strings + +## Attachment cap + +Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting. diff --git a/docs/Pleroma-API.md b/docs/Pleroma-API.md index e1448d3f0..379d3dbed 100644 --- a/docs/Pleroma-API.md +++ b/docs/Pleroma-API.md @@ -94,3 +94,17 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi  ## `/api/pleroma/admin/`…  See [Admin-API](Admin-API.md) + +## `/api/v1/pleroma/flavour/:flavour` +* Method `POST` +* Authentication: required +* Response: JSON string. Returns the user flavour or the default one on success, otherwise returns `{"error": "error_msg"}` +* Example response: "glitch" +* Note: This is intended to be used only by mastofe + +## `/api/v1/pleroma/flavour` +* Method `GET` +* Authentication: required +* Response: JSON string. Returns the user flavour or the default one. +* Example response: "glitch" +* Note: This is intended to be used only by mastofe diff --git a/docs/config.md b/docs/config.md index 74badd0da..6647549a2 100644 --- a/docs/config.md +++ b/docs/config.md @@ -36,14 +36,15 @@ This filter replaces the filename (not the path) of an upload. For complete obfu  An example for Sendgrid adapter: -``` +```exs  config :pleroma, Pleroma.Mailer,    adapter: Swoosh.Adapters.Sendgrid,    api_key: "YOUR_API_KEY"  ```  An example for SMTP adapter: -``` + +```exs  config :pleroma, Pleroma.Mailer,    adapter: Swoosh.Adapters.SMTP,    relay: "smtp.gmail.com", @@ -97,6 +98,8 @@ config :pleroma, Pleroma.Mailer,  * `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.  * `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.  * `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses +* `welcome_message`: A message that will be send to a newly registered users as a direct message. +* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.  ## :logger  * `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog @@ -207,7 +210,7 @@ their ActivityPub ID.  An example: -``` +```exs  config :pleroma, :mrf_user_allowlist,    "example.org": ["https://example.org/users/admin"]  ``` @@ -236,18 +239,34 @@ the source code is here: https://github.com/koto-bank/kocaptcha. The default end  Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter. Example: -``` +```exs  config :pleroma, :admin_token, "somerandomtoken"  ```  You can then do -``` + +```sh  curl "http://localhost:4000/api/pleroma/admin/invite_token?admin_token=somerandomtoken"  ``` -## Pleroma.Web.Federator +## Pleroma.Jobs + +A list of job queues and their settings. + +Job queue settings: + +* `max_jobs`: The maximum amount of parallel jobs running at the same time. + +Example: + +```exs +config :pleroma, Pleroma.Jobs, +  federator_incoming: [max_jobs: 50], +  federator_outgoing: [max_jobs: 50] +``` + +This config contains two queues: `federator_incoming` and `federator_outgoing`. Both have the `max_jobs` set to `50`. -* `max_jobs`: The maximum amount of parallel federation jobs running at the same time.  ## Pleroma.Web.Federator.RetryQueue diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index d67e2cdc8..d2523c045 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -108,9 +108,10 @@ defmodule Pleroma.Application do          hackney_pool_children() ++          [            worker(Pleroma.Web.Federator.RetryQueue, []), -          worker(Pleroma.Web.Federator, []),            worker(Pleroma.Stats, []), -          worker(Pleroma.Web.Push, []) +          worker(Pleroma.Web.Push, []), +          worker(Pleroma.Jobs, []), +          worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary)          ] ++          streamer_child() ++          chat_child() ++ diff --git a/lib/pleroma/jobs.ex b/lib/pleroma/jobs.ex new file mode 100644 index 000000000..24b7e5e46 --- /dev/null +++ b/lib/pleroma/jobs.ex @@ -0,0 +1,152 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Jobs do +  @moduledoc """ +  A basic job queue +  """ +  use GenServer + +  require Logger + +  def init(args) do +    {:ok, args} +  end + +  def start_link do +    queues = +      Pleroma.Config.get(Pleroma.Jobs) +      |> Enum.map(fn {name, _} -> create_queue(name) end) +      |> Enum.into(%{}) + +    state = %{ +      queues: queues, +      refs: %{} +    } + +    GenServer.start_link(__MODULE__, state, name: __MODULE__) +  end + +  def create_queue(name) do +    {name, {:sets.new(), []}} +  end + +  @doc """ +  Enqueues a job. + +  Returns `:ok`. + +  ## Arguments + +  - `queue_name` - a queue name(must be specified in the config). +  - `mod` - a worker module (must have `perform` function). +  - `args` - a list of arguments for the `perform` function of the worker module. +  - `priority` - a job priority (`0` by default). + +  ## Examples + +  Enqueue `Module.perform/0` with `priority=1`: + +      iex> Pleroma.Jobs.enqueue(:example_queue, Module, []) +      :ok + +  Enqueue `Module.perform(:job_name)` with `priority=5`: + +      iex> Pleroma.Jobs.enqueue(:example_queue, Module, [:job_name], 5) +      :ok + +  Enqueue `Module.perform(:another_job, data)` with `priority=1`: + +      iex> data = "foobar" +      iex> Pleroma.Jobs.enqueue(:example_queue, Module, [:another_job, data]) +      :ok + +  Enqueue `Module.perform(:foobar_job, :foo, :bar, 42)` with `priority=1`: + +      iex> Pleroma.Jobs.enqueue(:example_queue, Module, [:foobar_job, :foo, :bar, 42]) +      :ok + +  """ + +  def enqueue(queue_name, mod, args, priority \\ 1) + +  if Mix.env() == :test do +    def enqueue(_queue_name, mod, args, _priority) do +      apply(mod, :perform, args) +    end +  else +    @spec enqueue(atom(), atom(), [any()], integer()) :: :ok +    def enqueue(queue_name, mod, args, priority) do +      GenServer.cast(__MODULE__, {:enqueue, queue_name, mod, args, priority}) +    end +  end + +  def handle_cast({:enqueue, queue_name, mod, args, priority}, state) do +    {running_jobs, queue} = state[:queues][queue_name] + +    queue = enqueue_sorted(queue, {mod, args}, priority) + +    state = +      state +      |> update_queue(queue_name, {running_jobs, queue}) +      |> maybe_start_job(queue_name, running_jobs, queue) + +    {:noreply, state} +  end + +  def handle_info({:DOWN, ref, :process, _pid, _reason}, state) do +    queue_name = state.refs[ref] + +    {running_jobs, queue} = state[:queues][queue_name] + +    running_jobs = :sets.del_element(ref, running_jobs) + +    state = +      state +      |> remove_ref(ref) +      |> update_queue(queue_name, {running_jobs, queue}) +      |> maybe_start_job(queue_name, running_jobs, queue) + +    {:noreply, state} +  end + +  def maybe_start_job(state, queue_name, running_jobs, queue) do +    if :sets.size(running_jobs) < Pleroma.Config.get([__MODULE__, queue_name, :max_jobs]) && +         queue != [] do +      {{mod, args}, queue} = queue_pop(queue) +      {:ok, pid} = Task.start(fn -> apply(mod, :perform, args) end) +      mref = Process.monitor(pid) + +      state +      |> add_ref(queue_name, mref) +      |> update_queue(queue_name, {:sets.add_element(mref, running_jobs), queue}) +    else +      state +    end +  end + +  def enqueue_sorted(queue, element, priority) do +    [%{item: element, priority: priority} | queue] +    |> Enum.sort_by(fn %{priority: priority} -> priority end) +  end + +  def queue_pop([%{item: element} | queue]) do +    {element, queue} +  end + +  defp add_ref(state, queue_name, ref) do +    refs = Map.put(state[:refs], ref, queue_name) +    Map.put(state, :refs, refs) +  end + +  defp remove_ref(state, ref) do +    refs = Map.delete(state[:refs], ref) +    Map.put(state, :refs, refs) +  end + +  defp update_queue(state, queue_name, data) do +    queues = Map.put(state[:queues], queue_name, data) +    Map.put(state, :queues, queues) +  end +end diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 2a266c407..057553e24 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -33,7 +33,22 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do    end    defp csp_string do -    protocol = Config.get([Pleroma.Web.Endpoint, :protocol]) +    scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] +    websocket_url = String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws") + +    connect_src = +      if Mix.env() == :dev do +        "connect-src 'self' http://localhost:3035/ " <> websocket_url +      else +        "connect-src 'self' " <> websocket_url +      end + +    script_src = +      if Mix.env() == :dev do +        "script-src 'self' 'unsafe-eval'" +      else +        "script-src 'self'" +      end      [        "default-src 'none'", @@ -43,10 +58,10 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do        "media-src 'self' https:",        "style-src 'self' 'unsafe-inline'",        "font-src 'self'", -      "script-src 'self'", -      "connect-src 'self' " <> String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"),        "manifest-src 'self'", -      if protocol == "https" do +      connect_src, +      script_src, +      if scheme == "https" do          "upgrade-insecure-requests"        end      ] diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex index 320b07abd..190ed9f3a 100644 --- a/lib/pleroma/uploaders/mdii.ex +++ b/lib/pleroma/uploaders/mdii.ex @@ -25,7 +25,7 @@ defmodule Pleroma.Uploaders.MDII do      query = "#{cgi}?#{extension}"      with {:ok, %{status: 200, body: body}} <- -           @httpoison.post(query, file_data, adapter: [pool: :default]) do +           @httpoison.post(query, file_data, [], adapter: [pool: :default]) do        remote_file_name = String.split(body) |> List.first()        public_url = "#{files}/#{remote_file_name}.#{extension}"        {:ok, {:url, public_url}} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0060d966b..18bb56667 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -237,6 +237,7 @@ defmodule Pleroma.User do        changeset        |> put_change(:password_hash, hashed)        |> put_change(:ap_id, ap_id) +      |> unique_constraint(:ap_id)        |> put_change(:following, [followers])        |> put_change(:follower_address, followers)      else @@ -261,6 +262,7 @@ defmodule Pleroma.User do    def register(%Ecto.Changeset{} = changeset) do      with {:ok, user} <- Repo.insert(changeset),           {:ok, user} <- autofollow_users(user), +         {:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),           {:ok, _} <- try_send_confirmation_email(user) do        {:ok, user}      end @@ -311,12 +313,12 @@ defmodule Pleroma.User do      end    end -  @doc "A mass follow for local users. Respects blocks but does not create activities." +  @doc "A mass follow for local users. Respects blocks in both directions but does not create activities."    @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}    def follow_all(follower, followeds) do      followed_addresses =        followeds -      |> Enum.reject(fn %{ap_id: ap_id} -> ap_id in follower.info.blocks end) +      |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)        |> Enum.map(fn %{follower_address: fa} -> fa end)      q = @@ -618,6 +620,32 @@ defmodule Pleroma.User do      )    end +  def update_follow_request_count(%User{} = user) do +    subquery = +      user +      |> User.get_follow_requests_query() +      |> select([a], %{count: count(a.id)}) + +    User +    |> where(id: ^user.id) +    |> join(:inner, [u], s in subquery(subquery)) +    |> update([u, s], +      set: [ +        info: +          fragment( +            "jsonb_set(?, '{follow_request_count}', ?::varchar::jsonb, true)", +            u.info, +            s.count +          ) +      ] +    ) +    |> Repo.update_all([], returning: true) +    |> case do +      {1, [user]} -> {:ok, user} +      _ -> {:error, user} +    end +  end +    def get_follow_requests(%User{} = user) do      q = get_follow_requests_query(user)      reqs = Repo.all(q) @@ -731,7 +759,7 @@ defmodule Pleroma.User do      # Strip the beginning @ off if there is a query      query = String.trim_leading(query, "@") -    if resolve, do: User.get_or_fetch_by_nickname(query) +    if resolve, do: get_or_fetch(query)      fts_results = do_search(fts_search_subquery(query), for_user) @@ -1173,7 +1201,7 @@ defmodule Pleroma.User do      {:ok, updated_user} =        user        |> change(%{tags: new_tags}) -      |> Repo.update() +      |> update_and_set_cache()      updated_user    end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 9d8779fab..9099d7fbb 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -12,6 +12,7 @@ defmodule Pleroma.User.Info do      field(:source_data, :map, default: %{})      field(:note_count, :integer, default: 0)      field(:follower_count, :integer, default: 0) +    field(:follow_request_count, :integer, default: 0)      field(:locked, :boolean, default: false)      field(:confirmation_pending, :boolean, default: false)      field(:confirmation_token, :string, default: nil) @@ -34,6 +35,7 @@ defmodule Pleroma.User.Info do      field(:hide_followers, :boolean, default: false)      field(:hide_follows, :boolean, default: false)      field(:pinned_activities, {:array, :string}, default: []) +    field(:flavour, :string, default: nil)      # Found in the wild      # ap_id -> Where is this used? @@ -186,6 +188,14 @@ defmodule Pleroma.User.Info do      |> validate_required([:settings])    end +  def mastodon_flavour_update(info, flavour) do +    params = %{flavour: flavour} + +    info +    |> cast(params, [:flavour]) +    |> validate_required([:flavour]) +  end +    def set_source_data(info, source_data) do      params = %{source_data: source_data} diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex new file mode 100644 index 000000000..8018ac22f --- /dev/null +++ b/lib/pleroma/user/welcome_message.ex @@ -0,0 +1,30 @@ +defmodule Pleroma.User.WelcomeMessage do +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI + +  def post_welcome_message_to_user(user) do +    with %User{} = sender_user <- welcome_user(), +         message when is_binary(message) <- welcome_message() do +      CommonAPI.post(sender_user, %{ +        "visibility" => "direct", +        "status" => "@#{user.nickname}\n#{message}" +      }) +    else +      _ -> {:ok, nil} +    end +  end + +  defp welcome_user() do +    with nickname when is_binary(nickname) <- +           Pleroma.Config.get([:instance, :welcome_user_nickname]), +         %User{local: true} = user <- User.get_cached_by_nickname(nickname) do +      user +    else +      _ -> nil +    end +  end + +  defp welcome_message() do +    Pleroma.Config.get([:instance, :welcome_message]) +  end +end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index c46d8233e..8d3116839 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -172,9 +172,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      # only accept false as false value      local = !(params[:local] == false) -    with data <- %{"to" => to, "type" => "Accept", "actor" => actor, "object" => object}, +    with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},           {:ok, activity} <- insert(data, local), -         :ok <- maybe_federate(activity) do +         :ok <- maybe_federate(activity), +         _ <- User.update_follow_request_count(actor) do        {:ok, activity}      end    end @@ -183,9 +184,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      # only accept false as false value      local = !(params[:local] == false) -    with data <- %{"to" => to, "type" => "Reject", "actor" => actor, "object" => object}, +    with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},           {:ok, activity} <- insert(data, local), -         :ok <- maybe_federate(activity) do +         :ok <- maybe_federate(activity), +         _ <- User.update_follow_request_count(actor) do        {:ok, activity}      end    end @@ -283,7 +285,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    def follow(follower, followed, activity_id \\ nil, local \\ true) do      with data <- make_follow_data(follower, followed, activity_id),           {:ok, activity} <- insert(data, local), -         :ok <- maybe_federate(activity) do +         :ok <- maybe_federate(activity), +         _ <- User.update_follow_request_count(followed) do        {:ok, activity}      end    end @@ -293,7 +296,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do           {: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 +         :ok <- maybe_federate(activity), +         _ <- User.update_follow_request_count(followed) do        {:ok, activity}      end    end @@ -753,21 +757,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      public = is_public?(activity) -    reachable_inboxes_metadata = -      (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) -      |> Enum.filter(fn user -> User.ap_enabled?(user) end) -      |> Enum.map(fn %{info: %{source_data: data}} -> -        (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] -      end) -      |> Enum.uniq() -      |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) -      |> Instances.filter_reachable() -      {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)      json = Jason.encode!(data) -    Enum.each(reachable_inboxes_metadata, fn {inbox, unreachable_since} -> -      Federator.enqueue(:publish_single_ap, %{ +    (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) +    |> Enum.filter(fn user -> User.ap_enabled?(user) end) +    |> Enum.map(fn %{info: %{source_data: data}} -> +      (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] +    end) +    |> Enum.uniq() +    |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) +    |> Instances.filter_reachable() +    |> Enum.each(fn {inbox, unreachable_since} -> +      Federator.publish_single_ap(%{          inbox: inbox,          json: json,          actor: actor, @@ -818,8 +820,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      if object = Object.get_cached_by_ap_id(id) do        {:ok, object}      else -      Logger.info("Fetching #{id} via AP") -        with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),             nil <- Object.normalize(data),             params <- %{ @@ -851,7 +851,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    def fetch_and_contain_remote_object_from_id(id) do -    Logger.info("Fetching #{id} via AP") +    Logger.info("Fetching object #{id} via AP")      with true <- String.starts_with?(id, "http"),           {:ok, %{body: body, status: code}} when code in 200..299 <- @@ -878,7 +878,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    def is_private?(activity) do -    !is_public?(activity) && Enum.any?(activity.data["to"], &String.contains?(&1, "/followers")) +    unless is_public?(activity) do +      follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address +      Enum.any?(activity.data["to"], &(&1 == follower_address)) +    else +      false +    end    end    def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 69879476e..2bea51311 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -155,13 +155,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      with %User{} = user <- User.get_cached_by_nickname(nickname),           true <- Utils.recipient_in_message(user.ap_id, params),           params <- Utils.maybe_splice_recipient(user.ap_id, params) do -      Federator.enqueue(:incoming_ap_doc, params) +      Federator.incoming_ap_doc(params)        json(conn, "ok")      end    end    def inbox(%{assigns: %{valid_signature: true}} = conn, params) do -    Federator.enqueue(:incoming_ap_doc, params) +    Federator.incoming_ap_doc(params)      json(conn, "ok")    end diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index 4c6e612b2..6736f3cb9 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -6,40 +6,80 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do    alias Pleroma.User    @behaviour Pleroma.Web.ActivityPub.MRF -  defp delist_message(message) do +  defp delist_message(message, threshold) when threshold > 0 do      follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address -    message -    |> Map.put("to", [follower_collection]) -    |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"]) +    follower_collection? = Enum.member?(message["to"] ++ message["cc"], follower_collection) + +    message = +      case get_recipient_count(message) do +        {:public, recipients} +        when follower_collection? and recipients > threshold -> +          message +          |> Map.put("to", [follower_collection]) +          |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"]) + +        {:public, recipients} when recipients > threshold -> +          message +          |> Map.put("to", []) +          |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"]) + +        _ -> +          message +      end + +    {:ok, message} +  end + +  defp delist_message(message, _threshold), do: {:ok, message} + +  defp reject_message(message, threshold) when threshold > 0 do +    with {_, recipients} <- get_recipient_count(message) do +      if recipients > threshold do +        {:reject, nil} +      else +        {:ok, message} +      end +    end +  end + +  defp reject_message(message, _threshold), do: {:ok, message} + +  defp get_recipient_count(message) do +    recipients = (message["to"] || []) ++ (message["cc"] || []) +    follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address + +    if Enum.member?(recipients, "https://www.w3.org/ns/activitystreams#Public") do +      recipients = +        recipients +        |> List.delete("https://www.w3.org/ns/activitystreams#Public") +        |> List.delete(follower_collection) + +      {:public, length(recipients)} +    else +      recipients = +        recipients +        |> List.delete(follower_collection) + +      {:not_public, length(recipients)} +    end    end    @impl true    def filter(%{"type" => "Create"} = message) do -    delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold]) -      reject_threshold =        Pleroma.Config.get(          [:mrf_hellthread, :reject_threshold],          Pleroma.Config.get([:mrf_hellthread, :threshold])        ) -    recipients = (message["to"] || []) ++ (message["cc"] || []) - -    cond do -      length(recipients) > reject_threshold and reject_threshold > 0 -> -        {:reject, nil} - -      length(recipients) > delist_threshold and delist_threshold > 0 -> -        if Enum.member?(message["to"], "https://www.w3.org/ns/activitystreams#Public") or -             Enum.member?(message["cc"], "https://www.w3.org/ns/activitystreams#Public") do -          {:ok, delist_message(message)} -        else -          {:ok, message} -        end +    delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold]) -      true -> -        {:ok, message} +    with {:ok, message} <- reject_message(message, reject_threshold), +         {:ok, message} <- delist_message(message, delist_threshold) do +      {:ok, message} +    else +      _e -> {:reject, nil}      end    end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 98a2af819..41d89a02b 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -406,7 +406,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        if not User.locked?(followed) do          ActivityPub.accept(%{            to: [follower.ap_id], -          actor: followed.ap_id, +          actor: followed,            object: data,            local: true          }) @@ -432,7 +432,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do             ActivityPub.accept(%{               to: follow_activity.data["to"],               type: "Accept", -             actor: followed.ap_id, +             actor: followed,               object: follow_activity.data["id"],               local: false             }) do @@ -458,7 +458,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do             ActivityPub.reject(%{               to: follow_activity.data["to"],               type: "Reject", -             actor: followed.ap_id, +             actor: followed,               object: follow_activity.data["id"],               local: false             }) do @@ -649,7 +649,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      if object = Object.normalize(id), do: {:ok, object}, else: nil    end -  def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) do +  def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) when is_binary(inReplyTo) do      with false <- String.starts_with?(inReplyTo, "http"),           {:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do        Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo) @@ -765,12 +765,18 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    def add_hashtags(object) do      tags =        (object["tag"] || []) -      |> Enum.map(fn tag -> -        %{ -          "href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}", -          "name" => "##{tag}", -          "type" => "Hashtag" -        } +      |> Enum.map(fn +        # Expand internal representation tags into AS2 tags. +        tag when is_binary(tag) -> +          %{ +            "href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}", +            "name" => "##{tag}", +            "type" => "Hashtag" +          } + +        # Do not process tags which are already AS2 tag objects. +        tag when is_map(tag) -> +          tag        end)      object diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 964e11c9d..6a89374d0 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -164,7 +164,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do          _ -> 5        end -    Pleroma.Web.Federator.enqueue(:publish, activity, priority) +    Pleroma.Web.Federator.publish(activity, priority)      :ok    end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 15e6c1f68..c8e154989 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -12,9 +12,26 @@ defmodule Pleroma.Web.ActivityPub.UserView do    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Transmogrifier    alias Pleroma.Web.ActivityPub.Utils +  alias Pleroma.Web.Router.Helpers +  alias Pleroma.Web.Endpoint    import Ecto.Query +  def render("endpoints.json", %{user: %User{nickname: nil, local: true} = _user}) do +    %{"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)} +  end + +  def render("endpoints.json", %{user: %User{local: true} = _user}) do +    %{ +      "oauthAuthorizationEndpoint" => Helpers.o_auth_url(Endpoint, :authorize), +      "oauthRegistrationEndpoint" => Helpers.mastodon_api_url(Endpoint, :create_app), +      "oauthTokenEndpoint" => Helpers.o_auth_url(Endpoint, :token_exchange), +      "sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox) +    } +  end + +  def render("endpoints.json", _), do: %{} +    # the instance itself is not a Person, but instead an Application    def render("user.json", %{user: %{nickname: nil} = user}) do      {:ok, user} = WebFinger.ensure_keys_present(user) @@ -22,6 +39,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do      public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)      public_key = :public_key.pem_encode([public_key]) +    endpoints = render("endpoints.json", %{user: user}) +      %{        "id" => user.ap_id,        "type" => "Application", @@ -37,9 +56,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do          "owner" => user.ap_id,          "publicKeyPem" => public_key        }, -      "endpoints" => %{ -        "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox" -      } +      "endpoints" => endpoints      }      |> Map.merge(Utils.make_json_ld_header())    end @@ -50,6 +67,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do      public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)      public_key = :public_key.pem_encode([public_key]) +    endpoints = render("endpoints.json", %{user: user}) +      %{        "id" => user.ap_id,        "type" => "Person", @@ -67,9 +86,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do          "owner" => user.ap_id,          "publicKeyPem" => public_key        }, -      "endpoints" => %{ -        "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox" -      }, +      "endpoints" => endpoints,        "icon" => %{          "type" => "Image",          "url" => User.avatar_url(user) @@ -88,7 +105,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do      query = from(user in query, select: [:ap_id])      following = Repo.all(query) -    collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows) +    total = +      if !user.info.hide_follows do +        length(following) +      else +        0 +      end + +    collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows, total)      |> Map.merge(Utils.make_json_ld_header())    end @@ -97,10 +121,17 @@ defmodule Pleroma.Web.ActivityPub.UserView do      query = from(user in query, select: [:ap_id])      following = Repo.all(query) +    total = +      if !user.info.hide_follows do +        length(following) +      else +        0 +      end +      %{        "id" => "#{user.ap_id}/following",        "type" => "OrderedCollection", -      "totalItems" => length(following), +      "totalItems" => total,        "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)      }      |> Map.merge(Utils.make_json_ld_header()) @@ -111,7 +142,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do      query = from(user in query, select: [:ap_id])      followers = Repo.all(query) -    collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers) +    total = +      if !user.info.hide_followers do +        length(followers) +      else +        0 +      end + +    collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers, total)      |> Map.merge(Utils.make_json_ld_header())    end @@ -120,19 +158,24 @@ defmodule Pleroma.Web.ActivityPub.UserView do      query = from(user in query, select: [:ap_id])      followers = Repo.all(query) +    total = +      if !user.info.hide_followers do +        length(followers) +      else +        0 +      end +      %{        "id" => "#{user.ap_id}/followers",        "type" => "OrderedCollection", -      "totalItems" => length(followers), -      "first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers) +      "totalItems" => total, +      "first" => +        collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers, total)      }      |> Map.merge(Utils.make_json_ld_header())    end    def render("outbox.json", %{user: user, max_id: max_qid}) do -    # XXX: technically note_count is wrong for this, but it's better than nothing -    info = User.user_info(user) -      params = %{        "limit" => "10"      } @@ -160,7 +203,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do        "id" => "#{iri}?max_id=#{max_id}",        "type" => "OrderedCollectionPage",        "partOf" => iri, -      "totalItems" => info.note_count,        "orderedItems" => collection,        "next" => "#{iri}?max_id=#{min_id}"      } @@ -169,7 +211,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do        %{          "id" => iri,          "type" => "OrderedCollection", -        "totalItems" => info.note_count,          "first" => page        }        |> Map.merge(Utils.make_json_ld_header()) @@ -207,7 +248,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do        "id" => "#{iri}?max_id=#{max_id}",        "type" => "OrderedCollectionPage",        "partOf" => iri, -      "totalItems" => -1,        "orderedItems" => collection,        "next" => "#{iri}?max_id=#{min_id}"      } @@ -216,7 +256,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do        %{          "id" => iri,          "type" => "OrderedCollection", -        "totalItems" => -1,          "first" => page        }        |> Map.merge(Utils.make_json_ld_header()) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 86f249c54..90b208e54 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -95,7 +95,7 @@ defmodule Pleroma.Web.CommonAPI do      limit = Pleroma.Config.get([:instance, :limit])      with status <- String.trim(status), -         attachments <- attachments_from_ids(data["media_ids"]), +         attachments <- attachments_from_ids(data),           mentions <- Formatter.parse_mentions(status),           inReplyTo <- get_replied_to_activity(data["in_reply_to_status_id"]),           {to, cc} <- to_for_user_and_mentions(user, mentions, inReplyTo, visibility), diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 123107b56..abdeee947 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -35,12 +35,28 @@ defmodule Pleroma.Web.CommonAPI.Utils do    def get_replied_to_activity(_), do: nil -  def attachments_from_ids(ids) do +  def attachments_from_ids(data) do +    if Map.has_key?(data, "descriptions") do +      attachments_from_ids_descs(data["media_ids"], data["descriptions"]) +    else +      attachments_from_ids_no_descs(data["media_ids"]) +    end +  end + +  def attachments_from_ids_no_descs(ids) do      Enum.map(ids || [], fn media_id ->        Repo.get(Object, media_id).data      end)    end +  def attachments_from_ids_descs(ids, descs_str) do +    {_, descs} = Jason.decode(descs_str) + +    Enum.map(ids || [], fn media_id -> +      Map.put(Repo.get(Object, media_id).data, "name", descs[media_id]) +    end) +  end +    def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do      mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 468959a65..d4e2a9742 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -3,8 +3,6 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Web.Federator do -  use GenServer -    alias Pleroma.Activity    alias Pleroma.User    alias Pleroma.Web.WebFinger @@ -16,45 +14,71 @@ defmodule Pleroma.Web.Federator do    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.Federator.RetryQueue    alias Pleroma.Web.OStatus +  alias Pleroma.Jobs    require Logger    @websub Application.get_env(:pleroma, :websub)    @ostatus Application.get_env(:pleroma, :ostatus) -  def init(args) do -    {:ok, args} +  def init() do +    # 1 minute +    Process.sleep(1000 * 60 * 1) +    refresh_subscriptions()    end -  def start_link do -    spawn(fn -> -      # 1 minute -      Process.sleep(1000 * 60) -      enqueue(:refresh_subscriptions, nil) -    end) +  # Client API + +  def incoming_doc(doc) do +    Jobs.enqueue(:federator_incoming, __MODULE__, [:incoming_doc, doc]) +  end + +  def incoming_ap_doc(params) do +    Jobs.enqueue(:federator_incoming, __MODULE__, [:incoming_ap_doc, params]) +  end + +  def publish(activity, priority \\ 1) do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:publish, activity], priority) +  end + +  def publish_single_ap(params) do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:publish_single_ap, params]) +  end -    GenServer.start_link( -      __MODULE__, -      %{ -        in: {:sets.new(), []}, -        out: {:sets.new(), []} -      }, -      name: __MODULE__ -    ) +  def publish_single_websub(websub) do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:publish_single_websub, websub])    end -  def handle(:refresh_subscriptions, _) do +  def verify_websub(websub) do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:verify_websub, websub]) +  end + +  def request_subscription(sub) do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:request_subscription, sub]) +  end + +  def refresh_subscriptions() do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:refresh_subscriptions]) +  end + +  def publish_single_salmon(params) do +    Jobs.enqueue(:federator_outgoing, __MODULE__, [:publish_single_salmon, params]) +  end + +  # Job Worker Callbacks + +  def perform(:refresh_subscriptions) do      Logger.debug("Federator running refresh subscriptions")      Websub.refresh_subscriptions()      spawn(fn ->        # 6 hours        Process.sleep(1000 * 60 * 60 * 6) -      enqueue(:refresh_subscriptions, nil) +      refresh_subscriptions()      end)    end -  def handle(:request_subscription, websub) do +  def perform(:request_subscription, websub) do      Logger.debug("Refreshing #{websub.topic}")      with {:ok, websub} <- Websub.request_subscription(websub) do @@ -64,7 +88,7 @@ defmodule Pleroma.Web.Federator do      end    end -  def handle(:publish, activity) do +  def perform(:publish, activity) do      Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)      with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do @@ -90,7 +114,7 @@ defmodule Pleroma.Web.Federator do      end    end -  def handle(:verify_websub, websub) do +  def perform(:verify_websub, websub) do      Logger.debug(fn ->        "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})"      end) @@ -98,12 +122,12 @@ defmodule Pleroma.Web.Federator do      @websub.verify(websub)    end -  def handle(:incoming_doc, doc) do +  def perform(:incoming_doc, doc) do      Logger.info("Got document, trying to parse")      @ostatus.handle_incoming(doc)    end -  def handle(:incoming_ap_doc, params) do +  def perform(:incoming_ap_doc, params) do      Logger.info("Handling incoming AP activity")      params = Utils.normalize_params(params) @@ -128,11 +152,11 @@ defmodule Pleroma.Web.Federator do      end    end -  def handle(:publish_single_salmon, params) do +  def perform(:publish_single_salmon, params) do      Salmon.send_to_user(params)    end -  def handle(:publish_single_ap, params) do +  def perform(:publish_single_ap, params) do      case ActivityPub.publish_one(params) do        {:ok, _} ->          :ok @@ -142,7 +166,7 @@ defmodule Pleroma.Web.Federator do      end    end -  def handle( +  def perform(          :publish_single_websub,          %{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params        ) do @@ -155,74 +179,11 @@ defmodule Pleroma.Web.Federator do      end    end -  def handle(type, _) do +  def perform(type, _) do      Logger.debug(fn -> "Unknown task: #{type}" end)      {:error, "Don't know what to do with this"}    end -  if Mix.env() == :test do -    def enqueue(type, payload, _priority \\ 1) do -      if Pleroma.Config.get([:instance, :federating]) do -        handle(type, payload) -      end -    end -  else -    def enqueue(type, payload, priority \\ 1) do -      if Pleroma.Config.get([:instance, :federating]) do -        GenServer.cast(__MODULE__, {:enqueue, type, payload, priority}) -      end -    end -  end - -  def maybe_start_job(running_jobs, queue) do -    if :sets.size(running_jobs) < Pleroma.Config.get([__MODULE__, :max_jobs]) && queue != [] do -      {{type, payload}, queue} = queue_pop(queue) -      {:ok, pid} = Task.start(fn -> handle(type, payload) end) -      mref = Process.monitor(pid) -      {:sets.add_element(mref, running_jobs), queue} -    else -      {running_jobs, queue} -    end -  end - -  def handle_cast({:enqueue, type, payload, _priority}, state) -      when type in [:incoming_doc, :incoming_ap_doc] do -    %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state -    i_queue = enqueue_sorted(i_queue, {type, payload}, 1) -    {i_running_jobs, i_queue} = maybe_start_job(i_running_jobs, i_queue) -    {:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}} -  end - -  def handle_cast({:enqueue, type, payload, _priority}, state) do -    %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state -    o_queue = enqueue_sorted(o_queue, {type, payload}, 1) -    {o_running_jobs, o_queue} = maybe_start_job(o_running_jobs, o_queue) -    {:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}} -  end - -  def handle_cast(_, state) do -    {:noreply, state} -  end - -  def handle_info({:DOWN, ref, :process, _pid, _reason}, state) do -    %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state -    i_running_jobs = :sets.del_element(ref, i_running_jobs) -    o_running_jobs = :sets.del_element(ref, o_running_jobs) -    {i_running_jobs, i_queue} = maybe_start_job(i_running_jobs, i_queue) -    {o_running_jobs, o_queue} = maybe_start_job(o_running_jobs, o_queue) - -    {:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}} -  end - -  def enqueue_sorted(queue, element, priority) do -    [%{item: element, priority: priority} | queue] -    |> Enum.sort_by(fn %{priority: priority} -> priority end) -  end - -  def queue_pop([%{item: element} | queue]) do -    {element, queue} -  end -    def ap_enabled_actor(id) do      user = User.get_by_ap_id(id) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index dcaeccac6..e2715bd08 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -680,7 +680,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do           {:ok, _activity} <-             ActivityPub.accept(%{               to: [follower.ap_id], -             actor: followed.ap_id, +             actor: followed,               object: follow_activity.data["id"],               type: "Accept"             }) do @@ -702,7 +702,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do           {:ok, _activity} <-             ActivityPub.reject(%{               to: [follower.ap_id], -             actor: followed.ap_id, +             actor: followed,               object: follow_activity.data["id"],               type: "Reject"             }) do @@ -1051,6 +1051,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do        accounts =          Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user})) +      flavour = get_user_flavour(user) +        initial_state =          %{            meta: %{ @@ -1135,7 +1137,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do        conn        |> put_layout(false)        |> put_view(MastodonView) -      |> render("index.html", %{initial_state: initial_state}) +      |> render("index.html", %{initial_state: initial_state, flavour: flavour})      else        conn        |> redirect(to: "/web/login") @@ -1157,6 +1159,43 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end +  @supported_flavours ["glitch", "vanilla"] + +  def set_flavour(%{assigns: %{user: user}} = conn, %{"flavour" => flavour} = _params) +      when flavour in @supported_flavours do +    flavour_cng = User.Info.mastodon_flavour_update(user.info, flavour) + +    with changeset <- Ecto.Changeset.change(user), +         changeset <- Ecto.Changeset.put_embed(changeset, :info, flavour_cng), +         {:ok, user} <- User.update_and_set_cache(changeset), +         flavour <- user.info.flavour do +      json(conn, flavour) +    else +      e -> +        conn +        |> put_resp_content_type("application/json") +        |> send_resp(500, Jason.encode!(%{"error" => inspect(e)})) +    end +  end + +  def set_flavour(conn, _params) do +    conn +    |> put_status(400) +    |> json(%{error: "Unsupported flavour"}) +  end + +  def get_flavour(%{assigns: %{user: user}} = conn, _params) do +    json(conn, get_user_flavour(user)) +  end + +  defp get_user_flavour(%User{info: %{flavour: flavour}}) when flavour in @supported_flavours do +    flavour +  end + +  defp get_user_flavour(_) do +    "glitch" +  end +    def login(conn, %{"code" => code}) do      with {:ok, app} <- get_or_make_app(),           %Authorization{} = auth <- Repo.get_by(Authorization, token: code, app_id: app.id), diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 69f5f992c..a49b381c9 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -166,7 +166,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        sensitive: sensitive,        spoiler_text: object["summary"] || "",        visibility: get_visibility(object), -      media_attachments: attachments |> Enum.take(4), +      media_attachments: attachments,        mentions: mentions,        tags: build_tags(tags),        application: %{ diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex index 1e9da7283..39a725a69 100644 --- a/lib/pleroma/web/media_proxy/media_proxy.ex +++ b/lib/pleroma/web/media_proxy/media_proxy.ex @@ -19,11 +19,16 @@ defmodule Pleroma.Web.MediaProxy do      else        secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] +      # Must preserve `%2F` for compatibility with S3 (https://git.pleroma.social/pleroma/pleroma/issues/580) +      replacement = get_replacement(url, ":2F:") +        # The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.        base64 =          url +        |> String.replace("%2F", replacement)          |> URI.decode()          |> URI.encode() +        |> String.replace(replacement, "%2F")          |> Base.url_encode64(@base64_opts)        sig = :crypto.hmac(:sha, secret, base64) @@ -60,4 +65,12 @@ defmodule Pleroma.Web.MediaProxy do      |> Enum.filter(fn value -> value end)      |> Path.join()    end + +  defp get_replacement(url, replacement) do +    if String.contains?(url, replacement) do +      get_replacement(url, replacement <> replacement) +    else +      replacement +    end +  end  end diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index 3e8acde31..8b61bf3a4 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -25,8 +25,14 @@ defmodule Pleroma.Web.OAuth.App do      if changeset.valid? do        changeset -      |> put_change(:client_id, :crypto.strong_rand_bytes(32) |> Base.url_encode64()) -      |> put_change(:client_secret, :crypto.strong_rand_bytes(32) |> Base.url_encode64()) +      |> put_change( +        :client_id, +        :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) +      ) +      |> put_change( +        :client_secret, +        :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) +      )      else        changeset      end diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex index 75c9ab9aa..9039b8b45 100644 --- a/lib/pleroma/web/oauth/authorization.ex +++ b/lib/pleroma/web/oauth/authorization.ex @@ -24,7 +24,7 @@ defmodule Pleroma.Web.OAuth.Authorization do    end    def create_authorization(%App{} = app, %User{} = user) do -    token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() +    token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)      authorization = %Authorization{        token: token, diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index e4d0601f8..dddfcf299 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -173,7 +173,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do      token      |> URI.decode()      |> Base.url_decode64!(padding: false) -    |> Base.url_encode64() +    |> Base.url_encode64(padding: false)    end    defp get_app_from_request(conn, params) do diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex index b0bbeeb69..71fd1b874 100644 --- a/lib/pleroma/web/oauth/token.ex +++ b/lib/pleroma/web/oauth/token.ex @@ -31,8 +31,8 @@ defmodule Pleroma.Web.OAuth.Token do    end    def create_token(%App{} = app, %User{} = user) do -    token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() -    refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() +    token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) +    refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)      token = %Token{        token: token, @@ -47,9 +47,27 @@ defmodule Pleroma.Web.OAuth.Token do    def delete_user_tokens(%User{id: user_id}) do      from( -      t in Pleroma.Web.OAuth.Token, +      t in Token,        where: t.user_id == ^user_id      )      |> Repo.delete_all()    end + +  def delete_user_token(%User{id: user_id}, token_id) do +    from( +      t in Token, +      where: t.user_id == ^user_id, +      where: t.id == ^token_id +    ) +    |> Repo.delete_all() +  end + +  def get_user_tokens(%User{id: user_id}) do +    from( +      t in Token, +      where: t.user_id == ^user_id +    ) +    |> Repo.all() +    |> Repo.preload(:app) +  end  end diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index e7bde28a6..860b8210e 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -87,7 +87,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do      {:ok, body, _conn} = read_body(conn)      {:ok, doc} = decode_or_retry(body) -    Federator.enqueue(:incoming_doc, doc) +    Federator.incoming_doc(doc)      conn      |> send_resp(200, "") diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index d37ccd82c..196b87b66 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -236,6 +236,9 @@ defmodule Pleroma.Web.Router do      get("/suggestions", MastodonAPIController, :suggestions)      get("/endorsements", MastodonAPIController, :empty_array) + +    post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour) +    get("/pleroma/flavour", MastodonAPIController, :get_flavour)    end    scope "/api/web", Pleroma.Web.MastodonAPI do @@ -389,6 +392,9 @@ defmodule Pleroma.Web.Router do      get("/qvitter/mutes", TwitterAPI.Controller, :raw_empty_array)      get("/externalprofile/show", TwitterAPI.Controller, :external_profile) + +    get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) +    delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)    end    pipeline :ap_relay do @@ -469,8 +475,8 @@ defmodule Pleroma.Web.Router do    scope "/", Pleroma.Web.ActivityPub do      pipe_through(:activitypub) -    post("/users/:nickname/inbox", ActivityPubController, :inbox)      post("/inbox", ActivityPubController, :inbox) +    post("/users/:nickname/inbox", ActivityPubController, :inbox)    end    scope "/.well-known", Pleroma.Web do diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index a5a9e16c6..0a69aa1ec 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -229,7 +229,7 @@ defmodule Pleroma.Web.Salmon do        |> Enum.each(fn remote_user ->          Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) -        Pleroma.Web.Federator.enqueue(:publish_single_salmon, %{ +        Pleroma.Web.Federator.publish_single_salmon(%{            recipient: remote_user,            feed: feed,            poster: poster, diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex index 8dd3284d6..520e4b3d5 100644 --- a/lib/pleroma/web/templates/layout/app.html.eex +++ b/lib/pleroma/web/templates/layout/app.html.eex @@ -67,6 +67,32 @@          font-weight: 500;          font-size: 16px;        } + +      .alert-danger { +        box-sizing: border-box; +        width: 100%; +        color: #D8000C; +        background-color: #FFD2D2; +        border-radius: 4px; +        border: none; +        padding: 10px; +        margin-top: 20px; +        font-weight: 500; +        font-size: 16px; +      } + +      .alert-info { +        box-sizing: border-box; +        width: 100%; +        color: #00529B; +        background-color: #BDE5F8; +        border-radius: 4px; +        border: none; +        padding: 10px; +        margin-top: 20px; +        font-weight: 500; +        font-size: 16px; +      }      </style>    </head>    <body> diff --git a/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex b/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex index 9a725e420..5659c7828 100644 --- a/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex +++ b/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex @@ -8,7 +8,7 @@  </title>  <link rel="icon" type="image/png" href="/favicon.png"/>  <script crossorigin='anonymous' src="/packs/locales.js"></script> -<script crossorigin='anonymous' src="/packs/locales/glitch/en.js"></script> +<script crossorigin='anonymous' src="/packs/locales/<%= @flavour %>/en.js"></script>  <link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/getting_started.js'>  <link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/compose.js'> @@ -19,10 +19,10 @@  <script src="/packs/core/common.js"></script>  <link rel="stylesheet" media="all" href="/packs/core/common.css" /> -<script src="/packs/flavours/glitch/common.js"></script> -<link rel="stylesheet" media="all" href="/packs/flavours/glitch/common.css" /> +<script src="/packs/flavours/<%= @flavour %>/common.js"></script> +<link rel="stylesheet" media="all" href="/packs/flavours/<%= @flavour %>/common.css" /> -<script src="/packs/flavours/glitch/home.js"></script> +<script src="/packs/flavours/<%= @flavour %>/home.js"></script>  </head>  <body class='app-body no-reduce-motion system-font'>    <div class='app-holder' data-props='{"locale":"en"}' id='mastodon'> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index de2241ec9..32c458f0c 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -1,5 +1,9 @@ +<%= if get_flash(@conn, :info) do %>  <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> +<% end %> +<%= if get_flash(@conn, :error) do %>  <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> +<% end %>  <h2>OAuth Authorization</h2>  <%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>  <%= label f, :name, "Name or email" %> diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index c2f0dc2a9..b815379fd 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -8,6 +8,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    import Pleroma.Web.ControllerHelper, only: [json_response: 3]    alias Ecto.Changeset +  alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView, TokenView} +  alias Pleroma.Web.CommonAPI +  alias Pleroma.{Repo, Activity, Object, User, Notification} +  alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.CommonAPI @@ -524,6 +528,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    def friends(%{assigns: %{user: for_user}} = conn, params) do      {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) +    {:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false) + +    page = if export, do: nil, else: page      with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),           {:ok, friends} <- User.get_friends(user, page) do @@ -542,6 +549,20 @@ defmodule Pleroma.Web.TwitterAPI.Controller do      end    end +  def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do +    with oauth_tokens <- Token.get_user_tokens(user) do +      conn +      |> put_view(TokenView) +      |> render("index.json", %{tokens: oauth_tokens}) +    end +  end + +  def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do +    Token.delete_user_token(user, id) + +    json_reply(conn, 201, "") +  end +    def blocks(%{assigns: %{user: user}} = conn, _params) do      with blocked_users <- User.blocked_users(user) do        conn @@ -570,7 +591,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do           {:ok, _activity} <-             ActivityPub.accept(%{               to: [follower.ap_id], -             actor: followed.ap_id, +             actor: followed,               object: follow_activity.data["id"],               type: "Accept"             }) do @@ -590,7 +611,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do           {:ok, _activity} <-             ActivityPub.reject(%{               to: [follower.ap_id], -             actor: followed.ap_id, +             actor: followed,               object: follow_activity.data["id"],               type: "Reject"             }) do diff --git a/lib/pleroma/web/twitter_api/views/token_view.ex b/lib/pleroma/web/twitter_api/views/token_view.ex new file mode 100644 index 000000000..3ff314913 --- /dev/null +++ b/lib/pleroma/web/twitter_api/views/token_view.ex @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.TokenView do +  use Pleroma.Web, :view + +  def render("index.json", %{tokens: tokens}) do +    tokens +    |> render_many(Pleroma.Web.TwitterAPI.TokenView, "show.json") +    |> Enum.filter(&Enum.any?/1) +  end + +  def render("show.json", %{token: token_entry}) do +    %{ +      id: token_entry.id, +      valid_until: token_entry.valid_until, +      app_name: token_entry.app.client_name +    } +  end +end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index a09450df7..df7384476 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -113,10 +113,12 @@ defmodule Pleroma.Web.TwitterAPI.UserView do        "fields" => fields,        # Pleroma extension -      "pleroma" => %{ -        "confirmation_pending" => user_info.confirmation_pending, -        "tags" => user.tags -      } +      "pleroma" => +        %{ +          "confirmation_pending" => user_info.confirmation_pending, +          "tags" => user.tags +        } +        |> maybe_with_follow_request_count(user, for_user)      }      data = @@ -132,6 +134,14 @@ defmodule Pleroma.Web.TwitterAPI.UserView do      end    end +  defp maybe_with_follow_request_count(data, %User{id: id, info: %{locked: true}} = user, %User{ +         id: id +       }) do +    Map.put(data, "follow_request_count", user.info.follow_request_count) +  end + +  defp maybe_with_follow_request_count(data, _, _), do: data +    defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do      Map.merge(data, %{"role" => role(user), "show_role" => user.info.show_role})    end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index a08d7993d..c00ec0858 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.Websub do    alias Pleroma.Web.Endpoint    alias Pleroma.Web.OStatus    alias Pleroma.Web.Router.Helpers +  alias Pleroma.Web.Federator    require Logger    import Ecto.Query @@ -87,7 +88,7 @@ defmodule Pleroma.Web.Websub do          unreachable_since: reachable_callbacks_metadata[sub.callback]        } -      Pleroma.Web.Federator.enqueue(:publish_single_websub, data) +      Federator.publish_single_websub(data)      end)    end @@ -119,7 +120,7 @@ defmodule Pleroma.Web.Websub do        websub = Repo.update!(change) -      Pleroma.Web.Federator.enqueue(:verify_websub, websub) +      Federator.verify_websub(websub)        {:ok, websub}      else @@ -269,7 +270,7 @@ defmodule Pleroma.Web.Websub do      subs = Repo.all(query)      Enum.each(subs, fn sub -> -      Pleroma.Web.Federator.enqueue(:request_subscription, sub) +      Federator.request_subscription(sub)      end)    end diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex index 1ad18a8a4..ad40f1b94 100644 --- a/lib/pleroma/web/websub/websub_controller.ex +++ b/lib/pleroma/web/websub/websub_controller.ex @@ -84,7 +84,7 @@ defmodule Pleroma.Web.Websub.WebsubController do           %WebsubClientSubscription{} = websub <- Repo.get(WebsubClientSubscription, id),           {:ok, body, _conn} = read_body(conn),           ^signature <- Websub.sign(websub.secret, body) do -      Federator.enqueue(:incoming_doc, body) +      Federator.incoming_doc(body)        conn        |> send_resp(200, "OK") @@ -21,7 +21,14 @@ defmodule Pleroma.Mixfile do        homepage_url: "https://pleroma.social/",        docs: [          logo: "priv/static/static/logo.png", -        extras: ["README.md", "docs/config.md", "docs/Pleroma-API.md", "docs/Admin-API.md"], +        extras: [ +          "README.md", +          "docs/config.md", +          "docs/Pleroma-API.md", +          "docs/Admin-API.md", +          "docs/Clients.md", +          "docs/Differences-in-MastodonAPI-Responses.md" +        ],          main: "readme",          output: "priv/static/doc"        ] diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld index 15645646a..f36b231c5 100644 --- a/priv/static/schemas/litepub-0.1.jsonld +++ b/priv/static/schemas/litepub-0.1.jsonld @@ -19,7 +19,11 @@              "value": "schema:value",              "sensitive": "as:sensitive",              "litepub": "http://litepub.social/ns#", -            "directMessage": "litepub:directMessage" +            "directMessage": "litepub:directMessage", +            "oauthRegistrationEndpoint": { +                "@id": "litepub:oauthRegistrationEndpoint", +                "@type": "@id" +            }          }      ]  } diff --git a/test/formatter_test.exs b/test/formatter_test.exs index 2e717194b..f14077d25 100644 --- a/test/formatter_test.exs +++ b/test/formatter_test.exs @@ -197,7 +197,7 @@ defmodule Pleroma.FormatterTest do        {subs, text} = Formatter.add_user_links({[], text}, mentions) -      assert length(subs) == 0 +      assert Enum.empty?(subs)        Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)        expected_text = "@a hi" diff --git a/test/jobs_test.exs b/test/jobs_test.exs new file mode 100644 index 000000000..ccb518dec --- /dev/null +++ b/test/jobs_test.exs @@ -0,0 +1,83 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.JobsTest do +  use ExUnit.Case, async: true + +  alias Pleroma.Jobs +  alias Jobs.WorkerMock + +  setup do +    state = %{ +      queues: Enum.into([Jobs.create_queue(:testing)], %{}), +      refs: %{} +    } + +    [state: state] +  end + +  test "creates queue" do +    queue = Jobs.create_queue(:foobar) + +    assert {:foobar, set} = queue +    assert :set == elem(set, 0) |> elem(0) +  end + +  test "enqueues an element according to priority" do +    queue = [%{item: 1, priority: 2}] + +    new_queue = Jobs.enqueue_sorted(queue, 2, 1) +    assert new_queue == [%{item: 2, priority: 1}, %{item: 1, priority: 2}] + +    new_queue = Jobs.enqueue_sorted(queue, 2, 3) +    assert new_queue == [%{item: 1, priority: 2}, %{item: 2, priority: 3}] +  end + +  test "pop first item" do +    queue = [%{item: 2, priority: 1}, %{item: 1, priority: 2}] + +    assert {2, [%{item: 1, priority: 2}]} = Jobs.queue_pop(queue) +  end + +  test "enqueue a job", %{state: state} do +    assert {:noreply, new_state} = +             Jobs.handle_cast({:enqueue, :testing, WorkerMock, [:test_job, :foo, :bar], 3}, state) + +    assert %{queues: %{testing: {running_jobs, []}}, refs: _} = new_state +    assert :sets.size(running_jobs) == 1 +    assert [ref] = :sets.to_list(running_jobs) +    assert %{refs: %{^ref => :testing}} = new_state +  end + +  test "max jobs setting", %{state: state} do +    max_jobs = Pleroma.Config.get([Jobs, :testing, :max_jobs]) + +    {:noreply, state} = +      Enum.reduce(1..(max_jobs + 1), {:noreply, state}, fn _, {:noreply, state} -> +        Jobs.handle_cast({:enqueue, :testing, WorkerMock, [:test_job, :foo, :bar], 3}, state) +      end) + +    assert %{ +             queues: %{ +               testing: +                 {running_jobs, [%{item: {WorkerMock, [:test_job, :foo, :bar]}, priority: 3}]} +             } +           } = state + +    assert :sets.size(running_jobs) == max_jobs +  end + +  test "remove job after it finished", %{state: state} do +    {:noreply, new_state} = +      Jobs.handle_cast({:enqueue, :testing, WorkerMock, [:test_job, :foo, :bar], 3}, state) + +    %{queues: %{testing: {running_jobs, []}}} = new_state +    [ref] = :sets.to_list(running_jobs) + +    assert {:noreply, %{queues: %{testing: {running_jobs, []}}, refs: %{}}} = +             Jobs.handle_info({:DOWN, ref, :process, nil, nil}, new_state) + +    assert :sets.size(running_jobs) == 0 +  end +end diff --git a/test/media_proxy_test.exs b/test/media_proxy_test.exs index 05d927422..ddbadfbf5 100644 --- a/test/media_proxy_test.exs +++ b/test/media_proxy_test.exs @@ -140,6 +140,15 @@ defmodule Pleroma.MediaProxyTest do        assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url]))      end + +    # https://git.pleroma.social/pleroma/pleroma/issues/580 +    test "encoding S3 links (must preserve `%2F`)" do +      url = +        "https://s3.amazonaws.com/example/test.png?X-Amz-Credential=your-access-key-id%2F20130721%2Fus-east-1%2Fs3%2Faws4_request" + +      encoded = url(url) +      assert decode_result(encoded) == url +    end    end    describe "when disabled" do diff --git a/test/notification_test.exs b/test/notification_test.exs index 94fb0ab15..755874a3d 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -6,7 +6,8 @@ defmodule Pleroma.NotificationTest do    use Pleroma.DataCase    alias Pleroma.Web.TwitterAPI.TwitterAPI    alias Pleroma.Web.CommonAPI -  alias Pleroma.{User, Notification} +  alias Pleroma.User +  alias Pleroma.Notification    alias Pleroma.Web.ActivityPub.Transmogrifier    import Pleroma.Factory @@ -299,7 +300,7 @@ defmodule Pleroma.NotificationTest do        {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) @@ -307,7 +308,7 @@ defmodule Pleroma.NotificationTest do        {:ok, _} = CommonAPI.delete(activity.id, user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end      test "liking an activity results in 1 notification, then 0 if the activity is unliked" do @@ -316,7 +317,7 @@ defmodule Pleroma.NotificationTest do        {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) @@ -324,7 +325,7 @@ defmodule Pleroma.NotificationTest do        {:ok, _, _, _} = CommonAPI.unfavorite(activity.id, other_user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end      test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do @@ -333,7 +334,7 @@ defmodule Pleroma.NotificationTest do        {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) @@ -341,7 +342,7 @@ defmodule Pleroma.NotificationTest do        {:ok, _} = CommonAPI.delete(activity.id, user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end      test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do @@ -350,7 +351,7 @@ defmodule Pleroma.NotificationTest do        {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) @@ -358,7 +359,7 @@ defmodule Pleroma.NotificationTest do        {:ok, _, _} = CommonAPI.unrepeat(activity.id, other_user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end      test "liking an activity which is already deleted does not generate a notification" do @@ -367,15 +368,15 @@ defmodule Pleroma.NotificationTest do        {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:error, _} = CommonAPI.favorite(activity.id, other_user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end      test "repeating an activity which is already deleted does not generate a notification" do @@ -384,15 +385,15 @@ defmodule Pleroma.NotificationTest do        {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))        {:error, _} = CommonAPI.repeat(activity.id, other_user) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end      test "replying to a deleted post without tagging does not generate a notification" do @@ -408,7 +409,7 @@ defmodule Pleroma.NotificationTest do            "in_reply_to_status_id" => activity.id          }) -      assert length(Notification.for_user(user)) == 0 +      assert Enum.empty?(Notification.for_user(user))      end    end  end diff --git a/test/object_test.exs b/test/object_test.exs index 72194975d..a820a34ee 100644 --- a/test/object_test.exs +++ b/test/object_test.exs @@ -5,7 +5,8 @@  defmodule Pleroma.ObjectTest do    use Pleroma.DataCase    import Pleroma.Factory -  alias Pleroma.{Repo, Object} +  alias Pleroma.Repo +  alias Pleroma.Object    test "returns an object by it's AP id" do      object = insert(:note) diff --git a/test/support/builders/user_builder.ex b/test/support/builders/user_builder.ex index 7a1ca79b5..611a5be18 100644 --- a/test/support/builders/user_builder.ex +++ b/test/support/builders/user_builder.ex @@ -1,5 +1,6 @@  defmodule Pleroma.Builders.UserBuilder do -  alias Pleroma.{User, Repo} +  alias Pleroma.User +  alias Pleroma.Repo    def build(data \\ %{}) do      user = %User{ diff --git a/test/support/factory.ex b/test/support/factory.ex index 0c21093ce..7a91549f5 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -227,4 +227,17 @@ defmodule Pleroma.Factory do        unreachable_since: nil      }    end + +  def oauth_token_factory do +    user = insert(:user) +    oauth_app = insert(:oauth_app) + +    %Pleroma.Web.OAuth.Token{ +      token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), +      refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), +      user_id: user.id, +      app_id: oauth_app.id, +      valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10) +    } +  end  end diff --git a/test/support/jobs_worker_mock.ex b/test/support/jobs_worker_mock.ex new file mode 100644 index 000000000..0fb976d05 --- /dev/null +++ b/test/support/jobs_worker_mock.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Jobs.WorkerMock do +  require Logger + +  def perform(:test_job, arg, arg2) do +    Logger.debug({:perform, :test_job, arg, arg2}) +  end + +  def perform(:test_job, payload) do +    Logger.debug({:perform, :test_job, payload}) +  end + +  def test_job(payload) do +    Pleroma.Jobs.enqueue(:testing, __MODULE__, [:test_job, payload]) +  end +end diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index 96fac4811..64ff07753 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -4,7 +4,9 @@  defmodule Mix.Tasks.Pleroma.RelayTest do    alias Pleroma.Activity -  alias Pleroma.Web.ActivityPub.{ActivityPub, Relay, Utils} +  alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Utils +  alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.User    use Pleroma.DataCase diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index 44271898c..7b814d171 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -151,7 +151,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do        assert message =~ "Successfully unsubscribed"        user = User.get_by_nickname(user.nickname) -      assert length(user.following) == 0 +      assert Enum.empty?(user.following)        assert user.info.deactivated      end diff --git a/test/user_test.exs b/test/user_test.exs index 523ab1ea4..92991d063 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -4,7 +4,9 @@  defmodule Pleroma.UserTest do    alias Pleroma.Builders.UserBuilder -  alias Pleroma.{User, Repo, Activity} +  alias Pleroma.Activity +  alias Pleroma.Repo +  alias Pleroma.User    alias Pleroma.Web.CommonAPI    use Pleroma.DataCase @@ -55,18 +57,21 @@ defmodule Pleroma.UserTest do      followed_two = insert(:user)      blocked = insert(:user)      not_followed = insert(:user) +    reverse_blocked = insert(:user)      {:ok, user} = User.block(user, blocked) +    {:ok, reverse_blocked} = User.block(reverse_blocked, user)      {:ok, user} = User.follow(user, followed_zero) -    {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked]) +    {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked, reverse_blocked])      assert User.following?(user, followed_one)      assert User.following?(user, followed_two)      assert User.following?(user, followed_zero)      refute User.following?(user, not_followed)      refute User.following?(user, blocked) +    refute User.following?(user, reverse_blocked)    end    test "follow_all follows mutliple users without duplicating" do @@ -191,6 +196,26 @@ defmodule Pleroma.UserTest do        assert User.following?(registered_user, user)        refute User.following?(registered_user, remote_user) + +      Pleroma.Config.put([:instance, :autofollowed_nicknames], []) +    end + +    test "it sends a welcome message if it is set" do +      welcome_user = insert(:user) + +      Pleroma.Config.put([:instance, :welcome_user_nickname], welcome_user.nickname) +      Pleroma.Config.put([:instance, :welcome_message], "Hello, this is a cool site") + +      cng = User.register_changeset(%User{}, @full_user_data) +      {:ok, registered_user} = User.register(cng) + +      activity = Repo.one(Pleroma.Activity) +      assert registered_user.ap_id in activity.recipients +      assert activity.data["object"]["content"] =~ "cool site" +      assert activity.actor == welcome_user.ap_id + +      Pleroma.Config.put([:instance, :welcome_user_nickname], nil) +      Pleroma.Config.put([:instance, :welcome_message], nil)      end      test "it requires an email, name, nickname and password, bio is optional" do @@ -873,6 +898,16 @@ defmodule Pleroma.UserTest do          assert [] == User.search(query)        end)      end + +    test "works with URIs" do +      results = User.search("http://mastodon.example.org/users/admin", true) +      result = results |> List.first() + +      user = User.get_by_ap_id("http://mastodon.example.org/users/admin") + +      assert length(results) == 1 +      assert user == result |> Map.put(:search_rank, nil) +    end    end    test "auth_active?/1 works correctly" do diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 570bee6b3..398bedf77 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -5,8 +5,13 @@  defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do    use Pleroma.Web.ConnCase    import Pleroma.Factory -  alias Pleroma.Web.ActivityPub.{UserView, ObjectView} -  alias Pleroma.{Object, Repo, Activity, User, Instances} +  alias Pleroma.Web.ActivityPub.UserView +  alias Pleroma.Web.ActivityPub.ObjectView +  alias Pleroma.Object +  alias Pleroma.Repo +  alias Pleroma.Activity +  alias Pleroma.User +  alias Pleroma.Instances    setup_all do      Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -397,7 +402,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do          |> json_response(200)        assert result["first"]["orderedItems"] == [] -      assert result["totalItems"] == 1 +      assert result["totalItems"] == 0      end      test "it works for more than 10 users", %{conn: conn} do @@ -452,7 +457,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do          |> json_response(200)        assert result["first"]["orderedItems"] == [] -      assert result["totalItems"] == 1 +      assert result["totalItems"] == 0      end      test "it works for more than 10 users", %{conn: conn} do diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index a55961ac4..a6f8b822a 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -7,7 +7,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.CommonAPI -  alias Pleroma.{Activity, Object, User, Instances} +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.User +  alias Pleroma.Instances    alias Pleroma.Builders.ActivityBuilder    import Pleroma.Factory diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/web/activity_pub/mrf/hellthread_policy_test.exs new file mode 100644 index 000000000..eb6ee4d04 --- /dev/null +++ b/test/web/activity_pub/mrf/hellthread_policy_test.exs @@ -0,0 +1,73 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do +  use Pleroma.DataCase +  import Pleroma.Factory + +  import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy + +  setup do +    user = insert(:user) + +    message = %{ +      "actor" => user.ap_id, +      "cc" => [user.follower_address], +      "type" => "Create", +      "to" => [ +        "https://www.w3.org/ns/activitystreams#Public", +        "https://instance.tld/users/user1", +        "https://instance.tld/users/user2", +        "https://instance.tld/users/user3" +      ] +    } + +    [user: user, message: message] +  end + +  describe "reject" do +    test "rejects the message if the recipient count is above reject_threshold", %{ +      message: message +    } do +      Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) + +      {:reject, nil} = filter(message) +    end + +    test "does not reject the message if the recipient count is below reject_threshold", %{ +      message: message +    } do +      Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) + +      assert {:ok, ^message} = filter(message) +    end +  end + +  describe "delist" do +    test "delists the message if the recipient count is above delist_threshold", %{ +      user: user, +      message: message +    } do +      Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + +      {:ok, message} = filter(message) +      assert user.follower_address in message["to"] +      assert "https://www.w3.org/ns/activitystreams#Public" in message["cc"] +    end + +    test "does not delist the message if the recipient count is below delist_threshold", %{ +      message: message +    } do +      Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0}) + +      assert {:ok, ^message} = filter(message) +    end +  end + +  test "excludes follower collection and public URI from threshold count", %{message: message} do +    Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) + +    assert {:ok, ^message} = filter(message) +  end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index e5e3c8d33..86c66deff 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -1128,4 +1128,58 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do          )      end    end + +  describe "reserialization" do +    test "successfully reserializes a message with inReplyTo == nil" do +      user = insert(:user) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => ["https://www.w3.org/ns/activitystreams#Public"], +        "cc" => [], +        "type" => "Create", +        "object" => %{ +          "to" => ["https://www.w3.org/ns/activitystreams#Public"], +          "cc" => [], +          "type" => "Note", +          "content" => "Hi", +          "inReplyTo" => nil, +          "attributedTo" => user.ap_id +        }, +        "actor" => user.ap_id +      } + +      {:ok, activity} = Transmogrifier.handle_incoming(message) + +      {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) +    end + +    test "successfully reserializes a message with AS2 objects in IR" do +      user = insert(:user) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => ["https://www.w3.org/ns/activitystreams#Public"], +        "cc" => [], +        "type" => "Create", +        "object" => %{ +          "to" => ["https://www.w3.org/ns/activitystreams#Public"], +          "cc" => [], +          "type" => "Note", +          "content" => "Hi", +          "inReplyTo" => nil, +          "attributedTo" => user.ap_id, +          "tag" => [ +            %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"}, +            %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"} +          ] +        }, +        "actor" => user.ap_id +      } + +      {:ok, activity} = Transmogrifier.handle_incoming(message) + +      {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) +    end +  end  end diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 7fc870e96..0bc1d4728 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -15,4 +15,43 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do      assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY")    end + +  describe "endpoints" do +    test "local users have a usable endpoints structure" do +      user = insert(:user) +      {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) + +      result = UserView.render("user.json", %{user: user}) + +      assert result["id"] == user.ap_id + +      %{ +        "sharedInbox" => _, +        "oauthAuthorizationEndpoint" => _, +        "oauthRegistrationEndpoint" => _, +        "oauthTokenEndpoint" => _ +      } = result["endpoints"] +    end + +    test "remote users have an empty endpoints structure" do +      user = insert(:user, local: false) +      {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) + +      result = UserView.render("user.json", %{user: user}) + +      assert result["id"] == user.ap_id +      assert result["endpoints"] == %{} +    end + +    test "instance users do not expose oAuth endpoints" do +      user = insert(:user, nickname: nil, local: true) +      {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) + +      result = UserView.render("user.json", %{user: user}) + +      refute result["endpoints"]["oauthAuthorizationEndpoint"] +      refute result["endpoints"]["oauthRegistrationEndpoint"] +      refute result["endpoints"]["oauthTokenEndpoint"] +    end +  end  end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 42450a7b6..a27c26f95 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -5,7 +5,8 @@  defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do    use Pleroma.Web.ConnCase -  alias Pleroma.{Repo, User} +  alias Pleroma.Repo +  alias Pleroma.User    import Pleroma.Factory    describe "/api/pleroma/admin/user" do diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index d26b6e49c..870648fb5 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -2,7 +2,7 @@  # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.CommonAPI.Test do +defmodule Pleroma.Web.CommonAPITest do    use Pleroma.DataCase    alias Pleroma.Web.CommonAPI    alias Pleroma.User diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index 754bc7255..faed6b685 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -5,7 +5,7 @@  defmodule Pleroma.Web.CommonAPI.UtilsTest do    alias Pleroma.Web.CommonAPI.Utils    alias Pleroma.Web.Endpoint -  alias Pleroma.Builders.{UserBuilder} +  alias Pleroma.Builders.UserBuilder    use Pleroma.DataCase    test "it adds attachment links to a given text and attachment set" do diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs index 05f813291..08279f230 100644 --- a/test/web/federator_test.exs +++ b/test/web/federator_test.exs @@ -3,7 +3,8 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Web.FederatorTest do -  alias Pleroma.Web.{CommonAPI, Federator} +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.Federator    alias Pleroma.Instances    use Pleroma.DataCase    import Pleroma.Factory @@ -14,22 +15,6 @@ defmodule Pleroma.Web.FederatorTest do      :ok    end -  test "enqueues an element according to priority" do -    queue = [%{item: 1, priority: 2}] - -    new_queue = Federator.enqueue_sorted(queue, 2, 1) -    assert new_queue == [%{item: 2, priority: 1}, %{item: 1, priority: 2}] - -    new_queue = Federator.enqueue_sorted(queue, 2, 3) -    assert new_queue == [%{item: 1, priority: 2}, %{item: 2, priority: 3}] -  end - -  test "pop first item" do -    queue = [%{item: 2, priority: 1}, %{item: 1, priority: 2}] - -    assert {2, [%{item: 1, priority: 2}]} = Federator.queue_pop(queue) -  end -    describe "Publish an activity" do      setup do        user = insert(:user) @@ -49,7 +34,7 @@ defmodule Pleroma.Web.FederatorTest do        relay_mock: relay_mock      } do        with_mocks([relay_mock]) do -        Federator.handle(:publish, activity) +        Federator.publish(activity)        end        assert_received :relay_publish @@ -62,7 +47,7 @@ defmodule Pleroma.Web.FederatorTest do        Pleroma.Config.put([:instance, :allow_relay], false)        with_mocks([relay_mock]) do -        Federator.handle(:publish, activity) +        Federator.publish(activity)        end        refute_received :relay_publish @@ -103,11 +88,9 @@ defmodule Pleroma.Web.FederatorTest do        {:ok, _activity} =          CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"}) -      assert called( -               Federator.enqueue(:publish_single_ap, %{inbox: inbox1, unreachable_since: dt}) -             ) +      assert called(Federator.publish_single_ap(%{inbox: inbox1, unreachable_since: dt})) -      refute called(Federator.enqueue(:publish_single_ap, %{inbox: inbox2})) +      refute called(Federator.publish_single_ap(%{inbox: inbox2}))      end      test_with_mock "it federates only to reachable instances via Websub", @@ -139,13 +122,13 @@ defmodule Pleroma.Web.FederatorTest do        {:ok, _activity} = CommonAPI.post(user, %{"status" => "HI"})        assert called( -               Federator.enqueue(:publish_single_websub, %{ +               Federator.publish_single_websub(%{                   callback: sub2.callback,                   unreachable_since: dt                 })               ) -      refute called(Federator.enqueue(:publish_single_websub, %{callback: sub1.callback})) +      refute called(Federator.publish_single_websub(%{callback: sub1.callback}))      end      test_with_mock "it federates only to reachable instances via Salmon", @@ -179,13 +162,13 @@ defmodule Pleroma.Web.FederatorTest do          CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"})        assert called( -               Federator.enqueue(:publish_single_salmon, %{ +               Federator.publish_single_salmon(%{                   recipient: remote_user2,                   unreachable_since: dt                 })               ) -      refute called(Federator.enqueue(:publish_single_websub, %{recipient: remote_user1})) +      refute called(Federator.publish_single_websub(%{recipient: remote_user1}))      end    end @@ -205,7 +188,7 @@ defmodule Pleroma.Web.FederatorTest do          "to" => ["https://www.w3.org/ns/activitystreams#Public"]        } -      {:ok, _activity} = Federator.handle(:incoming_ap_doc, params) +      {:ok, _activity} = Federator.incoming_ap_doc(params)      end      test "rejects incoming AP docs with incorrect origin" do @@ -223,7 +206,7 @@ defmodule Pleroma.Web.FederatorTest do          "to" => ["https://www.w3.org/ns/activitystreams#Public"]        } -      :error = Federator.handle(:incoming_ap_doc, params) +      :error = Federator.incoming_ap_doc(params)      end    end  end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 1a60ad8e6..e43bc4508 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -6,8 +6,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do    use Pleroma.Web.ConnCase    alias Pleroma.Web.TwitterAPI.TwitterAPI -  alias Pleroma.{Repo, User, Object, Activity, Notification} -  alias Pleroma.Web.{OStatus, CommonAPI} +  alias Pleroma.Repo +  alias Pleroma.User +  alias Pleroma.Object +  alias Pleroma.Activity +  alias Pleroma.Notification +  alias Pleroma.Web.OStatus +  alias Pleroma.Web.CommonAPI    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.MastodonAPI.FilterView    alias Ecto.Changeset @@ -31,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do        |> assign(:user, user)        |> get("/api/v1/timelines/home") -    assert length(json_response(conn, 200)) == 0 +    assert Enum.empty?(json_response(conn, 200))      {:ok, user} = User.follow(user, following) @@ -932,7 +937,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end      test "/api/v1/follow_requests/:id/authorize works" do -      user = insert(:user, %{info: %Pleroma.User.Info{locked: true}}) +      user = insert(:user, %{info: %User.Info{locked: true}})        other_user = insert(:user)        {:ok, _activity} = ActivityPub.follow(other_user, user) @@ -941,6 +946,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do        other_user = Repo.get(User, other_user.id)        assert User.following?(other_user, user) == false +      assert user.info.follow_request_count == 1        conn =          build_conn() @@ -954,6 +960,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do        other_user = Repo.get(User, other_user.id)        assert User.following?(other_user, user) == true +      assert user.info.follow_request_count == 0      end      test "verify_credentials", %{conn: conn} do @@ -974,6 +981,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do        {:ok, _activity} = ActivityPub.follow(other_user, user) +      user = Repo.get(User, user.id) +      assert user.info.follow_request_count == 1 +        conn =          build_conn()          |> assign(:user, user) @@ -986,6 +996,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do        other_user = Repo.get(User, other_user.id)        assert User.following?(other_user, user) == false +      assert user.info.follow_request_count == 0      end    end @@ -1781,4 +1792,29 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do                 |> json_response(200)      end    end + +  test "flavours switching (Pleroma Extension)", %{conn: conn} do +    user = insert(:user) + +    get_old_flavour = +      conn +      |> assign(:user, user) +      |> get("/api/v1/pleroma/flavour") + +    assert "glitch" == json_response(get_old_flavour, 200) + +    set_flavour = +      conn +      |> assign(:user, user) +      |> post("/api/v1/pleroma/flavour/vanilla") + +    assert "vanilla" == json_response(set_flavour, 200) + +    get_new_flavour = +      conn +      |> assign(:user, user) +      |> post("/api/v1/pleroma/flavour/vanilla") + +    assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200) +  end  end diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs index 2106253f2..0dc9c538c 100644 --- a/test/web/mastodon_api/status_view_test.exs +++ b/test/web/mastodon_api/status_view_test.exs @@ -5,7 +5,8 @@  defmodule Pleroma.Web.MastodonAPI.StatusViewTest do    use Pleroma.DataCase -  alias Pleroma.Web.MastodonAPI.{StatusView, AccountView} +  alias Pleroma.Web.MastodonAPI.AccountView +  alias Pleroma.Web.MastodonAPI.StatusView    alias Pleroma.User    alias Pleroma.Web.OStatus    alias Pleroma.Web.CommonAPI diff --git a/test/web/oauth/authorization_test.exs b/test/web/oauth/authorization_test.exs index 3b1ddada8..81618e935 100644 --- a/test/web/oauth/authorization_test.exs +++ b/test/web/oauth/authorization_test.exs @@ -4,7 +4,8 @@  defmodule Pleroma.Web.OAuth.AuthorizationTest do    use Pleroma.DataCase -  alias Pleroma.Web.OAuth.{Authorization, App} +  alias Pleroma.Web.OAuth.Authorization +  alias Pleroma.Web.OAuth.App    import Pleroma.Factory    test "create an authorization token for a valid app" do diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index e0d3cb55f..2315f9a34 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -7,7 +7,8 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do    import Pleroma.Factory    alias Pleroma.Repo -  alias Pleroma.Web.OAuth.{Authorization, Token} +  alias Pleroma.Web.OAuth.Authorization +  alias Pleroma.Web.OAuth.Token    test "redirects with oauth authorization" do      user = insert(:user) diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs index 9a241d61a..4dab4a308 100644 --- a/test/web/oauth/token_test.exs +++ b/test/web/oauth/token_test.exs @@ -4,7 +4,9 @@  defmodule Pleroma.Web.OAuth.TokenTest do    use Pleroma.DataCase -  alias Pleroma.Web.OAuth.{App, Token, Authorization} +  alias Pleroma.Web.OAuth.App +  alias Pleroma.Web.OAuth.Authorization +  alias Pleroma.Web.OAuth.Token    alias Pleroma.Repo    import Pleroma.Factory diff --git a/test/web/ostatus/activity_representer_test.exs b/test/web/ostatus/activity_representer_test.exs index 0869f2fd5..eebc5c040 100644 --- a/test/web/ostatus/activity_representer_test.exs +++ b/test/web/ostatus/activity_representer_test.exs @@ -6,7 +6,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do    use Pleroma.DataCase    alias Pleroma.Web.OStatus.ActivityRepresenter -  alias Pleroma.{User, Activity, Object} +  alias Pleroma.Activity +  alias Pleroma.User +  alias Pleroma.Object    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.OStatus diff --git a/test/web/ostatus/feed_representer_test.exs b/test/web/ostatus/feed_representer_test.exs index 55717dec7..efd4e7217 100644 --- a/test/web/ostatus/feed_representer_test.exs +++ b/test/web/ostatus/feed_representer_test.exs @@ -6,7 +6,9 @@ defmodule Pleroma.Web.OStatus.FeedRepresenterTest do    use Pleroma.DataCase    import Pleroma.Factory    alias Pleroma.User -  alias Pleroma.Web.OStatus.{FeedRepresenter, UserRepresenter, ActivityRepresenter} +  alias Pleroma.Web.OStatus.ActivityRepresenter +  alias Pleroma.Web.OStatus.FeedRepresenter +  alias Pleroma.Web.OStatus.UserRepresenter    alias Pleroma.Web.OStatus    test "returns a feed of the last 20 items of the user" do diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs index d97cd79f4..d295cc539 100644 --- a/test/web/ostatus/incoming_documents/delete_handling_test.exs +++ b/test/web/ostatus/incoming_documents/delete_handling_test.exs @@ -4,7 +4,9 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest do    import Pleroma.Factory    import Tesla.Mock -  alias Pleroma.{Repo, Activity, Object} +  alias Pleroma.Repo +  alias Pleroma.Activity +  alias Pleroma.Object    alias Pleroma.Web.OStatus    setup do diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs index 3145ca9a1..da9c72be8 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/web/ostatus/ostatus_controller_test.exs @@ -5,7 +5,9 @@  defmodule Pleroma.Web.OStatus.OStatusControllerTest do    use Pleroma.Web.ConnCase    import Pleroma.Factory -  alias Pleroma.{User, Repo, Object} +  alias Pleroma.User +  alias Pleroma.Repo +  alias Pleroma.Object    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.OStatus.ActivityRepresenter diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs index dbe5de2e2..b4b19ab05 100644 --- a/test/web/ostatus/ostatus_test.exs +++ b/test/web/ostatus/ostatus_test.exs @@ -6,7 +6,11 @@ defmodule Pleroma.Web.OStatusTest do    use Pleroma.DataCase    alias Pleroma.Web.OStatus    alias Pleroma.Web.XML -  alias Pleroma.{Object, Repo, User, Activity, Instances} +  alias Pleroma.Object +  alias Pleroma.Repo +  alias Pleroma.User +  alias Pleroma.Activity +  alias Pleroma.Instances    import Pleroma.Factory    import ExUnit.CaptureLog diff --git a/test/web/salmon/salmon_test.exs b/test/web/salmon/salmon_test.exs index c539a28b2..9e583ba40 100644 --- a/test/web/salmon/salmon_test.exs +++ b/test/web/salmon/salmon_test.exs @@ -5,7 +5,9 @@  defmodule Pleroma.Web.Salmon.SalmonTest do    use Pleroma.DataCase    alias Pleroma.Web.Salmon -  alias Pleroma.{Repo, Activity, User} +  alias Pleroma.Activity +  alias Pleroma.Repo +  alias Pleroma.User    import Pleroma.Factory    @magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB" diff --git a/test/web/twitter_api/representers/activity_representer_test.exs b/test/web/twitter_api/representers/activity_representer_test.exs index ea5813733..365c7f659 100644 --- a/test/web/twitter_api/representers/activity_representer_test.exs +++ b/test/web/twitter_api/representers/activity_representer_test.exs @@ -4,8 +4,11 @@  defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do    use Pleroma.DataCase -  alias Pleroma.{User, Activity, Object} -  alias Pleroma.Web.TwitterAPI.Representers.{ActivityRepresenter, ObjectRepresenter} +  alias Pleroma.User +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter +  alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.TwitterAPI.UserView    import Pleroma.Factory diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 855ae1526..1571ab68e 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -5,9 +5,15 @@  defmodule Pleroma.Web.TwitterAPI.ControllerTest do    use Pleroma.Web.ConnCase    alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter -  alias Pleroma.Builders.{ActivityBuilder, UserBuilder} -  alias Pleroma.{Repo, Activity, User, Object, Notification} +  alias Pleroma.Builders.ActivityBuilder +  alias Pleroma.Builders.UserBuilder +  alias Pleroma.Repo +  alias Pleroma.Activity +  alias Pleroma.User +  alias Pleroma.Object +  alias Pleroma.Notification    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.TwitterAPI.UserView    alias Pleroma.Web.TwitterAPI.NotificationView    alias Pleroma.Web.CommonAPI @@ -635,6 +641,24 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do        assert json_response(conn, 200) ==                 UserView.render("show.json", %{user: followed, for: current_user})      end + +    test "for restricted account", %{conn: conn, user: current_user} do +      followed = insert(:user, info: %User.Info{locked: true}) + +      conn = +        conn +        |> with_credentials(current_user.nickname, "test") +        |> post("/api/friendships/create.json", %{user_id: followed.id}) + +      current_user = Repo.get(User, current_user.id) +      followed = Repo.get(User, followed.id) + +      refute User.ap_followers(followed) in current_user.following +      assert followed.info.follow_request_count == 1 + +      assert json_response(conn, 200) == +               UserView.render("show.json", %{user: followed, for: current_user}) +    end    end    describe "POST /friendships/destroy.json" do @@ -1213,7 +1237,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do        assert Enum.sort(expected) == Enum.sort(result)      end -    test "it returns 20 friends per page", %{conn: conn} do +    test "it returns 20 friends per page, except if 'export' is set to true", %{conn: conn} do        user = insert(:user)        followeds = insert_list(21, :user) @@ -1237,6 +1261,14 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do        result = json_response(res_conn, 200)        assert length(result) == 1 + +      res_conn = +        conn +        |> assign(:user, user) +        |> get("/api/statuses/friends", %{all: true}) + +      result = json_response(res_conn, 200) +      assert length(result) == 21      end      test "it returns a given user's friends with user_id", %{conn: conn} do @@ -1671,15 +1703,19 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do        other_user = Repo.get(User, other_user.id)        assert User.following?(other_user, user) == false +      assert user.info.follow_request_count == 1        conn =          build_conn()          |> assign(:user, user)          |> post("/api/pleroma/friendships/approve", %{"user_id" => other_user.id}) +      user = Repo.get(User, user.id) +        assert relationship = json_response(conn, 200)        assert other_user.id == relationship["id"]        assert relationship["follows_you"] == true +      assert user.info.follow_request_count == 0      end    end @@ -1694,15 +1730,19 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do        other_user = Repo.get(User, other_user.id)        assert User.following?(other_user, user) == false +      assert user.info.follow_request_count == 1        conn =          build_conn()          |> assign(:user, user)          |> post("/api/pleroma/friendships/deny", %{"user_id" => other_user.id}) +      user = Repo.get(User, user.id) +        assert relationship = json_response(conn, 200)        assert other_user.id == relationship["id"]        assert relationship["follows_you"] == false +      assert user.info.follow_request_count == 0      end    end @@ -1876,4 +1916,38 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do                 ActivityRepresenter.to_map(activity, %{user: user, for: user})      end    end + +  describe "GET /api/oauth_tokens" do +    setup do +      token = insert(:oauth_token) |> Repo.preload(:user) + +      %{token: token} +    end + +    test "renders list", %{token: token} do +      response = +        build_conn() +        |> assign(:user, token.user) +        |> get("/api/oauth_tokens") + +      keys = +        json_response(response, 200) +        |> hd() +        |> Map.keys() + +      assert keys -- ["id", "app_name", "valid_until"] == [] +    end + +    test "revoke token", %{token: token} do +      response = +        build_conn() +        |> assign(:user, token.user) +        |> delete("/api/oauth_tokens/#{token.id}") + +      tokens = Token.get_user_tokens(token.user) + +      assert tokens == [] +      assert response.status == 201 +    end +  end  end diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 48ddbcf50..aa2a4d650 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -4,8 +4,13 @@  defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do    use Pleroma.DataCase -  alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView} -  alias Pleroma.{Activity, User, Object, Repo, UserInviteToken} +  alias Pleroma.Web.TwitterAPI.TwitterAPI +  alias Pleroma.Web.TwitterAPI.UserView +  alias Pleroma.Activity +  alias Pleroma.User +  alias Pleroma.Object +  alias Pleroma.Repo +  alias Pleroma.UserInviteToken    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.TwitterAPI.ActivityView diff --git a/test/web/twitter_api/views/notification_view_test.exs b/test/web/twitter_api/views/notification_view_test.exs index 8367fc6c7..3a67f7292 100644 --- a/test/web/twitter_api/views/notification_view_test.exs +++ b/test/web/twitter_api/views/notification_view_test.exs @@ -5,7 +5,8 @@  defmodule Pleroma.Web.TwitterAPI.NotificationViewTest do    use Pleroma.DataCase -  alias Pleroma.{User, Notification} +  alias Pleroma.User +  alias Pleroma.Notification    alias Pleroma.Web.TwitterAPI.TwitterAPI    alias Pleroma.Web.TwitterAPI.NotificationView    alias Pleroma.Web.TwitterAPI.UserView diff --git a/test/web/websub/websub_controller_test.exs b/test/web/websub/websub_controller_test.exs index 6492df2a0..87b01d89b 100644 --- a/test/web/websub/websub_controller_test.exs +++ b/test/web/websub/websub_controller_test.exs @@ -6,7 +6,8 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do    use Pleroma.Web.ConnCase    import Pleroma.Factory    alias Pleroma.Web.Websub.WebsubClientSubscription -  alias Pleroma.{Repo, Activity} +  alias Pleroma.Activity +  alias Pleroma.Repo    alias Pleroma.Web.Websub    test "websub subscription request", %{conn: conn} do @@ -80,7 +81,7 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do        assert response(conn, 500) == "Error" -      assert length(Repo.all(Activity)) == 0 +      assert Enum.empty?(Repo.all(Activity))      end    end  end diff --git a/test/web/websub/websub_test.exs b/test/web/websub/websub_test.exs index 9751d161d..9a9b9df02 100644 --- a/test/web/websub/websub_test.exs +++ b/test/web/websub/websub_test.exs @@ -5,7 +5,8 @@  defmodule Pleroma.Web.WebsubTest do    use Pleroma.DataCase    alias Pleroma.Web.Websub -  alias Pleroma.Web.Websub.{WebsubServerSubscription, WebsubClientSubscription} +  alias Pleroma.Web.Websub.WebsubServerSubscription +  alias Pleroma.Web.Websub.WebsubClientSubscription    import Pleroma.Factory    alias Pleroma.Web.Router.Helpers    import Tesla.Mock | 
